support custom allocators for builder segments

This commit is contained in:
David Renshaw 2019-03-17 19:39:54 -04:00
parent c6762ff0f7
commit 0ff0cfa338
4 changed files with 103 additions and 31 deletions

View file

@ -0,0 +1,13 @@
package org.capnproto;
/**
* An object that allocates memory for a Cap'n Proto message as it is being built.
*/
interface Allocator {
/**
* Allocates a ByteBuffer to be used as a segment in a message. The returned
* buffer must contain at least `minimumSize` bytes, all of which MUST be
* set to zero.
*/
public java.nio.ByteBuffer allocateSegment(int minimumSize);
}

View file

@ -26,7 +26,6 @@ import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
public final class BuilderArena implements Arena { public final class BuilderArena implements Arena {
public enum AllocationStrategy { public enum AllocationStrategy {
FIXED_SIZE, FIXED_SIZE,
GROW_HEURISTICALLY GROW_HEURISTICALLY
@ -37,19 +36,20 @@ public final class BuilderArena implements Arena {
AllocationStrategy.GROW_HEURISTICALLY; AllocationStrategy.GROW_HEURISTICALLY;
public final ArrayList<SegmentBuilder> segments; public final ArrayList<SegmentBuilder> segments;
private Allocator allocator;
public int nextSize;
public final AllocationStrategy allocationStrategy;
public BuilderArena(int firstSegmentSizeWords, AllocationStrategy allocationStrategy) { public BuilderArena(int firstSegmentSizeWords, AllocationStrategy allocationStrategy) {
this.segments = new ArrayList<SegmentBuilder>(); this.segments = new ArrayList<SegmentBuilder>();
this.nextSize = firstSegmentSizeWords; {
this.allocationStrategy = allocationStrategy; DefaultAllocator allocator = new DefaultAllocator(allocationStrategy);
SegmentBuilder segment0 = new SegmentBuilder( allocator.setNextAllocationSizeBytes(firstSegmentSizeWords * Constants.BYTES_PER_WORD);
ByteBuffer.allocate(firstSegmentSizeWords * Constants.BYTES_PER_WORD), this); this.allocator = allocator;
segment0.buffer.order(ByteOrder.LITTLE_ENDIAN); }
this.segments.add(segment0); }
public BuilderArena(Allocator allocator) {
this.segments = new ArrayList<SegmentBuilder>();
this.allocator = allocator;
} }
@Override @Override
@ -76,32 +76,19 @@ public final class BuilderArena implements Arena {
} }
public AllocateResult allocate(int amount) { public AllocateResult allocate(int amount) {
int len = this.segments.size(); int len = this.segments.size();
// we allocate the first segment in the constructor. // we allocate the first segment in the constructor.
if (len > 0) {
int result = this.segments.get(len - 1).allocate(amount); int result = this.segments.get(len - 1).allocate(amount);
if (result != SegmentBuilder.FAILED_ALLOCATION) { if (result != SegmentBuilder.FAILED_ALLOCATION) {
return new AllocateResult(this.segments.get(len - 1), result); return new AllocateResult(this.segments.get(len - 1), result);
}
} }
// allocate_owned_memory
int size = Math.max(amount, this.nextSize);
SegmentBuilder newSegment = new SegmentBuilder( SegmentBuilder newSegment = new SegmentBuilder(
ByteBuffer.allocate(size * Constants.BYTES_PER_WORD), this.allocator.allocateSegment(amount * Constants.BYTES_PER_WORD),
this); this);
switch (this.allocationStrategy) {
case GROW_HEURISTICALLY:
this.nextSize += size;
break;
default:
break;
}
// --------
newSegment.buffer.order(ByteOrder.LITTLE_ENDIAN); newSegment.buffer.order(ByteOrder.LITTLE_ENDIAN);
newSegment.id = len; newSegment.id = len;
this.segments.add(newSegment); this.segments.add(newSegment);

View file

@ -0,0 +1,65 @@
package org.capnproto;
import java.nio.ByteBuffer;
import org.capnproto.BuilderArena.AllocationStrategy;
class DefaultAllocator implements Allocator {
// (minimum) number of bytes in the next allocation
private int nextSize = 0;
public enum ByteBufferAllocationStyle {
REGULAR,
DIRECT
}
public ByteBufferAllocationStyle allocationStyle = ByteBufferAllocationStyle.REGULAR;
public AllocationStrategy allocationStrategy =
AllocationStrategy.GROW_HEURISTICALLY;
public DefaultAllocator(AllocationStrategy allocationStrategy) {
this.allocationStrategy = allocationStrategy;
}
public DefaultAllocator(ByteBufferAllocationStyle style) {
this.allocationStyle = style;
}
public DefaultAllocator(AllocationStrategy allocationStrategy,
ByteBufferAllocationStyle style) {
this.allocationStrategy = allocationStrategy;
this.allocationStyle = style;
}
public void setNextAllocationSizeBytes(int nextSize) {
this.nextSize = nextSize;
}
@Override
public java.nio.ByteBuffer allocateSegment(int minimumSize) {
int size = Math.max(minimumSize, this.nextSize);
ByteBuffer result = null;
switch (allocationStyle) {
case REGULAR:
result = ByteBuffer.allocate(size);
break;
case DIRECT:
result = ByteBuffer.allocateDirect(size);
}
switch (this.allocationStrategy) {
case GROW_HEURISTICALLY:
this.nextSize += size;
break;
case FIXED_SIZE:
if (nextSize == 0) {
nextSize = size;
}
break;
}
this.nextSize += size;
return result;
}
}

View file

@ -40,7 +40,14 @@ public final class MessageBuilder {
allocationStrategy); allocationStrategy);
} }
public MessageBuilder(Allocator allocator) {
this.arena = new BuilderArena(allocator);
}
private AnyPointer.Builder getRootInternal() { private AnyPointer.Builder getRootInternal() {
if (this.arena.segments.isEmpty()) {
this.arena.allocate(1);
}
SegmentBuilder rootSegment = this.arena.segments.get(0); SegmentBuilder rootSegment = this.arena.segments.get(0);
if (rootSegment.currentSize() == 0) { if (rootSegment.currentSize() == 0) {
int location = rootSegment.allocate(1); int location = rootSegment.allocate(1);