/** * Gets a value indicating whether the DataFrame is sealed. */ boolean isSealed() { return this.sealed || this.contents.isReadOnly(); }
/** * Returns a new ByteArraySegment that wraps the same underlying array that this ByteSegmentDoes, except that the * new instance is marked as Read-Only. * If this instance is already Read-Only, this instance is returned instead. * * @return A new read-only ByteArraySegment. */ public ByteArraySegment asReadOnly() { if (isReadOnly()) { return this; } else { return new ByteArraySegment(this.array, this.startOffset, this.length, true); } }
/** * Seals the frame for writing. After this method returns, no more modifications are allowed on this DataFrame. * This method has no effect if the Frame is read-only if it is already sealed. * * @throws IllegalStateException If an open entry exists (entries must be closed prior to sealing). */ void seal() { if (!this.sealed && !this.contents.isReadOnly()) { Preconditions.checkState(writeEntryStartIndex < 0, "An open entry exists. Any open entries must be closed prior to sealing."); this.header.setContentLength(writePosition); this.header.commit(); this.sealed = true; } }
/** * 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); }
void serialize() { Preconditions.checkState(this.data != null && !this.data.isReadOnly(), "Cannot serialize a read-only EntryHeader."); // Write length. BitConverter.writeInt(this.data, 0, getEntryLength()); // Write flags. byte flags = isFirstRecordEntry() ? FIRST_ENTRY_MASK : 0; flags |= isLastRecordEntry() ? LAST_ENTRY_MASK : 0; this.data.set(FLAGS_OFFSET, flags); } }
void commit() { Preconditions.checkState(this.buffer != null && !this.buffer.isReadOnly(), "Cannot commit a read-only FrameHeader"); assert this.buffer.getLength() == SERIALIZATION_LENGTH; // We already checked the size of the target buffer (in the constructor); no need to do it here again. int bufferOffset = 0; this.buffer.set(bufferOffset, getVersion()); bufferOffset += Byte.BYTES; bufferOffset += BitConverter.writeInt(this.buffer, bufferOffset, getContentLength()); this.buffer.set(bufferOffset, encodeFlags()); } }
/** * 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); }
/** * 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()); }