int getBytes(Memory memory, Address mappedSourceBaseAddress, long offsetInSubBlock, byte[] b,
			int off, int len) throws MemoryAccessException, AddressOverflowException {

		if (isOneToOneMapping()) {
			return memory.getBytes(mappedSourceBaseAddress.addNoWrap(offsetInSubBlock), b, off,
				len);
		}

		// NOTE: approach avoids incremental reading by including unmapped bytes in
		// bulk read and filters as needed based upon mapping scheme ratio
		long patternCount = offsetInSubBlock / mappedByteCount;
		int partialByteCount = (int) (offsetInSubBlock % mappedByteCount);
		long mappedOffset = (mappedSourceByteCount * patternCount) + partialByteCount;

		int bufSize = mappedSourceByteCount * ((len / mappedByteCount) + 1);
		byte[] buf = new byte[bufSize];
		int bufCnt = memory.getBytes(mappedSourceBaseAddress.addNoWrap(mappedOffset), buf);
		int bufIndex = 0;

		int cnt = 0;
		int index = off;
		int i = mappedByteCount - partialByteCount;
		boolean skip = false;
		while (bufIndex < bufCnt && cnt < len) {
			if (!skip) {
				b[index++] = buf[bufIndex];
				++cnt;
				if (--i == 0) {
					skip = true;
					i = nonMappedByteCount;
				}
			}
			else if (--i == 0) {
				skip = false;
				i = mappedByteCount;
			}
			++bufIndex;
		}
		return cnt;
	}
Read bytes into an array from memory utilizing this mapping scheme.
@param memory program memory
@param mappedSourceBaseAddress base source memory address for byte-mapped subblock
@param offsetInSubBlock byte offset from start of subblock where reading should begin
@param b byte array to be filled
@param off offset within byte array b where filling should start
@param len number of bytes to be read
@return actual number of bytes read
@throws MemoryAccessException if read of uninitialized or non-existing memory occurs
@throws AddressOverflowException if address computation error occurs
Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/database/mem/ByteMappingScheme.java