public synchronized void getData(final long loc, final byte[] data, final int offset,
			final int length) {
		if (length < 0) {
			throw new IllegalArgumentException("length: " + length);
		}
		// Read in portion of first block (could be full block)
		long blockNum = Long.divideUnsigned(loc, BLOCK_SIZE);
		int blockOffset = (int) Long.remainderUnsigned(loc, BLOCK_SIZE);
		byte[] block = blocks.get(blockNum);
		int amt = Math.min(length, BLOCK_SIZE - blockOffset);
		if (block != null) {
			System.arraycopy(block, blockOffset, data, offset, amt);
		}

		// Read in each following block
		int cur = amt;
		while (cur < length) {
			blockNum++;
			if (blockNum == 0) {
				throw new BufferUnderflowException();
			}
			block = blocks.get(blockNum);
			amt = Math.min(length - cur, BLOCK_SIZE);
			if (block != null) {
				System.arraycopy(block, 0, data, cur + offset, amt);
			}
			cur += amt;
		}
	}
Copy a range of data from the semisparse array into a portion of the given byte array

<p>
Copies {@code length} bytes of data from the semisparse array starting at index {@code loc}
into {@code data} starting at index {@code offset}. All initialized portions within the
requested region are copied. The uninitialized portions may be treated as zeroes or not
copied at all. Typically, the destination array has been initialized to zero by the caller,
such that all uninitialized portions are zero. To avoid fetching uninitialized data, use
{@link #contiguousAvailableAfter(long)} as an upper bound on the length.

@param loc the index to begin copying data out
@param data the array to copy data into
@param offset the offset into the destination array
@param length the length of data to read
Ghidra/Framework/Emulation/src/main/java/ghidra/generic/util/datastruct/SemisparseByteArray.java