capnproto-java-rpc/runtime/src/main/java/org/capnproto/WireHelpers.java

744 lines
33 KiB
Java
Raw Normal View History

2014-10-08 20:20:15 +00:00
// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package org.capnproto;
2014-10-03 16:01:09 +00:00
import java.nio.ByteBuffer;
final class WireHelpers {
2014-10-05 14:01:43 +00:00
static int roundBytesUpToWords(int bytes) {
return (bytes + 7) / 8;
}
2014-10-05 14:01:43 +00:00
static int roundBitsUpToWords(long bits) {
2014-06-14 02:15:36 +00:00
//# This code assumes 64-bit words.
return (int)((bits + 63) / ((long) Constants.BITS_PER_WORD));
}
2014-06-16 18:05:59 +00:00
static class AllocateResult {
public final int ptr;
public final int refOffset;
public final SegmentBuilder segment;
AllocateResult(int ptr, int refOffset, SegmentBuilder segment) {
this.ptr = ptr; this.refOffset = refOffset; this.segment = segment;
}
}
2014-10-05 14:01:43 +00:00
static AllocateResult allocate(int refOffset,
SegmentBuilder segment,
int amount, // in words
byte kind) {
2014-10-15 00:17:07 +00:00
if (amount == 0 && kind == WirePointer.STRUCT) {
WirePointer.setKindAndTargetForEmptyStruct(segment.buffer, refOffset);
return new AllocateResult(refOffset, refOffset, segment);
}
2014-06-15 20:38:26 +00:00
int ptr = segment.allocate(amount);
if (ptr == 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.
2014-06-15 20:38:26 +00:00
int amountPlusRef = amount + Constants.POINTER_SIZE_IN_WORDS;
BuilderArena.AllocateResult allocation = segment.getArena().allocate(amountPlusRef);
2014-06-16 18:49:12 +00:00
//# Set up the original pointer to be a far pointer to
//# the new segment.
FarPointer.set(segment.buffer, refOffset, false, allocation.offset);
FarPointer.setSegmentId(segment.buffer, refOffset, allocation.segment.id);
2014-06-16 18:49:12 +00:00
//# Initialize the landing pad to indicate that the
//# data immediately follows the pad.
int resultRefOffset = allocation.offset;
int ptr1 = allocation.offset + Constants.POINTER_SIZE_IN_WORDS;
WirePointer.setKindAndTarget(allocation.segment.buffer, resultRefOffset, kind,
ptr1);
return new AllocateResult(ptr1, resultRefOffset, allocation.segment);
} else {
2014-06-15 20:38:26 +00:00
WirePointer.setKindAndTarget(segment.buffer, refOffset, kind, ptr);
2014-06-16 18:05:59 +00:00
return new AllocateResult(ptr, refOffset, segment);
}
}
static class FollowBuilderFarsResult {
public final int ptr;
public final long ref;
public final SegmentBuilder segment;
FollowBuilderFarsResult(int ptr, long ref, SegmentBuilder segment) {
this.ptr = ptr; this.ref = ref; this.segment = segment;
}
}
2014-10-05 14:01:43 +00:00
static FollowBuilderFarsResult followBuilderFars(long ref, int refTarget,
SegmentBuilder segment) {
//# If `ref` is a far pointer, follow it. On return, `ref` will
//# have been updated to point at a WirePointer that contains
//# the type information about the target object, and a pointer
//# to the object contents is returned. The caller must NOT use
//# `ref->target()` as this may or may not actually return a
//# valid pointer. `segment` is also updated to point at the
//# segment which actually contains the object.
//#
//# If `ref` is not a far pointer, this simply returns
//# `refTarget`. Usually, `refTarget` should be the same as
//# `ref->target()`, but may not be in cases where `ref` is
//# only a tag.
if (WirePointer.kind(ref) == WirePointer.FAR) {
SegmentBuilder resultSegment = segment.getArena().getSegment(FarPointer.getSegmentId(ref));
int padOffset = FarPointer.positionInSegment(ref);
long pad = WirePointer.get(resultSegment.buffer, padOffset);
if (! FarPointer.isDoubleFar(ref)) {
return new FollowBuilderFarsResult(WirePointer.target(padOffset, pad), pad, resultSegment);
}
2014-06-17 23:41:19 +00:00
//# Landing pad is another far pointer. It is followed by a
//# tag describing the pointed-to object.
throw new Error("unimplemented");
} else {
return new FollowBuilderFarsResult(refTarget, ref, segment);
}
}
2014-06-17 23:41:19 +00:00
static class FollowFarsResult {
public final int ptr;
public final long ref;
public final SegmentReader segment;
FollowFarsResult(int ptr, long ref, SegmentReader segment) {
this.ptr = ptr; this.ref = ref; this.segment = segment;
}
}
2014-10-05 14:01:43 +00:00
static FollowFarsResult followFars(long ref, int refTarget, SegmentReader segment) {
2014-06-17 23:41:19 +00:00
//# If the segment is null, this is an unchecked message,
//# so there are no FAR pointers.
if (segment != null && WirePointer.kind(ref) == WirePointer.FAR) {
2014-06-18 00:35:03 +00:00
SegmentReader resultSegment = segment.arena.tryGetSegment(FarPointer.getSegmentId(ref));
2014-06-18 01:49:42 +00:00
int padOffset = FarPointer.positionInSegment(ref);
long pad = WirePointer.get(resultSegment.buffer, padOffset);
2014-06-18 00:35:03 +00:00
int padWords = FarPointer.isDoubleFar(ref) ? 2 : 1;
// TODO read limiting
if (!FarPointer.isDoubleFar(ref)) {
2014-06-18 01:49:42 +00:00
return new FollowFarsResult(WirePointer.target(padOffset, pad),
2014-06-18 00:35:03 +00:00
pad, resultSegment);
} else {
//# Landing pad is another far pointer. It is
//# followed by a tag describing the pointed-to
//# object.
throw new Error("unimplemented");
}
2014-06-17 23:41:19 +00:00
} else {
return new FollowFarsResult(refTarget, ref, segment);
}
}
2014-10-05 14:01:43 +00:00
static void zeroObject(SegmentBuilder segment, int refOffset) {
2014-09-24 16:18:52 +00:00
//# Zero out the pointed-to object. Use when the pointer is
//# about to be overwritten making the target object no longer
//# reachable.
// TODO
}
2014-10-07 01:43:50 +00:00
static <T> T initStructPointer(StructBuilder.Factory<T> factory,
2014-10-07 01:43:50 +00:00
int refOffset,
SegmentBuilder segment,
StructSize size) {
2014-06-16 18:05:59 +00:00
AllocateResult allocation = allocate(refOffset, segment, size.total(), WirePointer.STRUCT);
StructPointer.setFromStructSize(allocation.segment.buffer, allocation.refOffset, size);
return factory.constructBuilder(allocation.segment, allocation.ptr * Constants.BYTES_PER_WORD,
2014-10-07 01:43:50 +00:00
allocation.ptr + size.data,
size.data * 64, size.pointers, (byte)0);
2014-05-24 14:12:44 +00:00
}
static <T> T getWritableStructPointer(StructBuilder.Factory<T> factory,
2014-10-07 01:43:50 +00:00
int refOffset,
SegmentBuilder segment,
StructSize size,
SegmentReader defaultSegment,
int defaultOffset) {
2014-06-05 22:25:29 +00:00
long ref = WirePointer.get(segment.buffer, refOffset);
int target = WirePointer.target(refOffset, ref);
if (WirePointer.isNull(ref)) {
2014-10-06 18:05:59 +00:00
if (defaultSegment == null) {
2014-10-07 01:43:50 +00:00
return initStructPointer(factory, refOffset, segment, size);
2014-10-06 18:05:59 +00:00
} else {
throw new Error("unimplemented");
}
2014-06-05 22:25:29 +00:00
}
FollowBuilderFarsResult resolved = followBuilderFars(ref, target, segment);
2014-06-05 22:25:29 +00:00
short oldDataSize = StructPointer.dataSize(resolved.ref);
short oldPointerCount = StructPointer.ptrCount(resolved.ref);
int oldPointerSectionOffset = resolved.ptr + oldDataSize;
2014-06-05 22:25:29 +00:00
if (oldDataSize < size.data || oldPointerCount < size.pointers) {
throw new Error("unimplemented");
} else {
return factory.constructBuilder(resolved.segment, resolved.ptr * Constants.BYTES_PER_WORD,
oldPointerSectionOffset, oldDataSize * Constants.BITS_PER_WORD,
oldPointerCount, (byte)0);
2014-06-05 22:25:29 +00:00
}
2014-06-04 01:52:52 +00:00
}
static <T> T initListPointer(ListBuilder.Factory<T> factory,
2014-10-07 20:49:36 +00:00
int refOffset,
SegmentBuilder segment,
int elementCount,
byte elementSize) {
2014-10-11 13:25:31 +00:00
assert elementSize != ElementSize.INLINE_COMPOSITE : "Should have called initStructListPointer instead";
2014-06-14 02:15:36 +00:00
2014-10-11 13:25:31 +00:00
int dataSize = ElementSize.dataBitsPerElement(elementSize);
int pointerCount = ElementSize.pointersPerElement(elementSize);
2014-06-14 02:15:36 +00:00
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
int wordCount = roundBitsUpToWords((long)elementCount * (long)step);
2014-06-16 18:05:59 +00:00
AllocateResult allocation = allocate(refOffset, segment, wordCount, WirePointer.LIST);
2014-06-14 02:15:36 +00:00
2014-06-16 18:05:59 +00:00
ListPointer.set(allocation.segment.buffer, allocation.refOffset, elementSize, elementCount);
2014-06-14 02:15:36 +00:00
2014-10-07 20:49:36 +00:00
return factory.constructBuilder(allocation.segment,
allocation.ptr * Constants.BYTES_PER_WORD,
elementCount, step, dataSize, (short)pointerCount);
}
static <T> T initStructListPointer(ListBuilder.Factory<T> factory,
2014-10-07 20:49:36 +00:00
int refOffset,
SegmentBuilder segment,
int elementCount,
StructSize elementSize) {
2014-10-11 13:25:31 +00:00
if (elementSize.preferredListEncoding != ElementSize.INLINE_COMPOSITE) {
//# Small data-only struct. Allocate a list of primitives instead.
2014-10-07 20:49:36 +00:00
return initListPointer(factory, refOffset, segment, elementCount,
elementSize.preferredListEncoding);
}
int wordsPerElement = elementSize.total();
2014-05-17 20:04:51 +00:00
//# Allocate the list, prefixed by a single WirePointer.
int wordCount = elementCount * wordsPerElement;
2014-06-16 18:05:59 +00:00
AllocateResult allocation = allocate(refOffset, segment, Constants.POINTER_SIZE_IN_WORDS + wordCount,
WirePointer.LIST);
2014-05-17 20:04:51 +00:00
//# Initialize the pointer.
2014-06-16 18:05:59 +00:00
ListPointer.setInlineComposite(allocation.segment.buffer, allocation.refOffset, wordCount);
WirePointer.setKindAndInlineCompositeListElementCount(allocation.segment.buffer, allocation.ptr,
2014-05-17 20:04:51 +00:00
WirePointer.STRUCT, elementCount);
2014-06-16 18:05:59 +00:00
StructPointer.setFromStructSize(allocation.segment.buffer, allocation.ptr, elementSize);
2014-05-17 20:57:13 +00:00
2014-10-07 20:49:36 +00:00
return factory.constructBuilder(allocation.segment,
(allocation.ptr + 1) * Constants.BYTES_PER_WORD,
elementCount, wordsPerElement * Constants.BITS_PER_WORD,
elementSize.data * Constants.BITS_PER_WORD, elementSize.pointers);
}
static <T> T getWritableListPointer(ListBuilder.Factory<T> factory,
2014-10-07 20:49:36 +00:00
int origRefOffset,
SegmentBuilder origSegment,
byte elementSize,
SegmentReader defaultSegment,
int defaultOffset) {
2014-10-11 13:25:31 +00:00
assert elementSize != ElementSize.INLINE_COMPOSITE : "Use getStructList{Element,Field} for structs";
2014-06-14 18:32:54 +00:00
long origRef = WirePointer.get(origSegment.buffer, origRefOffset);
int origRefTarget = WirePointer.target(origRefOffset, origRef);
if (WirePointer.isNull(origRef)) {
throw new Error("unimplemented");
}
//# We must verify that the pointer has the right size. Unlike
//# in getWritableStructListReference(), we never need to
//# "upgrade" the data, because this method is called only for
//# non-struct lists, and there is no allowed upgrade path *to*
//# a non-struct list, only *from* them.
FollowBuilderFarsResult resolved = followBuilderFars(origRef, origRefTarget, origSegment);
2014-06-14 18:32:54 +00:00
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
2014-06-14 18:32:54 +00:00
throw new DecodeException("Called getList{Field,Element}() but existing pointer is not a list");
}
byte oldSize = ListPointer.elementSize(resolved.ref);
2014-06-14 18:32:54 +00:00
2014-10-11 13:25:31 +00:00
if (oldSize == ElementSize.INLINE_COMPOSITE) {
2014-06-14 18:32:54 +00:00
//# The existing element size is InlineComposite, which
//# means that it is at least two words, which makes it
//# bigger than the expected element size. Since fields can
//# only grow when upgraded, the existing data must have
//# been written with a newer version of the protocol. We
//# therefore never need to upgrade the data in this case,
//# but we do need to validate that it is a valid upgrade
//# from what we expected.
throw new Error("unimplemented");
} else {
2014-10-11 13:25:31 +00:00
int dataSize = ElementSize.dataBitsPerElement(oldSize);
int pointerCount = ElementSize.pointersPerElement(oldSize);
2014-06-14 18:32:54 +00:00
2014-10-11 13:25:31 +00:00
if (dataSize < ElementSize.dataBitsPerElement(elementSize)) {
2014-06-14 18:32:54 +00:00
throw new DecodeException("Existing list value is incompatible with expected type.");
}
2014-10-11 13:25:31 +00:00
if (pointerCount < ElementSize.pointersPerElement(elementSize)) {
2014-06-14 18:32:54 +00:00
throw new DecodeException("Existing list value is incompatible with expected type.");
}
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
2014-10-07 20:49:36 +00:00
return factory.constructBuilder(resolved.segment, resolved.ptr * Constants.BYTES_PER_WORD,
ListPointer.elementCount(resolved.ref),
step, dataSize, (short) pointerCount);
2014-06-14 18:32:54 +00:00
}
}
static <T> T getWritableStructListPointer(ListBuilder.Factory<T> factory,
2014-10-07 21:05:37 +00:00
int origRefOffset,
SegmentBuilder origSegment,
StructSize elementSize,
SegmentReader defaultSegment,
int defaultOffset) {
throw new Error("getWritableStructListPointer is unimplemented");
}
// size is in bytes
2014-10-05 14:01:43 +00:00
static Text.Builder initTextPointer(int refOffset,
SegmentBuilder segment,
int size) {
//# The byte list must include a NUL terminator.
int byteSize = size + 1;
2014-05-17 20:57:13 +00:00
//# Allocate the space.
2014-06-16 18:05:59 +00:00
AllocateResult allocation = allocate(refOffset, segment, roundBytesUpToWords(byteSize),
WirePointer.LIST);
2014-05-17 20:57:13 +00:00
//# Initialize the pointer.
2014-10-11 13:25:31 +00:00
ListPointer.set(allocation.segment.buffer, allocation.refOffset, ElementSize.BYTE, byteSize);
2014-05-17 20:57:13 +00:00
2014-06-16 18:05:59 +00:00
return new Text.Builder(allocation.segment.buffer, allocation.ptr * Constants.BYTES_PER_WORD, size);
}
2014-10-05 14:01:43 +00:00
static Text.Builder setTextPointer(int refOffset,
SegmentBuilder segment,
Text.Reader value) {
2014-05-18 03:06:32 +00:00
Text.Builder builder = initTextPointer(refOffset, segment, value.size);
2014-10-07 17:14:07 +00:00
ByteBuffer slice = value.buffer.duplicate();
slice.position(value.offset);
slice.limit(value.offset + value.size);
builder.buffer.position(builder.offset);
builder.buffer.put(slice);
2014-05-18 03:06:32 +00:00
return builder;
}
2014-10-05 14:01:43 +00:00
static Text.Builder getWritableTextPointer(int refOffset,
SegmentBuilder segment,
ByteBuffer defaultBuffer,
int defaultOffset,
int defaultSize) {
2014-06-13 20:14:16 +00:00
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
2014-10-03 16:01:09 +00:00
if (defaultBuffer == null) {
return new Text.Builder(null, 0, 0);
} else {
Text.Builder builder = initTextPointer(refOffset, segment, defaultSize);
// TODO is there a way to do this with bulk methods?
for (int i = 0; i < builder.size; ++i) {
2014-10-06 18:05:59 +00:00
builder.buffer.put(builder.offset + i, defaultBuffer.get(defaultOffset * 8 + i));
2014-10-03 16:01:09 +00:00
}
return builder;
}
2014-06-13 20:14:16 +00:00
}
int refTarget = WirePointer.target(refOffset, ref);
FollowBuilderFarsResult resolved = followBuilderFars(ref, refTarget, segment);
2014-06-13 20:14:16 +00:00
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
2014-06-13 20:14:16 +00:00
throw new DecodeException("Called getText{Field,Element} but existing pointer is not a list.");
}
2014-10-11 13:25:31 +00:00
if (ListPointer.elementSize(resolved.ref) != ElementSize.BYTE) {
2014-06-13 20:14:16 +00:00
throw new DecodeException(
"Called getText{Field,Element} but existing list pointer is not byte-sized.");
}
//# Subtract 1 from the size for the NUL terminator.
return new Text.Builder(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
ListPointer.elementCount(resolved.ref) - 1);
2014-06-13 20:14:16 +00:00
}
2014-10-03 17:24:51 +00:00
// size is in bytes
2014-10-05 14:01:43 +00:00
static Data.Builder initDataPointer(int refOffset,
SegmentBuilder segment,
int size) {
2014-10-03 17:24:51 +00:00
//# Allocate the space.
AllocateResult allocation = allocate(refOffset, segment, roundBytesUpToWords(size),
WirePointer.LIST);
//# Initialize the pointer.
2014-10-11 13:25:31 +00:00
ListPointer.set(allocation.segment.buffer, allocation.refOffset, ElementSize.BYTE, size);
2014-10-03 17:24:51 +00:00
return new Data.Builder(allocation.segment.buffer, allocation.ptr * Constants.BYTES_PER_WORD, size);
}
2014-10-05 14:01:43 +00:00
static Data.Builder setDataPointer(int refOffset,
SegmentBuilder segment,
Data.Reader value) {
2014-10-03 17:24:51 +00:00
Data.Builder builder = initDataPointer(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;
}
2014-10-05 14:01:43 +00:00
static Data.Builder getWritableDataPointer(int refOffset,
SegmentBuilder segment,
ByteBuffer defaultBuffer,
int defaultOffset,
int defaultSize) {
2014-10-03 17:24:51 +00:00
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
if (defaultBuffer == null) {
return new Data.Builder(ByteBuffer.allocate(0), 0, 0);
} else {
Data.Builder builder = initDataPointer(refOffset, segment, defaultSize);
// TODO is there a way to do this with bulk methods?
for (int i = 0; i < builder.size; ++i) {
2014-10-06 18:05:59 +00:00
builder.buffer.put(builder.offset + i, defaultBuffer.get(defaultOffset * 8 + i));
2014-10-03 17:24:51 +00:00
}
return builder;
}
}
int refTarget = WirePointer.target(refOffset, ref);
FollowBuilderFarsResult resolved = followBuilderFars(ref, refTarget, segment);
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
throw new DecodeException("Called getData{Field,Element} but existing pointer is not a list.");
}
2014-10-11 13:25:31 +00:00
if (ListPointer.elementSize(resolved.ref) != ElementSize.BYTE) {
2014-10-03 17:24:51 +00:00
throw new DecodeException(
"Called getData{Field,Element} but existing list pointer is not byte-sized.");
}
return new Data.Builder(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
ListPointer.elementCount(resolved.ref));
}
static <T> T readStructPointer(StructReader.Factory<T> factory,
2014-10-07 01:43:50 +00:00
SegmentReader segment,
int refOffset,
SegmentReader defaultSegment,
int defaultOffset,
int nestingLimit) {
2014-10-06 18:05:59 +00:00
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
if (defaultSegment == null) {
return factory.constructReader(SegmentReader.EMPTY, 0, 0, 0, (short) 0, (byte) 0, 0x7fffffff);
2014-10-06 18:05:59 +00:00
} else {
2014-10-07 21:05:37 +00:00
segment = defaultSegment;
refOffset = defaultOffset;
ref = WirePointer.get(segment.buffer, refOffset);
2014-10-06 18:05:59 +00:00
}
}
2014-06-17 23:41:19 +00:00
if (nestingLimit <= 0) {
throw new DecodeException("Message is too deeply nested or contains cycles.");
}
2014-10-06 18:05:59 +00:00
2014-06-17 23:41:19 +00:00
int refTarget = WirePointer.target(refOffset, ref);
FollowFarsResult resolved = followFars(ref, refTarget, segment);
int dataSizeWords = StructPointer.dataSize(resolved.ref);
if (WirePointer.kind(resolved.ref) != WirePointer.STRUCT) {
throw new DecodeException("Message contains non-struct pointer where struct pointer was expected.");
}
2014-10-08 19:16:17 +00:00
resolved.segment.arena.checkReadLimit(StructPointer.wordSize(resolved.ref));
2014-06-17 23:41:19 +00:00
return factory.constructReader(resolved.segment,
2014-10-07 01:43:50 +00:00
resolved.ptr * Constants.BYTES_PER_WORD,
(resolved.ptr + dataSizeWords),
dataSizeWords * Constants.BITS_PER_WORD,
StructPointer.ptrCount(resolved.ref),
(byte)0,
nestingLimit - 1);
}
2014-10-05 14:01:43 +00:00
static SegmentBuilder setStructPointer(SegmentBuilder segment, int refOffset, StructReader value) {
throw new Error("setStructPointer is unimplemented");
};
2014-10-07 00:04:01 +00:00
static SegmentBuilder setListPointer(SegmentBuilder segment, int refOffset, ListReader value) {
int totalSize = roundBitsUpToWords(value.elementCount * value.step);
if (value.step <= Constants.BITS_PER_WORD) {
//# List of non-structs.
AllocateResult allocation = allocate(refOffset, segment, totalSize, WirePointer.LIST);
if (value.structPointerCount == 1) {
//# List of pointers.
2014-10-11 13:25:31 +00:00
ListPointer.set(allocation.segment.buffer, allocation.refOffset, ElementSize.POINTER, value.elementCount);
2014-10-07 00:04:01 +00:00
for (int i = 0; i < value.elementCount; ++i) {
//copyPointer(segment);
}
} else {
//# List of data.
}
} else {
//# List of structs.
}
throw new Error("setListPointer is unimplemented");
}
static SegmentBuilder copyPointer(SegmentBuilder dstSegment, int dstOffset,
SegmentReader srcSegment, int srcOffset, int nestingLimit) {
// Deep-copy the object pointed to by src into dst. It turns out we can't reuse
// readStructPointer(), etc. because they do type checking whereas here we want to accept any
// valid pointer.
2014-10-11 13:18:41 +00:00
long srcRef = WirePointer.get(srcSegment.buffer, srcOffset);
if (WirePointer.isNull(srcRef)) {
dstSegment.buffer.putLong(dstOffset * 8, 0L);
return dstSegment;
}
int srcTarget = WirePointer.target(srcOffset, srcRef);
FollowFarsResult resolved = followFars(srcRef, srcTarget, srcSegment);
switch (WirePointer.kind(resolved.ref)) {
case WirePointer.STRUCT :
if (nestingLimit <= 0) {
throw new DecodeException("Message is too deeply nested or contains cycles. See org.capnproto.ReaderOptions.");
}
resolved.segment.arena.checkReadLimit(StructPointer.wordSize(resolved.ref));
// TODO
//return setStructPointer(dstSegment, dstOffset, ...);
case WirePointer.LIST :
byte elementSize = ListPointer.elementSize(resolved.ref);
if (nestingLimit <= 0) {
throw new DecodeException("Message is too deeply nested or contains cycles. See org.capnproto.ReaderOptions.");
}
2014-10-11 13:25:31 +00:00
if (elementSize == ElementSize.INLINE_COMPOSITE) {
2014-10-11 13:18:41 +00:00
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
//long tag =
} else {
}
case WirePointer.FAR :
case WirePointer.OTHER :
}
2014-10-07 00:04:01 +00:00
throw new Error("copyPointer is unimplemented");
}
static <T> T readListPointer(ListReader.Factory<T> factory,
2014-10-07 20:49:36 +00:00
SegmentReader segment,
int refOffset,
SegmentReader defaultSegment,
int defaultOffset,
byte expectedElementSize,
int nestingLimit) {
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
2014-10-06 18:54:15 +00:00
if (defaultSegment == null) {
2014-10-07 20:49:36 +00:00
factory.constructReader(SegmentReader.EMPTY, 0, 0, 0, 0, (short) 0, 0x7fffffff);
2014-10-06 18:54:15 +00:00
} else {
segment = defaultSegment;
refOffset = defaultOffset;
ref = WirePointer.get(segment.buffer, refOffset);
}
}
2014-06-17 23:41:19 +00:00
if (nestingLimit <= 0) {
throw new Error("nesting limit exceeded");
}
2014-06-17 23:41:19 +00:00
int refTarget = WirePointer.target(refOffset, ref);
2014-06-17 23:41:19 +00:00
FollowFarsResult resolved = followFars(ref, refTarget, segment);
switch (ListPointer.elementSize(resolved.ref)) {
2014-10-11 13:25:31 +00:00
case ElementSize.INLINE_COMPOSITE : {
2014-06-17 23:41:19 +00:00
int wordCount = ListPointer.inlineCompositeWordCount(resolved.ref);
2014-06-17 23:41:19 +00:00
long tag = WirePointer.get(resolved.segment.buffer, resolved.ptr);
int ptr = resolved.ptr + 1;
2014-10-08 19:16:17 +00:00
resolved.segment.arena.checkReadLimit(wordCount + 1);
int size = WirePointer.inlineCompositeListElementCount(tag);
int wordsPerElement = StructPointer.wordSize(tag);
// TODO check that elemements do not overrun word count
// TODO check whether the size is compatible
2014-10-07 20:49:36 +00:00
return factory.constructReader(resolved.segment,
ptr * Constants.BYTES_PER_WORD,
size,
wordsPerElement * Constants.BITS_PER_WORD,
StructPointer.dataSize(tag) * Constants.BITS_PER_WORD,
StructPointer.ptrCount(tag),
nestingLimit - 1);
}
2014-06-18 01:30:30 +00:00
default : {
//# This is a primitive or pointer list, but all such
//# lists can also be interpreted as struct lists. We
//# need to compute the data size and pointer count for
//# such structs.
2014-10-11 13:25:31 +00:00
int dataSize = ElementSize.dataBitsPerElement(ListPointer.elementSize(resolved.ref));
int pointerCount = ElementSize.pointersPerElement(ListPointer.elementSize(resolved.ref));
2014-06-18 01:30:30 +00:00
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
2014-10-08 19:16:17 +00:00
resolved.segment.arena.checkReadLimit(
roundBitsUpToWords(ListPointer.elementCount(resolved.ref) * step));
2014-06-18 01:30:30 +00:00
//# Verify that the elements are at least as large as
//# the expected type. Note that if we expected
//# InlineComposite, the expected sizes here will be
//# zero, because bounds checking will be performed at
//# field access time. So this check here is for the
//# case where we expected a list of some primitive or
//# pointer type.
2014-10-11 13:25:31 +00:00
int expectedDataBitsPerElement = ElementSize.dataBitsPerElement(expectedElementSize);
int expectedPointersPerElement = ElementSize.pointersPerElement(expectedElementSize);
2014-06-18 01:30:30 +00:00
if (expectedDataBitsPerElement > dataSize) {
throw new DecodeException("Message contains list with incompatible element type.");
}
2014-06-18 01:30:30 +00:00
if (expectedPointersPerElement > pointerCount) {
throw new DecodeException("Message contains list with incompatible element type.");
}
2014-10-07 20:49:36 +00:00
return factory.constructReader(resolved.segment,
resolved.ptr * Constants.BYTES_PER_WORD,
ListPointer.elementCount(resolved.ref),
step,
dataSize,
(short)pointerCount,
nestingLimit - 1);
2014-06-18 01:30:30 +00:00
}
}
}
2014-10-05 14:01:43 +00:00
static Text.Reader readTextPointer(SegmentReader segment,
int refOffset,
ByteBuffer defaultBuffer,
int defaultOffset,
int defaultSize) {
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
2014-10-03 16:01:09 +00:00
if (defaultBuffer == null) {
// XXX -- what about null terminator?
return new Text.Reader(ByteBuffer.wrap(new byte[0]), 0, 0);
} else {
return new Text.Reader(defaultBuffer, defaultOffset, defaultSize);
}
}
2014-06-17 23:41:19 +00:00
int refTarget = WirePointer.target(refOffset, ref);
FollowFarsResult resolved = followFars(ref, refTarget, segment);
2014-06-17 23:41:19 +00:00
int size = ListPointer.elementCount(resolved.ref);
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
throw new DecodeException("Message contains non-list pointer where text was expected.");
}
2014-10-11 13:25:31 +00:00
if (ListPointer.elementSize(resolved.ref) != ElementSize.BYTE) {
throw new DecodeException("Message contains list pointer of non-bytes where text was expected.");
}
2014-10-08 19:16:17 +00:00
resolved.segment.arena.checkReadLimit(roundBytesUpToWords(size));
2014-06-17 23:41:19 +00:00
if (size == 0 || resolved.segment.buffer.get(8 * resolved.ptr + size - 1) != 0) {
throw new DecodeException("Message contains text that is not NUL-terminated.");
}
2014-06-17 23:41:19 +00:00
return new Text.Reader(resolved.segment.buffer, resolved.ptr, size - 1);
}
2014-10-03 17:24:51 +00:00
2014-10-05 14:01:43 +00:00
static Data.Reader readDataPointer(SegmentReader segment,
int refOffset,
ByteBuffer defaultBuffer,
int defaultOffset,
int defaultSize) {
2014-10-03 17:24:51 +00:00
long ref = WirePointer.get(segment.buffer, refOffset);
if (WirePointer.isNull(ref)) {
if (defaultBuffer == null) {
return new Data.Reader(ByteBuffer.wrap(new byte[0]), 0, 0);
} else {
return new Data.Reader(defaultBuffer, defaultOffset, defaultSize);
}
}
int refTarget = WirePointer.target(refOffset, ref);
FollowFarsResult resolved = followFars(ref, refTarget, segment);
int size = ListPointer.elementCount(resolved.ref);
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
throw new DecodeException("Message contains non-list pointer where data was expected.");
}
2014-10-11 13:25:31 +00:00
if (ListPointer.elementSize(resolved.ref) != ElementSize.BYTE) {
2014-10-03 17:24:51 +00:00
throw new DecodeException("Message contains list pointer of non-bytes where data was expected.");
}
2014-10-08 19:16:17 +00:00
resolved.segment.arena.checkReadLimit(roundBytesUpToWords(size));
2014-10-03 17:24:51 +00:00
return new Data.Reader(resolved.segment.buffer, resolved.ptr, size);
}
}