diff --git a/runtime/src/main/java/org/capnproto/BuilderArena.java b/runtime/src/main/java/org/capnproto/BuilderArena.java index 0323fad..02d4396 100644 --- a/runtime/src/main/java/org/capnproto/BuilderArena.java +++ b/runtime/src/main/java/org/capnproto/BuilderArena.java @@ -64,6 +64,30 @@ public final class BuilderArena implements Arena { this.allocator = allocator; } + /** + * Constructs a BuilderArena from a ReaderArena and uses the size of the largest segment + * as the next allocation size. + */ + BuilderArena(ReaderArena arena) { + this.segments = new ArrayList(); + int largestSegment = SUGGESTED_FIRST_SEGMENT_WORDS*Constants.BYTES_PER_WORD; + for (int ii = 0; ii < arena.segments.size(); ++ii) { + SegmentReader segment = arena.segments.get(ii); + SegmentBuilder segmentBuilder = new SegmentBuilder(segment.buffer, this); + segmentBuilder.id = ii; + segmentBuilder.pos = segmentBuilder.capacity(); // buffer is pre-filled + segments.add(segmentBuilder); + + // Find the largest segment for the allocation strategy. + largestSegment = Math.max(largestSegment, segment.buffer.capacity()); + } + DefaultAllocator defaultAllocator = new DefaultAllocator(SUGGESTED_ALLOCATION_STRATEGY); + + // Use largest segment as next size. + defaultAllocator.setNextAllocationSizeBytes(largestSegment); + this.allocator = defaultAllocator; + } + @Override public final SegmentReader tryGetSegment(int id) { return this.segments.get(id); diff --git a/runtime/src/main/java/org/capnproto/MessageBuilder.java b/runtime/src/main/java/org/capnproto/MessageBuilder.java index 40d714d..8d75b6d 100644 --- a/runtime/src/main/java/org/capnproto/MessageBuilder.java +++ b/runtime/src/main/java/org/capnproto/MessageBuilder.java @@ -71,6 +71,22 @@ public final class MessageBuilder { this.arena = new BuilderArena(new DefaultAllocator(), firstSegment); } + /** + * Constructs a MessageBuilder from a MessageReader. This constructor is private + * because it is unsafe. To use it, you must call `unsafeConstructFromMessageReader()`. + */ + private MessageBuilder(MessageReader messageReader) { + this.arena = new BuilderArena(messageReader.arena); + } + + /** + * Constructs a MessageBuilder from a MessageReader without copying the segments. + * This method should only be used on trusted data. Otherwise you may observe infinite + * loops or large memory allocations or index-out-of-bounds errors. + */ + public static MessageBuilder unsafeConstructFromMessageReader(MessageReader messageReader) { + return new MessageBuilder(messageReader); + } private AnyPointer.Builder getRootInternal() { if (this.arena.segments.isEmpty()) { diff --git a/runtime/src/main/java/org/capnproto/SegmentBuilder.java b/runtime/src/main/java/org/capnproto/SegmentBuilder.java index e9ac57a..9727b58 100644 --- a/runtime/src/main/java/org/capnproto/SegmentBuilder.java +++ b/runtime/src/main/java/org/capnproto/SegmentBuilder.java @@ -35,7 +35,7 @@ public final class SegmentBuilder extends SegmentReader { } // the total number of words the buffer can hold - private final int capacity() { + final int capacity() { this.buffer.rewind(); return this.buffer.remaining() / 8; }