/** * Returns a new ByteArraySegment that is a sub-segment of this ByteArraySegment. The new ByteArraySegment wraps * the same underlying byte array that this ByteArraySegment does. * * @param offset The offset within this ByteArraySegment where the new ByteArraySegment starts. * @param length The length of the new ByteArraySegment. * @return The new ByteArraySegment. * @throws ArrayIndexOutOfBoundsException If offset or length are invalid. */ public ByteArraySegment subSegment(int offset, int length) { return subSegment(offset, length, this.readOnly); }
/** * Gets a PageEntry representing the entry (Key + Value) at the given Position. * * @param pos The position to get the value at. * @return A PageEntry containing the entry at the given Position. Note that the ByteArraySegments returned by this * PageEntry's getKey() and getValue() are views inside a larger array and any modifications to that array will be * reflected in them. If this value needs to be held for longer then it is recommended to get a copy of it (use getCopy()). */ PageEntry getEntryAt(int pos) { Preconditions.checkElementIndex(pos, getCount(), "pos must be non-negative and smaller than the number of items."); return new PageEntry( this.data.subSegment(pos * this.config.entryLength, this.config.keyLength), this.data.subSegment(pos * this.config.entryLength + this.config.keyLength, this.config.valueLength)); }
/** * Gets a ByteArraySegment representing the value at the given Position. * * @param pos The position to get the value at. * @return A ByteArraySegment containing the value at the given Position. Note that this is a view inside a larger array * and any modifications to that array will be reflected in this. If this value needs to be held for * longer then it is recommended to get a copy of it (use getCopy()). */ ByteArraySegment getValueAt(int pos) { Preconditions.checkElementIndex(pos, getCount(), "pos must be non-negative and smaller than the number of items."); return this.data.subSegment(pos * this.config.entryLength + this.config.keyLength, this.config.valueLength); }
private ArrayView getKeyData(ArrayView soughtKey, ByteArraySegment readData, EntrySerializer.Header header) { if (soughtKey == null) { soughtKey = readData.subSegment(header.getKeyOffset(), header.getKeyLength()); } return soughtKey; } }
/** * Gets the Key at the given Position. * * @param pos The Position to get the Key at. * @return A ByteArraySegment containing the Key at the given Position. Note that this is a view inside a larger array * and any modifications to that array will be reflected in this. If this value needs to be held for * longer then it is recommended to get a copy of it (use getCopy()). */ ByteArraySegment getKeyAt(int pos) { Preconditions.checkElementIndex(pos, getCount(), "pos must be non-negative and smaller than the number of items."); return this.data.subSegment(pos * this.config.entryLength, this.config.keyLength); }
/** * Returns an ArrayView representing the serialized form of this frame. */ ArrayView getData() { if (this.data.isReadOnly()) { return this.data; } else { // We have just created this frame. Only return the segment of the buffer that contains data. return this.data.subSegment(0, getLength()); } }
/** * Creates a new instance of a DataFrame. * * @param source The ByteArraySegment to wrap. */ DataFrame(ByteArraySegment source) { Preconditions.checkArgument(!source.isReadOnly(), "Cannot create a WriteFrame for a readonly source."); this.data = source; this.writeEntryStartIndex = -1; this.sealed = source.isReadOnly(); this.writePosition = this.sealed ? -1 : 0; //We want to use the DataFrame for at least 1 byte of data. int sourceLength = this.data.getLength(); Exceptions.checkArgument(sourceLength > FrameHeader.SERIALIZATION_LENGTH, "data", "Insufficient array length. Byte array must have a length of at least %d.", FrameHeader.SERIALIZATION_LENGTH + 1); this.header = new WriteFrameHeader(CURRENT_VERSION, this.data.subSegment(0, FrameHeader.SERIALIZATION_LENGTH)); this.contents = this.data.subSegment(FrameHeader.SERIALIZATION_LENGTH, sourceLength - FrameHeader.SERIALIZATION_LENGTH); }
/** * Creates a new instance of the BTreePage class wrapping an existing ByteArraySegment. * * @param config Page Configuration. * @param contents The ByteArraySegment to wrap. Changes to this BTreePage may change the values in the array backing * this ByteArraySegment. * @param validate If true, will perform validation. * @throws IllegalDataFormatException If the given contents is not a valid BTreePage format and validate == true. */ private BTreePage(@NonNull Config config, @NonNull ByteArraySegment contents, boolean validate) { Preconditions.checkArgument(!contents.isReadOnly(), "Cannot wrap a read-only ByteArraySegment."); this.config = config; this.contents = contents; this.header = contents.subSegment(0, DATA_OFFSET); this.data = contents.subSegment(DATA_OFFSET, contents.getLength() - DATA_OFFSET - FOOTER_LENGTH); this.footer = contents.subSegment(contents.getLength() - FOOTER_LENGTH, FOOTER_LENGTH); if (validate) { int headerId = getHeaderId(); int footerId = getFooterId(); if (headerId != footerId) { throw new IllegalDataFormatException("Invalid Page Format (id mismatch). HeaderId=%s, FooterId=%s.", headerId, footerId); } } // Cache the count value. It's used a lot. this.count = BitConverter.readInt(this.header, COUNT_OFFSET); }
/** * Indicates that a new DataFrame Entry should be opened. * * @param firstRecordEntry If true, this entry will be marked as the first entry in a record (records can be split * across multiple frames, and this helps ensure that, upon reading, the records are recomposed * starting with the right Data Frame Entry). * @return True if we were able to start an entry in this Frame, or false otherwise (this happens in case the frame is full). * If the frame is full, the new entry will need to be started on a new Frame. * @throws IllegalStateException If the entry is sealed. */ boolean startNewEntry(boolean firstRecordEntry) { Preconditions.checkState(!this.sealed, "DataFrame is sealed and cannot accept any more entries."); endEntry(true); if (getAvailableLength() < MIN_ENTRY_LENGTH_NEEDED) { // If we cannot fit at least entry header + 1 byte, we cannot record anything in this write frame anymore. return false; } this.writeEntryStartIndex = this.writePosition; this.writeEntryHeader = new WriteEntryHeader(this.contents.subSegment(this.writePosition, WriteEntryHeader.HEADER_SIZE)); this.writeEntryHeader.setFirstRecordEntry(firstRecordEntry); this.writePosition += WriteEntryHeader.HEADER_SIZE; return true; }
/** * Completes this Request with the given request as a source (this Request is a sub-interval of the given request). * * @param source The source Request to complete with. */ private void complete(Request source) { Preconditions.checkState(!isDone(), "This Request is already completed."); Preconditions.checkArgument(source.isDone(), "Given request is not completed."); Preconditions.checkArgument(isSubRequest(source, this), "This Request is not a sub-request of the given one."); try { // Get the source Request's result, slice it and return the sub-segment that this request maps to. Result sourceResult = source.resultFuture.join(); int offset = (int) (this.getOffset() - source.getOffset()); this.resultFuture.complete(new Result(sourceResult.getData().subSegment(offset, getLength()), true)); } catch (Throwable ex) { if (Exceptions.mustRethrow(ex)) { throw ex; } fail(ex); } }
CompletableFuture<ByteArraySegment> read(long offset, int length, Duration timeout) { return CompletableFuture.supplyAsync(() -> { synchronized (this.data) { if (this.checkOffsets.get()) { // We want to make sure that we actually read pages that we wrote, and not from arbitrary locations // in the data source. Preconditions.checkArgument(this.offsets.isEmpty() || this.offsets.getOrDefault(offset, false), "Offset not registered or already obsolete: " + offset); } return new ByteArraySegment(this.data.getData().subSegment((int) offset, length).getCopy()); } }, executorService()); }
@Override public ReadResult read(long offset, int maxLength, Duration timeout) { // We actually get a view of the data frozen in time, as any changes to the contents field after exiting from the // synchronized block may create a new buffer, but we don't care as the data we already have won't change. ByteArraySegment dataView; synchronized (this) { dataView = this.contents.getData(); } // We get a slice of the data view, and return a ReadResultMock with entry lengths of 3. return new ReadResultMock(offset, dataView.subSegment((int) offset, dataView.getLength() - (int) offset), maxLength, 3); }
ByteArraySegment splitPageData = this.data.subSegment(readIndex, itemsPerPage * this.config.entryLength); result.add(new BTreePage(this.config, itemsPerPage, splitPageData));
@Override protected boolean processReadData(ByteArraySegment readData) { val header = getHeader(); assert header != null : "acceptResult called with no header loaded."; if (readData.getLength() >= EntrySerializer.HEADER_LENGTH + header.getKeyLength()) { // We read enough information. ArrayView keyData = readData.subSegment(header.getKeyOffset(), header.getKeyLength()); if (header.isDeletion()) { complete(TableKey.notExists(keyData)); } else { complete(TableKey.versioned(keyData, this.keyVersion)); } return true; // We are done. } return false; } }
int bytesAppended = dataFrame.append(record.subSegment(0, firstHalfLength)); dataFrame.endEntry(false); // false - we did not finish the record. if (bytesAppended < firstHalfLength) { bytesAppended = dataFrame.append(record.subSegment(firstHalfLength, secondHalfLength)); fullRecordsAppended += bytesAppended; if (bytesAppended < secondHalfLength) {
/** * Tests the ability for the ByteArraySegment to create sub-segments. */ @Test public void testSubSegment() { final byte[] buffer = createFormattedBuffer(); ByteArraySegment segment = new ByteArraySegment(buffer); // As long as the size of the segment > 1, choose a segment half the length of the current one. // If current iteration is odd, choose the upper segment. If it is even, choose the lower one. int iteration = 0; int startOffset = 0; while (segment.getLength() > 1) { iteration++; // Check correctness. for (int i = 0; i < segment.getLength(); i++) { Assert.assertEquals(String.format("Unexpected value at offset %d for subsegment (O=%d, L=%d), iteration %d.", i, startOffset, segment.getLength(), iteration), buffer[i + startOffset], segment.get(i)); } // Pick a new size and create a new subsegment. if (iteration % 2 == 0) { // Upper half for even iterations. startOffset = startOffset + segment.getLength() / 2; segment = segment.subSegment(segment.getLength() / 2, segment.getLength() - segment.getLength() / 2); } else { // Lower half for odd iterations. segment = segment.subSegment(0, segment.getLength() / 2); } } }
/** * Tests the behavior of the ByteArraySegment in read-only mode. */ @Test public void testReadOnly() throws Exception { final byte[] buffer = createFormattedBuffer(); ByteArraySegment segment = new ByteArraySegment(buffer, 0, buffer.length, true); // Check the isReadonly flag Assert.assertTrue("Unexpected value for isReadOnly() for read-only segment.", segment.isReadOnly()); Assert.assertFalse("Unexpected value for isReadOnly() for non-read-only segment.", new ByteArraySegment(buffer).isReadOnly()); // Verify that "mutator" methods do not work. checkReadOnlyException("copyFrom", () -> segment.copyFrom(new ByteArraySegment(new byte[10], 0, 10), 0, 10)); checkReadOnlyException("getWriter", segment::getWriter); checkReadOnlyException("set", () -> segment.set(0, (byte) 0)); // Check to see that, even though we did get an exception, the buffer was not modified. for (int i = 0; i < buffer.length; i++) { Assert.assertEquals("One of the 'mutator' methods modified the buffer at index " + i, i, buffer[i]); } // Check that a subsegment is also read-only. Assert.assertTrue("Unexpected value for isReadOnly() for read-only sub-segment.", segment.subSegment(0, 1).isReadOnly()); Assert.assertTrue("Unexpected value for isReadOnly() for read-only sub-segment from non-read-only segment (when attempting to create a non-read-only segment).", segment.subSegment(0, 1, false).isReadOnly()); Assert.assertTrue("Unexpected value for isReadOnly() for read-only sub-segment from non-read-only segment.", new ByteArraySegment(buffer).subSegment(0, 1, true).isReadOnly()); }
ByteArraySegment keyData = readData.subSegment(header.getKeyOffset(), header.getKeyLength()); for (int i = 0; i < this.soughtKey.getLength(); i++) { if (this.soughtKey.get(i) != keyData.get(i)) { valueData = new ByteArraySegment(new byte[0]); } else { valueData = readData.subSegment(header.getValueOffset(), header.getValueLength());