public void apply(InputStream in, long maxBytes, ArrayList<Match> match, TaskMonitor monitor)
			throws IOException {
		long progress = monitor.getProgress();

		int maxSize = getMaxSequenceSize() + 1;
		if (maxSize < 4096) {
			maxSize = 4096;
		}
		if (maxBytes > 0) {
			maxBytes += getMaxSequenceSize() + 1;
		}
		byte[] firstBuf = new byte[maxSize];
		byte[] secondBuf = new byte[maxSize];
		byte[] curBuf;
		SequenceSearchState curState;
		int fullBuffers;				// Number of buffers that are completely full
		int ra = in.read(firstBuf);
		if (ra == firstBuf.length) {
			ra = in.read(secondBuf);
			if (ra == secondBuf.length) {
				fullBuffers = 2;
			}
			else {
				if (ra < 0) {
					ra = 0;
				}
				fullBuffers = 1;
				byte[] tmp = new byte[ra];
				for (int i = 0; i < ra; ++i) {
					tmp[i] = secondBuf[i];
				}
				secondBuf = tmp;
			}
		}
		else if (ra < 0) {
			return;				// No bytes at all were read
		}
		else {
			byte[] tmp = new byte[ra];
			for (int i = 0; i < ra; ++i) {
				tmp[i] = firstBuf[i];
			}
			firstBuf = tmp;
			fullBuffers = 0;
			secondBuf = new byte[0];
		}
		int offset = 0;
		int bufRelativeOffset = 0;
		int subIndex;
		while (fullBuffers == 2) {
			curState = this;			// New starting offset -> Root state
			subIndex = bufRelativeOffset;
			curBuf = firstBuf;
			do {
				if (curState.success != null) {
					curState.exportSuccess(match, offset);
				}
				if (subIndex >= curBuf.length) {						// check that we have enough bytes in current buffer
					curBuf = secondBuf;									// If not, switch to secondary buffer
					subIndex = 0;
				}
				curState = curState.trans[0xff & curBuf[subIndex]];		// Perform state transition based on next byte in buffer
				subIndex += 1;
			}
			while (curState != null);
			offset += 1;												// Advance to next starting offset
			if (maxBytes > 0 && offset > maxBytes) {
				break;
			}
			bufRelativeOffset += 1;
			if (bufRelativeOffset == firstBuf.length) {							// If starting offset no longer falls in firstbuf
				byte[] tmp = firstBuf;									//     Switch firstbuf with secondbuf
				firstBuf = secondBuf;
				secondBuf = tmp;
				ra = in.read(secondBuf);							//     refill secondbuf (old firstbuf) with new bytes
				if (monitor != null) {
					if (monitor.isCancelled()) {
						return;
					}
					monitor.setProgress(progress + offset);
				}
				if (ra != secondBuf.length) {
					fullBuffers = 1;
					if (ra < 0) {
						ra = 0;
					}
					tmp = new byte[ra];
					for (int i = 0; i < ra; ++i) {
						tmp[i] = secondBuf[i];
					}
					secondBuf = tmp;
				}
				bufRelativeOffset = 0;
			}
		}

		while (fullBuffers >= 0 && (maxBytes <= 0 || offset < maxBytes)) {
			if (secondBuf.length == 0) {
				fullBuffers = 0;
			}
			curState = this;
			subIndex = bufRelativeOffset;
			curBuf = firstBuf;
			do {
				if (curState.success != null) {
					curState.exportSuccess(match, offset);
				}
				if (subIndex >= curBuf.length) {
					if (curBuf == secondBuf) {
						break;				// Out of data, all pending patterns fail
					}
					curBuf = secondBuf;
					subIndex = 0;
					if (curBuf.length == 0) {
						break;
					}
				}
				curState = curState.trans[0xff & curBuf[subIndex]];
				subIndex += 1;
			}
			while (curState != null);
			offset += 1;
			bufRelativeOffset += 1;
			if (bufRelativeOffset == firstBuf.length) {
				if (fullBuffers == 0) {
					break;
				}
				firstBuf = secondBuf;
				fullBuffers = 0;
				bufRelativeOffset = 0;
				secondBuf = new byte[0];
			}
		}
	}
Search for pattern in the stream -in-.
@param in - The stream to scan for matches
@param maxBytes - The maximum number of bytes to scan forward in this stream
@param match - Any matches are appended as Match records to this ArrayList
@param monitor - if non-null, check for user cancel, and maintain progress info
@throws IOException
Ghidra/Features/Base/src/main/java/ghidra/util/bytesearch/SequenceSearchState.java