All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.officefloor.server.buffer.StreamBufferScanner Maven / Gradle / Ivy

There is a newer version: 3.40.0
Show newest version
/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2018 Daniel Sagenschneider
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see .
 */
package net.officefloor.server.buffer;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;

import net.officefloor.server.stream.StreamBuffer;

/**
 * Provides scanning of {@link StreamBuffer} instances.
 * 
 * @author Daniel Sagenschneider
 */
public class StreamBufferScanner {

	/**
	 * Scan target.
	 */
	public static class ScanTarget {

		/**
		 * Byte value to scan.
		 */
		public final byte value;

		/**
		 * Mask for the value.
		 */
		public final long mask;

		/**
		 * Instantiate.
		 * 
		 * @param value Target byte for scan.
		 */
		public ScanTarget(byte value) {
			this.value = value;

			// Create the mask from the value
			long mask = value;
			for (int i = 0; i < 7; i++) {
				mask <<= 8; // move bytes up
				mask |= value; // add in the value
			}
			this.mask = mask;
		}
	}

	/**
	 * 

* List of previous {@link StreamBuffer} instances involved before current * {@link StreamBuffer}. *

* Typically, content should not span more than one previous * {@link StreamBuffer} (assuming {@link ByteBuffer} sizes are reasonably * large). */ private final List> previousBuffers = new ArrayList<>(1); /** * Start position to read data from for the first previous {@link StreamBuffer}. * All previous {@link StreamBuffer} instances after first are assumed to start * at 0. */ private int firstPreviousBufferStart = 0; /** * Number of bytes in the previous {@link StreamBuffer} instances. This is a * long to enable potentially very large HTTP entities. */ private long previousBuffersByteCount = 0; /** * Current {@link StreamBuffer}. */ private StreamBuffer currentBuffer = null; /** * Starting position within the current {@link StreamBuffer}. */ private int currentBufferStartPosition = 0; /** * Scan starting position with the current {@link StreamBuffer}. This allows the * {@link #currentBufferStartPosition} to only move forward when data obtained. */ private int currentBufferScanStart = 0; /** * Ending position with the current {@link StreamBuffer}. */ private int currentBufferEndPosition = 0; /** * Buffer long (8) bytes. */ private long bufferLong = 0; /** * Number of bytes from previous buffers. Value is always between 0 - 8 (as only * 8 bytes in long). */ private byte bufferLongByteCount = 0; /** * Appends {@link StreamBuffer} for scanning. * * @param buffer {@link StreamBuffer}. */ public void appendStreamBuffer(StreamBuffer buffer) { // Do nothing if same buffer (just data appended) if (buffer == this.currentBuffer) { return; } // Keep track of previous buffer if (this.currentBuffer != null) { // Determine if first previous buffer (start at position) if (this.previousBuffers.size() == 0) { // Add the first previous buffer (keep track of start position) this.firstPreviousBufferStart = this.currentBufferStartPosition; this.previousBuffersByteCount += (this.currentBuffer.pooledBuffer.position() - this.firstPreviousBufferStart); } else { // Add further previous buffer (entire buffer) this.previousBuffersByteCount += this.currentBuffer.pooledBuffer.position(); } // Add the current buffer as a previous buffer this.previousBuffers.add(this.currentBuffer); } // Now the current buffer this.currentBuffer = buffer; // Start at beginning of buffer this.currentBufferStartPosition = 0; this.currentBufferScanStart = 0; this.currentBufferEndPosition = 0; } /** * Builds a long (8 bytes) from the {@link StreamBuffer} at current position. * * @param Illegal value {@link Exception} * @param illegalValueExceptionFactory {@link Supplier} to create * {@link Throwable} for illegal long value. * @return Long value, otherwise -1 if not enough bytes in * {@link StreamBuffer} to build a long. * @throws T If invalid value. */ public long buildLong(Supplier illegalValueExceptionFactory) throws T { // Determine if remaining content for long in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; int remaining = data.position() - this.currentBufferScanStart; // Determine if build immediately (typical case) if (remaining >= 8) { /* * Buffer contains enough data to read in the full long. Therefore determine * whether have data to read in the full long in one go (avoid extra build steps * below, if just built a short or byte). Two scenarios are: * * 1) No previous build of shorter content, so read in entire long from scan * start * * 2) Or, have already read in some bytes to buffer, so need to ensure those * bytes are in current buffer for full read */ if ((this.bufferLongByteCount == 0) || (this.bufferLongByteCount < this.currentBufferScanStart)) { // Obtain the bytes directly from buffer long returnBytes = data.getLong(this.currentBufferScanStart); // Ensure legal value if (returnBytes == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the short bytes return returnBytes; } } // Append to previous bytes remaining = Math.min(remaining, 8 - this.bufferLongByteCount); this.bufferLongByteCount += remaining; // will be appending bytes while (remaining > 0) { switch (remaining) { case 7: case 6: case 5: case 4: // Take 4 bytes and append int intBytes = data.getInt(this.currentBufferScanStart); this.currentBufferScanStart += 4; // read the 4 bytes this.bufferLong <<= 32; // move up 4 bytes this.bufferLong += intBytes & 0xffffffff; remaining -= 4; // 4 bytes break; case 3: case 2: // Take 2 bytes and append int shortBytes = data.getShort(this.currentBufferScanStart); this.currentBufferScanStart += 2; // read the 2 bytes this.bufferLong <<= 16; // move up 2 bytes this.bufferLong += shortBytes & 0xffff; remaining -= 2; // 2 bytes break; case 1: // Append the single byte int singleByte = data.get(this.currentBufferScanStart); this.currentBufferScanStart++; // read the 1 byte this.bufferLong <<= 8; // move up a byte this.bufferLong += singleByte & 0xff; remaining = 0; // should always complete on last single byte break; } } // Determine if have the long if (this.bufferLongByteCount >= 8) { // Have all bytes, so ensure legal value if (this.bufferLong == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the long bytes return this.bufferLong; } // Not able to build long return -1; } /** * Builds a short (2 bytes) from the {@link StreamBuffer} at current position. * * @param Illegal value {@link Exception} * @param illegalValueExceptionFactory {@link Supplier} to create * {@link Throwable} for illegal short * value. * @return Short value, otherwise -1 if not enough bytes in * {@link StreamBuffer} to build a short. * @throws T If invalid value. */ public short buildShort(Supplier illegalValueExceptionFactory) throws T { // Determine if remaining content for long in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; int remaining = data.position() - this.currentBufferScanStart; // Determine if build immediately (typical case) if ((this.bufferLongByteCount == 0) && (remaining >= 2)) { // Obtain the bytes directly from buffer short returnBytes = data.getShort(this.currentBufferScanStart); // Ensure legal value if (returnBytes == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the short bytes return returnBytes; } // Append to previous bytes (could be buffers of 1 byte each) remaining = Math.min(remaining, 2 - this.bufferLongByteCount); this.bufferLongByteCount += remaining; // will be appending bytes if (remaining == 1) { // Append the single byte // Will only ever be 1 byte appending, otherwise have short int singleByte = data.get(this.currentBufferScanStart); this.currentBufferScanStart++; // read in byte this.bufferLong <<= 8; // move up a byte this.bufferLong += singleByte & 0xff; } // Determine if have the short if (this.bufferLongByteCount >= 2) { // Obtain the just the first two bytes long bytes = this.bufferLong; bytes >>= (this.bufferLongByteCount - 2) * 8; // Have all bytes, so obtain short short returnBytes = (short) (bytes & 0xffff); // Ensure legal value if (returnBytes == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the short bytes return returnBytes; } // As here, not enough bytes to build short return -1; } /** * Builds a byte from the {@link StreamBuffer} at current position. * * @param Illegal value {@link Exception} * @param illegalValueExceptionFactory {@link Supplier} to create * {@link Throwable} for illegal short * value. * @return Byte value, otherwise -1 if not enough bytes in * {@link StreamBuffer} to build a byte. * @throws T If invalid value. */ public byte buildByte(Supplier illegalValueExceptionFactory) throws T { // Determine if remaining content for byte in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; int remaining = data.position() - this.currentBufferScanStart; // Determine if build immediately (typical case) if ((this.bufferLongByteCount == 0) && (remaining >= 1)) { // Obtain the bytes directly from buffer byte returnBytes = data.get(this.currentBufferScanStart); // Ensure legal value if (returnBytes == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the short bytes return returnBytes; } // Determine if have byte if (this.bufferLongByteCount >= 1) { // Obtain the just the first byte long bytes = this.bufferLong; bytes >>= (this.bufferLongByteCount - 1) * 8; // Have all bytes, so obtain byte byte returnByte = (byte) (bytes & 0xff); // Ensure legal value if (returnByte == -1) { // Illegal value throw illegalValueExceptionFactory.get(); } // Return the byte return returnByte; } // As here, not enough bytes to build byte return -1; } /** * Peeks in the current {@link StreamBuffer} to attempt to find the * {@link ScanTarget}. * * @param target {@link ScanTarget}. * @return Position of the {@link ScanTarget} within the current * {@link StreamBuffer} (starting at current position). Otherwise, * -1 to indicate did not find {@link ScanTarget} in * current {@link StreamBuffer}. */ public int peekToTarget(ScanTarget target) { // Determine if remaining content for long in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; int lastPosition = data.position(); // Obtain current position in the buffer to start scanning int position = this.currentBufferStartPosition; // Keep reading until end of current buffer int lastFullReadPosition = lastPosition - 7; while (position < lastFullReadPosition) { // Read in the long (8 bytes) long bytes = data.getLong(position); // Determine if byte in long int index = indexOf(bytes, target); if (index != -1) { // Found the byte (offset of current position) return position + index - this.currentBufferStartPosition; } // Not in long, so try next long (8 bytes) position += 8; } // Attempt to find in possible remaining bytes while (position < data.position()) { // At worse, 7 bytes so just check each byte value = data.get(position); if (value == target.value) { // Found the byte (offset of current position) return position - this.currentBufferStartPosition; } // Attempt at next position position++; } // As here, did not find byte in the buffer return -1; } /** * Scans a specific number of bytes. * * @param numberOfBytes Number of bytes to scan. * @return {@link StreamBufferByteSequence} to the scanned bytes. */ public StreamBufferByteSequence scanBytes(long numberOfBytes) { // Determine remaining bytes required long requiredBytes = numberOfBytes - this.previousBuffersByteCount; if (requiredBytes < 0) { // Data from previous buffers return this.createByteSequence((int) numberOfBytes); } // Determine number of available bytes in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; int availableBytes = data.position() - this.currentBufferStartPosition; // Determine if have bytes to complete scan if (requiredBytes <= availableBytes) { // Have all bytes so create byte sequence // (Buffer size should always be less than max int) this.currentBufferEndPosition = (int) (this.currentBufferStartPosition + requiredBytes); return this.createByteSequence(); } // Not enough bytes return null; } /** *

* Scans the {@link StreamBuffer} for the byte value from the current position. *

* The returned {@link StreamBufferByteSequence} is inclusive of the current * position but exclusive of the target byte. This is because target bytes for * HTTP are typically delimiters (eg. space, CR, etc) that are not included in * the bytes of interest. * * @param Too long {@link Exception} type. * @param target {@link ScanTarget}. * @param maxBytesLength Max bytes. * @param tooLongExceptionFactory {@link Supplier} to provide too long * {@link Exception}. * @return {@link StreamBufferByteSequence} if found the byte. Otherwise, * null indicating further {@link StreamBuffer} instances * may contain the byte. * @throws T If too long {@link Exception}. */ public StreamBufferByteSequence scanToTarget(ScanTarget target, int maxBytesLength, Supplier tooLongExceptionFactory) throws T { // Determine if previous bytes if (this.bufferLongByteCount != 0) { // Previous bytes from short/long, so use first byte previousBytesIndex = indexOf(this.bufferLong, target); if (previousBytesIndex != -1) { // Obtain number of bytes (may not be full, as possibly short) int numberOfPreviousBytes = previousBytesIndex - (8 - this.bufferLongByteCount); // Return content to index return this.createByteSequence(numberOfPreviousBytes); } // As here, did not find in buffer (so clear) this.bufferLong = 0; this.bufferLongByteCount = 0; } // Determine if remaining content for long in current buffer ByteBuffer data = this.currentBuffer.pooledBuffer; // Keep reading until end of current buffer int lastFullReadPosition = data.position() - 7; while (this.currentBufferScanStart < lastFullReadPosition) { // Read in the long (8 bytes) long bytes = data.getLong(this.currentBufferScanStart); // Determine if byte in long int index = indexOf(bytes, target); if (index != -1) { // Found the byte this.currentBufferEndPosition = (this.currentBufferScanStart + index); return this.createByteSequence(); } // Not in long, so try next long (8 bytes) this.currentBufferScanStart += 8; } // Attempt to find in possible remaining bytes while (this.currentBufferScanStart < data.position()) { // At worse, 7 bytes so just check each byte value = data.get(this.currentBufferScanStart); if (value == target.value) { // Found the byte this.currentBufferEndPosition = this.currentBufferScanStart; return this.createByteSequence(); } // Attempt at next position this.currentBufferScanStart++; } // Add to previous bytes (and error if too long) long scannedBytes = (this.currentBufferScanStart - this.currentBufferStartPosition) + this.previousBuffersByteCount; if (scannedBytes >= maxBytesLength) { throw tooLongExceptionFactory.get(); } // As here, did not find byte in the buffer return null; } /** * Skips forward the particular number of bytes in the current * {@link StreamBuffer}. * * @param numberOfBytes Number of bytes to skip. */ public void skipBytes(int numberOfBytes) { // Keep track of original bytes skipped int numberOfBytesSkipped = numberOfBytes; // Remove possible previous buffer bytes this.removeBufferLongBytes(numberOfBytes); // Consume number of bytes if (this.previousBuffers.size() == 0) { // Only current buffer this.currentBufferStartPosition += numberOfBytes; this.currentBufferScanStart = this.currentBufferStartPosition; } else { // Have past buffers, so include data from them StreamBuffer firstBuffer = this.previousBuffers.get(0); int length = firstBuffer.pooledBuffer.position() - this.firstPreviousBufferStart; // Determine if all data on first previous buffer if (numberOfBytes <= length) { // All content from first buffer (adjust for remaining) this.firstPreviousBufferStart += numberOfBytes; return; } // Consumes all of first buffer and requires further data numberOfBytes -= length; // Create iterator and remove the first buffer (as already included) Iterator> iterator = this.previousBuffers.iterator(); iterator.next(); // move to first buffer iterator.remove(); // remove first buffer // Load in remaining data while (iterator.hasNext()) { // Obtain next buffer StreamBuffer buffer = iterator.next(); // Obtain the number of bytes in next buffer length = buffer.pooledBuffer.position(); // Determine if buffer completes the required data if (numberOfBytes <= length) { // Set start to appropriate position this.firstPreviousBufferStart = numberOfBytes; this.previousBuffersByteCount -= numberOfBytesSkipped; return; } // Skip the entire buffer iterator.remove(); // Obtain remaining bytes numberOfBytes -= length; } // All previous buffers removed this.firstPreviousBufferStart = 0; this.previousBuffersByteCount = 0; // Set position for next scan this.currentBufferStartPosition = numberOfBytes; this.currentBufferScanStart = this.currentBufferStartPosition; } } /** * Removes past bytes from the long buffer. * * @param numberOfBytes Number of bytes to remove. Value is absolute and does * not take into account number of bytes in the buffer. */ private void removeBufferLongBytes(int numberOfBytes) { // Remove possible previous buffer bytes if (this.bufferLongByteCount != 0) { int pastBufferBytes; if (numberOfBytes > this.bufferLongByteCount) { // Remove all bytes in long buffer pastBufferBytes = 8; } else { // Obtain index of remaining byte pastBufferBytes = 8 - (this.bufferLongByteCount - numberOfBytes); } // Remove previous bytes (as now consumed) switch (pastBufferBytes) { case 8: // Remove all bytes this.bufferLong = 0; this.bufferLongByteCount = 0; break; case 7: // Leave only last byte this.bufferLong &= 0xffL; this.bufferLongByteCount = 1; break; case 6: // Leave two bytes this.bufferLong &= 0xffffL; this.bufferLongByteCount = 2; break; case 5: // Leave 3 bytes this.bufferLong &= 0xffffffL; this.bufferLongByteCount = 3; break; case 4: // Leave 4 bytes this.bufferLong &= 0xffffffffL; this.bufferLongByteCount = 4; break; case 3: // Leave 5 bytes this.bufferLong &= 0xffffffffffL; this.bufferLongByteCount = 5; break; case 2: // Leave 6 bytes this.bufferLong &= 0xffffffffffffL; this.bufferLongByteCount = 6; break; case 1: // Leave 7 bytes this.bufferLong &= 0xffffffffffffffL; this.bufferLongByteCount = 7; break; // 0 leave all bytes } } } /** *

* Creates the {@link StreamBufferByteSequence} up to current position. *

* This will also reset for obtaining the next {@link StreamBufferByteSequence}. * * @return {@link StreamBufferByteSequence} up to current position. */ private StreamBufferByteSequence createByteSequence() { // Create the sequence StreamBufferByteSequence sequence; if (this.previousBuffers.size() == 0) { // Just current buffer int length = this.currentBufferEndPosition - this.currentBufferStartPosition; sequence = new StreamBufferByteSequence(this.currentBuffer, this.currentBufferStartPosition, length); } else { // Previous buffers StreamBuffer firstBuffer = this.previousBuffers.get(0); int length = firstBuffer.pooledBuffer.position() - this.firstPreviousBufferStart; sequence = new StreamBufferByteSequence(firstBuffer, this.firstPreviousBufferStart, length); // Add in remaining previous buffers for (int i = 1; i < this.previousBuffers.size(); i++) { StreamBuffer nextBuffer = this.previousBuffers.get(i); length = nextBuffer.pooledBuffer.position(); sequence.appendStreamBuffer(nextBuffer, 0, length); } // Add in the current buffer length = this.currentBufferEndPosition; sequence.appendStreamBuffer(this.currentBuffer, 0, length); // Reset previous buffers, as now in byte sequence this.firstPreviousBufferStart = 0; this.previousBuffersByteCount = 0; this.previousBuffers.clear(); } // Remove long buffer data this.removeBufferLongBytes(sequence.length()); // Set to current position to continue this.currentBufferStartPosition = this.currentBufferEndPosition; this.currentBufferScanStart = this.currentBufferStartPosition; // Return the sequence return sequence; } /** *

* Creates a {@link StreamBufferByteSequence} from current position to the * specified number of bytes. *

* This will also reset to after the number of bytes. * * @param numberOfBytes Number of bytes for the * {@link StreamBufferByteSequence}. * @return {@link StreamBufferByteSequence} with the specified number of bytes. */ private StreamBufferByteSequence createByteSequence(int numberOfBytes) { // Keep track of original bytes skipped int numberOfBytesSkipped = numberOfBytes; // Remove possible previous buffer bytes this.removeBufferLongBytes(numberOfBytes); // Create the byte sequence StreamBufferByteSequence sequence; if (this.previousBuffers.size() == 0) { // Only current buffer sequence = new StreamBufferByteSequence(this.currentBuffer, this.currentBufferStartPosition, numberOfBytes); this.currentBufferStartPosition += numberOfBytes; this.currentBufferScanStart = this.currentBufferStartPosition; } else { // Have past buffers, so include data from them StreamBuffer firstBuffer = this.previousBuffers.get(0); int length = firstBuffer.pooledBuffer.position() - this.firstPreviousBufferStart; // Determine if all data on first previous buffer if (numberOfBytes <= length) { // All content from first buffer (adjust for remaining) sequence = new StreamBufferByteSequence(firstBuffer, this.firstPreviousBufferStart, numberOfBytes); this.firstPreviousBufferStart += numberOfBytes; this.previousBuffersByteCount -= numberOfBytesSkipped; return sequence; } // Consumes all of first buffer and requires further data sequence = new StreamBufferByteSequence(firstBuffer, this.firstPreviousBufferStart, length); numberOfBytes -= length; // Create iterator and remove the first buffer (as already included) Iterator> iterator = this.previousBuffers.iterator(); iterator.next(); // move to first buffer iterator.remove(); // remove first buffer // Load in remaining data while (iterator.hasNext()) { // Obtain next buffer StreamBuffer buffer = iterator.next(); // Obtain the number of bytes in next buffer length = buffer.pooledBuffer.position(); // Determine if buffer completes the required data if (numberOfBytes <= length) { // Add the sequence (with appropriate bytes) sequence.appendStreamBuffer(buffer, 0, numberOfBytes); // Set start to appropriate position this.firstPreviousBufferStart = numberOfBytes; this.previousBuffersByteCount -= numberOfBytesSkipped; // Return the completed sequence return sequence; } // Include the entire buffer (and remove as included) sequence.appendStreamBuffer(buffer, 0, length); iterator.remove(); // Obtain remaining bytes numberOfBytes -= length; } // All previous buffers removed this.firstPreviousBufferStart = 0; this.previousBuffersByteCount = 0; // As here, require data from current buffer sequence.appendStreamBuffer(this.currentBuffer, 0, numberOfBytes); // Set position for next scan this.currentBufferStartPosition = numberOfBytes; this.currentBufferScanStart = this.currentBufferStartPosition; } // Return the sequence return sequence; } /** *

* Scans the {@link ByteBuffer} to find the next byte of particular value. *

* Note: the algorithm used does not handle negative byte values. However, the * values (ASCII characters) searched for in HTTP parsing are all positive * (lower 7 bits of the byte). * * @param bytes Long of 8 bytes to check for the value. * @param target {@link ScanTarget} for the byte to find the index. * @return Index within the long of the first byte value. Otherwise, * -1 if does not find the byte. */ public static final byte indexOf(long bytes, ScanTarget target) { // Determine if the bytes contain the value long xorWithMask = bytes ^ target.mask; // matching byte will be 0 /* * Need to find if any byte is zero. As ASCII is 7 bits, can use the top bit for * overflow to find if any byte is zero. In other words, add 0x7f (01111111) to * each byte and if top bit is 1, then not the byte of interest (as byte of * interest is 0 from previous XOR). * * Note: top bit value of 0 may only mean potential matching byte, as could have * UTF-8 encoded characters. However, the separation characters for HTTP parsing * are all 7 bit values. Therefore, will need to check for false positives (e.g. * null 0). However, most HTTP content is 7 bits so win efficiency in scanning * past bytes not of interest (i.e. can potentially skip past 8 bytes in 4 * operations - where looping over the 8 bytes is 8 operations minimum). */ long overflowNonZero = xorWithMask + 0x7f7f7f7f7f7f7f7fL; long topBitCheck = overflowNonZero & 0x8080808080808080L; while (topBitCheck != 0x8080808080808080L) { /* * Potential value within the 8 bytes, so search for it. * * To reduce operations, use binary search to determine where byte of interest * is. * * Note: as may have two bytes of interest within the range, need to find the * first actual matching byte. Example case is UTF-8 creating false positives. * However, also HTTP with end of HTTP headers having repeating CR LF bytes. */ long IIII_OOOO_Check = topBitCheck & 0xffffffff00000000L; if (IIII_OOOO_Check != 0x8080808000000000L) { // First byte of interest in first 4 bytes long IIOO_OOOO_Check = topBitCheck & 0xffff000000000000L; if (IIOO_OOOO_Check != 0x8080000000000000L) { // First byte of interest in first 2 bytes long IOOO_OOOO_Check = topBitCheck & 0xff00000000000000L; if (IOOO_OOOO_Check != 0x8000000000000000L) { // Check first byte long check = bytes & 0xff00000000000000L; check >>= 56; // 7 x 8 bits if (check == target.value) { // Found at first byte return 0; } else { // False positive, set top bit to ignore topBitCheck |= 0x8000000000000000L; } } else { // Check second byte long check = bytes & 0x00ff000000000000L; check >>= 48; // 6 x 8 bits if (check == target.value) { // Found at second byte return 1; } else { // False positive, set top bit to ignore topBitCheck |= 0x0080000000000000L; } } } else { // First byte of interest in second 2 bytes long OOIO_OOOO_Check = topBitCheck & 0x0000ff0000000000L; if (OOIO_OOOO_Check != 0x0000800000000000L) { // Check third byte long check = bytes & 0x0000ff0000000000L; check >>= 40; // 5 x 8 bits if (check == target.value) { // Found at third byte return 2; } else { // False positive, set top bit to ignore topBitCheck |= 0x0000800000000000L; } } else { // Check fourth byte long check = bytes & 0x000000ff00000000L; check >>= 32; // 4 x 8 bits if (check == target.value) { // Found at fourth byte return 3; } else { // False positive, set top bit to ignore topBitCheck |= 0x0000008000000000L; } } } } else { // First byte of interest in last 4 bytes long OOOO_IIOO_Check = topBitCheck & 0x00000000ffff0000L; if (OOOO_IIOO_Check != 0x0000000080800000L) { // First byte of interest in third 2 bytes long OOOO_IOOO_Check = topBitCheck & 0x00000000ff000000L; if (OOOO_IOOO_Check != 0x0000000080000000L) { // Check fifth byte long check = bytes & 0x00000000ff000000L; check >>= 24; // 3 x 8 bits if (check == target.value) { // Found at fifth byte return 4; } else { // False positive, set top bit to ignore topBitCheck |= 0x0000000080000000L; } } else { // Check sixth byte long check = bytes & 0x0000000000ff0000L; check >>= 16; // 2 x 8 bits if (check == target.value) { // Found at sixth byte return 5; } else { // False positive, set top bit to ignore topBitCheck |= 0x0000000000800000L; } } } else { // First byte of interest in fourth 2 bytes long OOOO_OOIO_Check = topBitCheck & 0x000000000000ff00L; if (OOOO_OOIO_Check != 0x0000000000008000L) { // Check seventh byte long check = bytes & 0x000000000000ff00L; check >>= 8; // 1 x 8 bits if (check == target.value) { // Found at seventh byte return 6; } else { // False positive, set top bit to ignore topBitCheck |= 0x0000000000008000L; } } else { // Check eighth byte long check = bytes & 0x00000000000000ffL; // bytes already in correct place for check if (check == target.value) { // Found at eighth byte return 7; } else { // False positive, set top bit to ignore // Note: should exit loop topBitCheck |= 0x0000000000000080L; } } } } } // As here, did not find value in buffer return -1; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy