public int readBytes(long index, byte[] buffer, int offset, int len) throws IOException {
		ensureBounds(index, 0);
		len = (int) Math.min(length - index, len);
		int totalBytesRead = 0;
		int bufferDest = offset;
		long currentIndex = index;
		while (totalBytesRead < len) {
			Entry<Long, Long> entry = offsetMap.floorEntry(currentIndex);
			Entry<Long, Long> nextEntry = offsetMap.higherEntry(entry.getKey());

			long rangeStart = entry.getKey();
			long rangeOffset = currentIndex - rangeStart;
			long rangeEnd = (nextEntry != null) ? nextEntry.getKey() : length;
			long delegateRangeStart = entry.getValue();
			int bytesToRead =
				(int) Math.min(len - totalBytesRead, rangeEnd - rangeStart - rangeOffset);
			if (delegateRangeStart != -1) {
				long delegateOffsetToRead = delegateRangeStart + rangeOffset;
				// TODO: when ByteProvider interface has better readBytes() method, use it here
				byte[] rangeBytes = delegate.readBytes(delegateOffsetToRead, bytesToRead);
				System.arraycopy(rangeBytes, 0, buffer, bufferDest, bytesToRead);
			}
			else {
				// the range was not present, result will be 0's
				Arrays.fill(buffer, bufferDest, bufferDest + bytesToRead,
					(byte) 0 /* fill value */);
			}
			totalBytesRead += bytesToRead;
			bufferDest += bytesToRead;
			currentIndex += bytesToRead;
		}
		return totalBytesRead;
	}
Read bytes at the specified index into the given byte array.
<p>
See {@link InputStream#read(byte[], int, int)}.

@param index file offset to start reading
@param buffer byte array that will receive the bytes
@param offset offset inside the byte array to place the bytes
@param len number of bytes to read
@return number of actual bytes read
@throws IOException if error
Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/RangeMappedByteProvider.java