capnproto-java-rpc/runtime/src/main/java/org/capnproto/WireHelpers.java
2014-06-05 18:25:29 -04:00

233 lines
9.3 KiB
Java

package org.capnproto;
final class WireHelpers {
public static int roundBytesUpToWords(int bytes) {
return (bytes + 7) / 8;
}
public static int allocate(int refOffset,
SegmentBuilder segment,
int amount,
byte kind) {
// TODO check for nullness, amount == 0 case.
int allocation = segment.allocate(amount);
if (allocation == SegmentBuilder.FAILED_ALLOCATION) {
//# Need to allocate in a new segment. We'll need to
//# allocate an extra pointer worth of space to act as
//# the landing pad for a far pointer.
throw new Error("unimplemented");
} else {
WirePointer.setKindAndTarget(segment.buffer, refOffset, kind, allocation);
return allocation;
}
}
public static StructBuilder initStructPointer(int refOffset,
SegmentBuilder segment,
StructSize size) {
int ptrOffset = allocate(refOffset, segment, size.total(), WirePointer.STRUCT);
StructPointer.setFromStructSize(segment.buffer, refOffset, size);
return new StructBuilder(segment, ptrOffset * 8, ptrOffset + size.data,
size.data * 64, size.pointers, (byte)0);
}
public static StructBuilder getWritableStructPointer(int refOffset,
SegmentBuilder segment,
StructSize size) {
long ref = WirePointer.get(segment.buffer, refOffset);
int target = WirePointer.target(refOffset, ref);
if (WirePointer.isNull(ref)) {
return initStructPointer(refOffset, segment, size);
}
long oldRef = ref;
SegmentBuilder oldSegment = segment;
// TODO follow fars.
int oldPtrOffset = target;
short oldDataSize = StructPointer.dataSize(WirePointer.structPointer(oldRef));
short oldPointerCount = StructPointer.ptrCount(WirePointer.structPointer(oldRef));
int oldPointerSectionOffset = oldPtrOffset + oldDataSize;
if (oldDataSize < size.data || oldPointerCount < size.pointers) {
throw new Error("unimplemented");
} else {
return new StructBuilder(oldSegment, oldPtrOffset * 8,
oldPointerSectionOffset, oldDataSize * 64,
oldPointerCount, (byte)0);
}
}
public static ListBuilder initListPointer(int refOffset,
SegmentBuilder segment,
int elementCount,
byte elementSize) {
throw new Error("unimplemented");
}
public static ListBuilder initStructListPointer(int refOffset,
SegmentBuilder segment,
int elementCount,
StructSize elementSize) {
if (elementSize.preferredListEncoding != FieldSize.INLINE_COMPOSITE) {
//# Small data-only struct. Allocate a list of primitives instead.
return initListPointer(refOffset, segment, elementCount,
elementSize.preferredListEncoding);
}
int wordsPerElement = elementSize.total();
//# Allocate the list, prefixed by a single WirePointer.
int wordCount = elementCount * wordsPerElement;
int ptrOffset = allocate(refOffset, segment, 1 + wordCount, WirePointer.LIST);
//# Initialize the pointer.
ListPointer.setInlineComposite(segment.buffer, refOffset, wordCount);
WirePointer.setKindAndInlineCompositeListElementCount(segment.buffer, ptrOffset,
WirePointer.STRUCT, elementCount);
StructPointer.setFromStructSize(segment.buffer, ptrOffset, elementSize);
ptrOffset += 1;
return new ListBuilder(segment, ptrOffset * 8, elementCount, wordsPerElement * 64,
elementSize.data * 64, elementSize.pointers);
}
// size is in bytes
public static Text.Builder initTextPointer(int refOffset,
SegmentBuilder segment,
int size) {
//# The byte list must include a NUL terminator.
int byteSize = size + 1;
//# Allocate the space.
int ptrOffset = allocate(refOffset, segment, roundBytesUpToWords(byteSize), WirePointer.LIST);
//# Initialize the pointer.
ListPointer.set(segment.buffer, refOffset, FieldSize.BYTE, byteSize);
return new Text.Builder(segment.buffer, ptrOffset * 8, size);
}
public static Text.Builder setTextPointer(int refOffset,
SegmentBuilder segment,
Text.Reader value) {
Text.Builder builder = initTextPointer(refOffset, segment, value.size);
// TODO is there a way to do this with bulk methods?
for (int i = 0; i < builder.size; ++i) {
builder.buffer.put(builder.offset + i, value.buffer.get(value.offset + i));
}
return builder;
}
public static StructReader readStructPointer(SegmentReader segment,
int refOffset,
int nestingLimit) {
// TODO error handling
if (nestingLimit < 0) {
throw new DecodeException("Message is too deeply nested or contains cycles.");
}
long ref = WirePointer.get(segment.buffer, refOffset);
int ptrOffset = WirePointer.target(refOffset, ref);
int structPtr = WirePointer.structPointer(ref);
int dataSizeWords = StructPointer.dataSize(structPtr);
return new StructReader(segment,
ptrOffset * 8,
(ptrOffset + dataSizeWords),
dataSizeWords * 64,
StructPointer.ptrCount(structPtr),
(byte)0,
nestingLimit - 1);
}
public static ListReader readListPointer(SegmentReader segment,
int refOffset,
byte expectedElementSize,
int nestingLimit) {
long ref = WirePointer.get(segment.buffer, refOffset);
// TODO check for null, follow fars, nestingLimit
if (WirePointer.isNull(ref)) {
return new ListReader();
}
int listPtr = WirePointer.listPointer(ref);
int ptrOffset = WirePointer.target(refOffset, ref);
long ptr = WirePointer.get(segment.buffer, ptrOffset);
switch (ListPointer.elementSize(listPtr)) {
case FieldSize.INLINE_COMPOSITE : {
int wordCount = ListPointer.inlineCompositeWordCount(listPtr);
long tag = ptr;
ptrOffset += 1;
// TODO bounds check
int size = WirePointer.inlineCompositeListElementCount(tag);
int structPtr = WirePointer.structPointer(tag);
int wordsPerElement = StructPointer.wordSize(structPtr);
// TODO check that elemements do not overrun word count
// TODO check whether the size is compatible
return new ListReader(segment, // TODO follow fars
ptrOffset * 8, //
size,
wordsPerElement * 64,
StructPointer.dataSize(structPtr) * 64,
StructPointer.ptrCount(structPtr),
nestingLimit - 1);
}
case FieldSize.VOID : break;
default :
throw new Error("unrecognized element size");
}
throw new Error();
}
public static Text.Reader readTextPointer(SegmentReader segment,
int refOffset) {
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
// XXX should use the default value
return new Text.Reader(java.nio.ByteBuffer.wrap(new byte[0]), 0, 0);
}
int ptrOffset = WirePointer.target(refOffset, ref);
int listPtr = WirePointer.listPointer(ref);
int size = ListPointer.elementCount(listPtr);
if (WirePointer.kind(ref) != WirePointer.LIST) {
throw new DecodeException("Message contains non-list pointer where text was expected.");
}
if (ListPointer.elementSize(listPtr) != FieldSize.BYTE) {
throw new DecodeException("Message contains list pointer of non-bytes where text was expected.");
}
// TODO bounds check?
if (size == 0 || segment.buffer.get(8 * ptrOffset + size - 1) != 0) {
throw new DecodeException("Message contains text that is not NUL-terminated.");
}
return new Text.Reader(segment.buffer, ptrOffset, size - 1);
}
}