support custom allocators for builder segments
This commit is contained in:
parent
c6762ff0f7
commit
0ff0cfa338
4 changed files with 103 additions and 31 deletions
13
runtime/src/main/java/org/capnproto/Allocator.java
Normal file
13
runtime/src/main/java/org/capnproto/Allocator.java
Normal 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);
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
65
runtime/src/main/java/org/capnproto/DefaultAllocator.java
Normal file
65
runtime/src/main/java/org/capnproto/DefaultAllocator.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue