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.
|
|
|
|
|
2014-05-15 23:05:20 +00:00
|
|
|
package org.capnproto;
|
|
|
|
|
2014-10-03 16:01:09 +00:00
|
|
|
import java.nio.ByteBuffer;
|
|
|
|
|
2014-05-15 23:05:20 +00:00
|
|
|
final class WireHelpers {
|
|
|
|
|
2014-10-05 14:01:43 +00:00
|
|
|
static int roundBytesUpToWords(int bytes) {
|
2014-05-15 23:05:20 +00:00
|
|
|
return (bytes + 7) / 8;
|
|
|
|
}
|
|
|
|
|
2014-11-17 00:06:35 +00:00
|
|
|
static int roundBitsUpToBytes(int bits) {
|
|
|
|
return (bits + 7) / Constants.BITS_PER_BYTE;
|
|
|
|
}
|
|
|
|
|
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,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
int amount, // in words
|
|
|
|
byte kind) {
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2014-11-09 21:32:51 +00:00
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
if (!WirePointer.isNull(ref)) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, refOffset);
|
2014-11-09 21:32:51 +00:00
|
|
|
}
|
|
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
|
2014-06-15 20:38:26 +00:00
|
|
|
int ptr = segment.allocate(amount);
|
|
|
|
if (ptr == SegmentBuilder.FAILED_ALLOCATION) {
|
2014-05-15 23:05:20 +00:00
|
|
|
//# 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.
|
2014-06-17 21:56:33 +00:00
|
|
|
FarPointer.set(segment.buffer, refOffset, false, allocation.offset);
|
2014-06-16 23:46:33 +00:00
|
|
|
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);
|
2014-05-15 23:05:20 +00:00
|
|
|
} 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);
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-16 23:46:33 +00:00
|
|
|
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) {
|
2014-06-16 23:46:33 +00:00
|
|
|
//# 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);
|
2014-10-26 22:22:48 +00:00
|
|
|
long pad = resultSegment.get(padOffset);
|
2014-06-16 23:46:33 +00:00
|
|
|
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.
|
2014-11-18 13:49:02 +00:00
|
|
|
int refOffset = padOffset + 1;
|
|
|
|
ref = resultSegment.get(refOffset);
|
2014-06-16 23:46:33 +00:00
|
|
|
|
2014-11-18 13:49:02 +00:00
|
|
|
resultSegment = resultSegment.getArena().getSegment(FarPointer.getSegmentId(pad));
|
|
|
|
return new FollowBuilderFarsResult(FarPointer.positionInSegment(pad), ref, resultSegment);
|
2014-06-16 23:46:33 +00:00
|
|
|
} 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);
|
2014-10-26 22:22:48 +00:00
|
|
|
long pad = resultSegment.get(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.
|
2014-06-17 23:41:19 +00:00
|
|
|
|
2014-11-16 19:14:15 +00:00
|
|
|
long tag = resultSegment.get(padOffset + 1);
|
|
|
|
resultSegment = resultSegment.arena.tryGetSegment(FarPointer.getSegmentId(pad));
|
|
|
|
return new FollowFarsResult(FarPointer.positionInSegment(pad), tag, resultSegment);
|
|
|
|
}
|
2014-06-17 23:41:19 +00:00
|
|
|
} else {
|
|
|
|
return new FollowFarsResult(refTarget, ref, segment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
static void zeroObject(SegmentBuilder segment, CapTableBuilder capTable, 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.
|
|
|
|
|
2014-11-05 02:53:34 +00:00
|
|
|
//# We shouldn't zero out external data linked into the message.
|
|
|
|
if (!segment.isWritable()) return;
|
|
|
|
|
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
|
|
|
|
switch (WirePointer.kind(ref)) {
|
|
|
|
case WirePointer.STRUCT:
|
|
|
|
case WirePointer.LIST:
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, ref, WirePointer.target(refOffset, ref));
|
2014-11-05 02:53:34 +00:00
|
|
|
break;
|
|
|
|
case WirePointer.FAR: {
|
|
|
|
segment = segment.getArena().getSegment(FarPointer.getSegmentId(ref));
|
2014-11-09 18:36:24 +00:00
|
|
|
if (segment.isWritable()) { //# Don't zero external data.
|
|
|
|
int padOffset = FarPointer.positionInSegment(ref);
|
|
|
|
long pad = segment.get(padOffset);
|
|
|
|
if (FarPointer.isDoubleFar(ref)) {
|
|
|
|
SegmentBuilder otherSegment = segment.getArena().getSegment(FarPointer.getSegmentId(ref));
|
|
|
|
if (otherSegment.isWritable()) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(otherSegment, capTable, padOffset + 1, FarPointer.positionInSegment(pad));
|
2014-11-09 18:36:24 +00:00
|
|
|
}
|
|
|
|
segment.buffer.putLong(padOffset * 8, 0L);
|
|
|
|
segment.buffer.putLong((padOffset + 1) * 8, 0L);
|
|
|
|
|
|
|
|
} else {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, padOffset);
|
2014-11-09 18:36:24 +00:00
|
|
|
segment.buffer.putLong(padOffset * 8, 0L);
|
|
|
|
}
|
2014-11-05 02:53:34 +00:00
|
|
|
}
|
2014-11-09 18:36:24 +00:00
|
|
|
|
2014-11-05 02:53:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WirePointer.OTHER: {
|
2020-10-02 20:47:25 +00:00
|
|
|
assert WirePointer.isCapability(ref) : "Unknown pointer type";
|
|
|
|
if (WirePointer.isCapability(ref)) {
|
|
|
|
var capIndex = WirePointer.upper32Bits(ref);
|
|
|
|
assert capTable != null: "Cannot zero out capability pointer with no capTable";
|
|
|
|
if (capTable != null) {
|
|
|
|
capTable.dropCap(capIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2014-11-05 02:53:34 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-24 16:18:52 +00:00
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
static void zeroObject(SegmentBuilder segment, CapTableBuilder capTable, long tag, int ptr) {
|
2014-11-05 02:53:34 +00:00
|
|
|
//# We shouldn't zero out external data linked into the message.
|
|
|
|
if (!segment.isWritable()) return;
|
|
|
|
|
|
|
|
switch (WirePointer.kind(tag)) {
|
|
|
|
case WirePointer.STRUCT: {
|
|
|
|
int pointerSection = ptr + StructPointer.dataSize(tag);
|
|
|
|
int count = StructPointer.ptrCount(tag);
|
|
|
|
for (int ii = 0; ii < count; ++ii) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, pointerSection + ii);
|
2014-11-05 02:53:34 +00:00
|
|
|
}
|
|
|
|
memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0,
|
|
|
|
StructPointer.wordSize(tag) * Constants.BYTES_PER_WORD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WirePointer.LIST: {
|
2014-11-09 18:36:24 +00:00
|
|
|
switch (ListPointer.elementSize(tag)) {
|
|
|
|
case ElementSize.VOID: break;
|
|
|
|
case ElementSize.BIT:
|
|
|
|
case ElementSize.BYTE:
|
|
|
|
case ElementSize.TWO_BYTES:
|
|
|
|
case ElementSize.FOUR_BYTES:
|
|
|
|
case ElementSize.EIGHT_BYTES: {
|
|
|
|
memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0,
|
|
|
|
roundBitsUpToWords(
|
|
|
|
ListPointer.elementCount(tag) *
|
|
|
|
ElementSize.dataBitsPerElement(ListPointer.elementSize(tag))) * Constants.BYTES_PER_WORD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementSize.POINTER: {
|
|
|
|
int count = ListPointer.elementCount(tag);
|
|
|
|
for (int ii = 0; ii < count; ++ii) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, ptr + ii);
|
2014-11-09 18:36:24 +00:00
|
|
|
}
|
|
|
|
memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0,
|
|
|
|
count * Constants.BYTES_PER_WORD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ElementSize.INLINE_COMPOSITE: {
|
|
|
|
long elementTag = segment.get(ptr);
|
|
|
|
if (WirePointer.kind(elementTag) != WirePointer.STRUCT) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("Don't know how to handle non-STRUCT inline composite.");
|
2014-11-09 18:36:24 +00:00
|
|
|
}
|
|
|
|
int dataSize = StructPointer.dataSize(elementTag);
|
|
|
|
int pointerCount = StructPointer.ptrCount(elementTag);
|
|
|
|
|
|
|
|
int pos = ptr + Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
int count = WirePointer.inlineCompositeListElementCount(elementTag);
|
|
|
|
for (int ii = 0; ii < count; ++ii) {
|
|
|
|
pos += dataSize;
|
|
|
|
for (int jj = 0; jj < pointerCount; ++jj) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, pos);
|
2014-11-09 18:36:24 +00:00
|
|
|
pos += Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(segment.buffer, ptr * Constants.BYTES_PER_WORD, (byte)0,
|
|
|
|
(StructPointer.wordSize(elementTag) * count + Constants.POINTER_SIZE_IN_WORDS) * Constants.BYTES_PER_WORD);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2014-11-05 02:53:34 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case WirePointer.FAR:
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new IllegalArgumentException("Unexpected FAR pointer.");
|
2014-11-05 02:53:34 +00:00
|
|
|
case WirePointer.OTHER:
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new IllegalArgumentException("Unexpected OTHER pointer.");
|
2014-11-05 02:53:34 +00:00
|
|
|
}
|
|
|
|
}
|
2014-10-07 01:43:50 +00:00
|
|
|
|
2014-11-17 00:06:35 +00:00
|
|
|
static void zeroPointerAndFars(SegmentBuilder segment, int refOffset) {
|
|
|
|
//# Zero out the pointer itself and, if it is a far pointer, zero the landing pad as well,
|
|
|
|
//# but do not zero the object body. Used when upgrading.
|
|
|
|
|
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
if (WirePointer.kind(ref) == WirePointer.FAR) {
|
|
|
|
SegmentBuilder padSegment = segment.getArena().getSegment(FarPointer.getSegmentId(ref));
|
|
|
|
if (padSegment.isWritable()) { //# Don't zero external data.
|
|
|
|
int padOffset = FarPointer.positionInSegment(ref);
|
|
|
|
padSegment.buffer.putLong(padOffset * Constants.BYTES_PER_WORD, 0L);
|
|
|
|
if (FarPointer.isDoubleFar(ref)) {
|
|
|
|
padSegment.buffer.putLong(padOffset * Constants.BYTES_PER_WORD + 1, 0L);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-17 02:17:56 +00:00
|
|
|
segment.put(refOffset, 0L);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void transferPointer(SegmentBuilder dstSegment, int dstOffset,
|
|
|
|
SegmentBuilder srcSegment, int srcOffset) {
|
|
|
|
//# Make *dst point to the same object as *src. Both must reside in the same message, but can
|
|
|
|
//# be in different segments.
|
|
|
|
//#
|
|
|
|
//# Caller MUST zero out the source pointer after calling this, to make sure no later code
|
|
|
|
//# mistakenly thinks the source location still owns the object. transferPointer() doesn't do
|
|
|
|
//# this zeroing itself because many callers transfer several pointers in a loop then zero out
|
|
|
|
//# the whole section.
|
|
|
|
|
|
|
|
long src = srcSegment.get(srcOffset);
|
|
|
|
if (WirePointer.isNull(src)) {
|
|
|
|
dstSegment.put(dstOffset, 0L);
|
|
|
|
} else if (WirePointer.kind(src) == WirePointer.FAR) {
|
|
|
|
//# Far pointers are position-independent, so we can just copy.
|
|
|
|
dstSegment.put(dstOffset, srcSegment.get(srcOffset));
|
|
|
|
} else {
|
|
|
|
transferPointer(dstSegment, dstOffset, srcSegment, srcOffset,
|
|
|
|
WirePointer.target(srcOffset, src));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void transferPointer(SegmentBuilder dstSegment, int dstOffset,
|
|
|
|
SegmentBuilder srcSegment, int srcOffset, int srcTargetOffset) {
|
|
|
|
//# Like the other overload, but splits src into a tag and a target. Particularly useful for
|
|
|
|
//# OrphanBuilder.
|
|
|
|
|
|
|
|
long src = srcSegment.get(srcOffset);
|
|
|
|
long srcTarget = srcSegment.get(srcTargetOffset);
|
|
|
|
|
|
|
|
if (dstSegment == srcSegment) {
|
|
|
|
//# Same segment, so create a direct pointer.
|
|
|
|
|
2015-12-17 02:55:03 +00:00
|
|
|
if (WirePointer.kind(src) == WirePointer.STRUCT && StructPointer.wordSize(src) == 0) {
|
|
|
|
WirePointer.setKindAndTargetForEmptyStruct(dstSegment.buffer, dstOffset);
|
|
|
|
} else {
|
|
|
|
WirePointer.setKindAndTarget(dstSegment.buffer, dstOffset,
|
|
|
|
WirePointer.kind(src), srcTargetOffset);
|
|
|
|
}
|
2016-01-13 03:25:35 +00:00
|
|
|
// We can just copy the upper 32 bits.
|
|
|
|
dstSegment.buffer.putInt(dstOffset * Constants.BYTES_PER_WORD + 4,
|
|
|
|
srcSegment.buffer.getInt(srcOffset * Constants.BYTES_PER_WORD + 4));
|
|
|
|
|
2014-11-17 02:17:56 +00:00
|
|
|
} else {
|
|
|
|
//# Need to create a far pointer. Try to allocate it in the same segment as the source,
|
|
|
|
//# so that it doesn't need to be a double-far.
|
|
|
|
|
2014-11-17 23:42:44 +00:00
|
|
|
int landingPadOffset = srcSegment.allocate(1);
|
|
|
|
if (landingPadOffset == SegmentBuilder.FAILED_ALLOCATION) {
|
|
|
|
//# Darn, need a double-far.
|
|
|
|
|
|
|
|
BuilderArena.AllocateResult allocation = srcSegment.getArena().allocate(2);
|
|
|
|
SegmentBuilder farSegment = allocation.segment;
|
|
|
|
landingPadOffset = allocation.offset;
|
|
|
|
|
|
|
|
FarPointer.set(farSegment.buffer, landingPadOffset, false, srcTargetOffset);
|
|
|
|
FarPointer.setSegmentId(farSegment.buffer, landingPadOffset, srcSegment.id);
|
|
|
|
|
|
|
|
WirePointer.setKindWithZeroOffset(farSegment.buffer, landingPadOffset + 1,
|
2017-02-27 23:42:38 +00:00
|
|
|
WirePointer.kind(src));
|
2014-11-17 23:42:44 +00:00
|
|
|
|
|
|
|
farSegment.buffer.putInt((landingPadOffset + 1) * Constants.BYTES_PER_WORD + 4,
|
|
|
|
srcSegment.buffer.getInt(srcOffset * Constants.BYTES_PER_WORD + 4));
|
|
|
|
|
|
|
|
FarPointer.set(dstSegment.buffer, dstOffset,
|
|
|
|
true, landingPadOffset);
|
|
|
|
FarPointer.setSegmentId(dstSegment.buffer, dstOffset,
|
|
|
|
farSegment.id);
|
|
|
|
} else {
|
|
|
|
//# Simple landing pad is just a pointer.
|
|
|
|
WirePointer.setKindAndTarget(srcSegment.buffer, landingPadOffset,
|
|
|
|
WirePointer.kind(srcTarget), srcTargetOffset);
|
|
|
|
srcSegment.buffer.putInt(landingPadOffset * Constants.BYTES_PER_WORD + 4,
|
|
|
|
srcSegment.buffer.getInt(srcOffset * Constants.BYTES_PER_WORD + 4));
|
|
|
|
|
|
|
|
FarPointer.set(dstSegment.buffer, dstOffset,
|
|
|
|
false, landingPadOffset);
|
|
|
|
FarPointer.setSegmentId(dstSegment.buffer, dstOffset,
|
|
|
|
srcSegment.id);
|
|
|
|
}
|
2014-11-17 02:17:56 +00:00
|
|
|
}
|
|
|
|
|
2014-11-17 00:06:35 +00:00
|
|
|
}
|
|
|
|
|
2014-10-07 21:47:42 +00:00
|
|
|
static <T> T initStructPointer(StructBuilder.Factory<T> factory,
|
2014-10-07 01:43:50 +00:00
|
|
|
int refOffset,
|
|
|
|
SegmentBuilder segment,
|
|
|
|
StructSize size) {
|
2020-10-02 20:47:25 +00:00
|
|
|
return initStructPointer(factory, refOffset, segment, null, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static <T> T initStructPointer(StructBuilder.Factory<T> factory,
|
|
|
|
int refOffset,
|
|
|
|
SegmentBuilder segment,
|
|
|
|
CapTableBuilder capTable,
|
|
|
|
StructSize size) {
|
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, size.total(), WirePointer.STRUCT);
|
2014-06-16 18:05:59 +00:00
|
|
|
StructPointer.setFromStructSize(allocation.segment.buffer, allocation.refOffset, size);
|
2020-10-02 20:47:25 +00:00
|
|
|
return factory.constructBuilder(allocation.segment, capTable, allocation.ptr * Constants.BYTES_PER_WORD,
|
2014-10-07 01:43:50 +00:00
|
|
|
allocation.ptr + size.data,
|
2014-10-26 14:13:12 +00:00
|
|
|
size.data * 64, size.pointers);
|
2014-05-24 14:12:44 +00:00
|
|
|
}
|
|
|
|
|
2014-10-07 21:47:42 +00:00
|
|
|
static <T> T getWritableStructPointer(StructBuilder.Factory<T> factory,
|
2014-10-07 01:43:50 +00:00
|
|
|
int refOffset,
|
|
|
|
SegmentBuilder segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-07 01:43:50 +00:00
|
|
|
StructSize size,
|
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-06-05 22:25:29 +00:00
|
|
|
int target = WirePointer.target(refOffset, ref);
|
|
|
|
if (WirePointer.isNull(ref)) {
|
2014-10-06 18:05:59 +00:00
|
|
|
if (defaultSegment == null) {
|
2020-10-02 20:47:25 +00:00
|
|
|
return initStructPointer(factory, refOffset, segment, capTable, size);
|
2014-10-06 18:05:59 +00:00
|
|
|
} else {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("unimplemented");
|
2014-10-06 18:05:59 +00:00
|
|
|
}
|
2014-06-05 22:25:29 +00:00
|
|
|
}
|
2014-06-16 23:46:33 +00:00
|
|
|
FollowBuilderFarsResult resolved = followBuilderFars(ref, target, segment);
|
2014-06-05 22:25:29 +00:00
|
|
|
|
2014-06-17 00:00:01 +00:00
|
|
|
short oldDataSize = StructPointer.dataSize(resolved.ref);
|
|
|
|
short oldPointerCount = StructPointer.ptrCount(resolved.ref);
|
2014-11-17 22:39:16 +00:00
|
|
|
int oldPointerSection = resolved.ptr + oldDataSize;
|
2014-06-05 22:25:29 +00:00
|
|
|
|
|
|
|
if (oldDataSize < size.data || oldPointerCount < size.pointers) {
|
2014-11-17 22:39:16 +00:00
|
|
|
//# The space allocated for this struct is too small. Unlike with readers, we can't just
|
|
|
|
//# run with it and do bounds checks at access time, because how would we handle writes?
|
|
|
|
//# Instead, we have to copy the struct to a new space now.
|
|
|
|
|
|
|
|
short newDataSize = (short)Math.max(oldDataSize, size.data);
|
|
|
|
short newPointerCount = (short)Math.max(oldPointerCount, size.pointers);
|
|
|
|
int totalSize = newDataSize + newPointerCount * Constants.WORDS_PER_POINTER;
|
|
|
|
|
|
|
|
//# Don't let allocate() zero out the object just yet.
|
|
|
|
zeroPointerAndFars(segment, refOffset);
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable,
|
2014-11-17 22:39:16 +00:00
|
|
|
totalSize, WirePointer.STRUCT);
|
|
|
|
|
|
|
|
StructPointer.set(allocation.segment.buffer, allocation.refOffset,
|
|
|
|
newDataSize, newPointerCount);
|
|
|
|
|
|
|
|
//# Copy data section.
|
|
|
|
memcpy(allocation.segment.buffer, allocation.ptr * Constants.BYTES_PER_WORD,
|
|
|
|
resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
|
|
|
|
oldDataSize * Constants.BYTES_PER_WORD);
|
|
|
|
|
|
|
|
//# Copy pointer section.
|
|
|
|
int newPointerSection = allocation.ptr + newDataSize;
|
|
|
|
for (int ii = 0; ii < oldPointerCount; ++ii) {
|
|
|
|
transferPointer(allocation.segment, newPointerSection + ii,
|
|
|
|
resolved.segment, oldPointerSection + ii);
|
|
|
|
}
|
|
|
|
|
|
|
|
//# Zero out old location. This has two purposes:
|
|
|
|
//# 1) We don't want to leak the original contents of the struct when the message is written
|
|
|
|
//# out as it may contain secrets that the caller intends to remove from the new copy.
|
|
|
|
//# 2) Zeros will be deflated by packing, making this dead memory almost-free if it ever
|
|
|
|
//# hits the wire.
|
|
|
|
memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD, (byte)0,
|
|
|
|
(oldDataSize + oldPointerCount * Constants.WORDS_PER_POINTER) * Constants.BYTES_PER_WORD);
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
return factory.constructBuilder(allocation.segment, capTable, allocation.ptr * Constants.BYTES_PER_WORD,
|
2014-11-17 22:39:16 +00:00
|
|
|
newPointerSection, newDataSize * Constants.BITS_PER_WORD,
|
|
|
|
newPointerCount);
|
2014-06-05 22:25:29 +00:00
|
|
|
} else {
|
2020-10-02 20:47:25 +00:00
|
|
|
return factory.constructBuilder(resolved.segment, capTable, resolved.ptr * Constants.BYTES_PER_WORD,
|
2014-11-17 22:39:16 +00:00
|
|
|
oldPointerSection, oldDataSize * Constants.BITS_PER_WORD,
|
2014-10-26 14:13:12 +00:00
|
|
|
oldPointerCount);
|
2014-06-05 22:25:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-04 01:52:52 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 01:21:15 +00:00
|
|
|
static <T> T initListPointer(ListBuilder.Factory<T> factory,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
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);
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, 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,
|
2014-10-19 01:25:38 +00:00
|
|
|
allocation.ptr * Constants.BYTES_PER_WORD,
|
|
|
|
elementCount, step, dataSize, (short)pointerCount);
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 01:21:15 +00:00
|
|
|
static <T> T initStructListPointer(ListBuilder.Factory<T> factory,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-07 20:49:36 +00:00
|
|
|
int refOffset,
|
|
|
|
SegmentBuilder segment,
|
|
|
|
int elementCount,
|
|
|
|
StructSize elementSize) {
|
2014-05-15 23:05:20 +00:00
|
|
|
int wordsPerElement = elementSize.total();
|
|
|
|
|
2014-05-17 20:04:51 +00:00
|
|
|
//# Allocate the list, prefixed by a single WirePointer.
|
|
|
|
int wordCount = elementCount * wordsPerElement;
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, Constants.POINTER_SIZE_IN_WORDS + wordCount,
|
2014-06-16 18:05:59 +00:00
|
|
|
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,
|
2014-10-19 01:25:38 +00:00
|
|
|
(allocation.ptr + 1) * Constants.BYTES_PER_WORD,
|
|
|
|
elementCount, wordsPerElement * Constants.BITS_PER_WORD,
|
|
|
|
elementSize.data * Constants.BITS_PER_WORD, elementSize.pointers);
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 01:21:15 +00:00
|
|
|
static <T> T getWritableListPointer(ListBuilder.Factory<T> factory,
|
2014-10-07 20:49:36 +00:00
|
|
|
int origRefOffset,
|
|
|
|
SegmentBuilder origSegment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-07 20:49:36 +00:00
|
|
|
byte elementSize,
|
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset) {
|
2017-04-24 23:35:12 +00:00
|
|
|
assert elementSize != ElementSize.INLINE_COMPOSITE : "Use getWritableStructListPointer() for struct lists";
|
2014-06-14 18:32:54 +00:00
|
|
|
|
2014-10-26 22:22:48 +00:00
|
|
|
long origRef = origSegment.get(origRefOffset);
|
2014-06-14 18:32:54 +00:00
|
|
|
int origRefTarget = WirePointer.target(origRefOffset, origRef);
|
|
|
|
|
|
|
|
if (WirePointer.isNull(origRef)) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("unimplemented");
|
2014-06-14 18:32:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//# We must verify that the pointer has the right size. Unlike
|
2014-10-26 13:31:09 +00:00
|
|
|
//# in getWritableStructListPointer(), we never need to
|
2014-06-14 18:32:54 +00:00
|
|
|
//# "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.
|
|
|
|
|
2014-06-16 23:46:33 +00:00
|
|
|
FollowBuilderFarsResult resolved = followBuilderFars(origRef, origRefTarget, origSegment);
|
2014-06-14 18:32:54 +00:00
|
|
|
|
2014-06-16 23:46:33 +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");
|
|
|
|
}
|
|
|
|
|
2014-06-17 00:00:01 +00:00
|
|
|
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.
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("unimplemented");
|
2014-06-14 18:32:54 +00:00
|
|
|
} 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
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-08 01:21:15 +00:00
|
|
|
static <T> T getWritableStructListPointer(ListBuilder.Factory<T> factory,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-07 21:05:37 +00:00
|
|
|
int origRefOffset,
|
|
|
|
SegmentBuilder origSegment,
|
|
|
|
StructSize elementSize,
|
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long origRef = origSegment.get(origRefOffset);
|
2014-10-26 13:31:09 +00:00
|
|
|
int origRefTarget = WirePointer.target(origRefOffset, origRef);
|
|
|
|
|
|
|
|
if (WirePointer.isNull(origRef)) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("unimplemented");
|
2014-10-26 13:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//# We must verify that the pointer has the right size and potentially upgrade it if not.
|
|
|
|
|
|
|
|
FollowBuilderFarsResult resolved = followBuilderFars(origRef, origRefTarget, origSegment);
|
|
|
|
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
|
|
|
|
throw new DecodeException("Called getList{Field,Element}() but existing pointer is not a list");
|
|
|
|
}
|
|
|
|
|
|
|
|
byte oldSize = ListPointer.elementSize(resolved.ref);
|
|
|
|
|
|
|
|
if (oldSize == ElementSize.INLINE_COMPOSITE) {
|
2014-10-26 22:22:48 +00:00
|
|
|
//# Existing list is INLINE_COMPOSITE, but we need to verify that the sizes match.
|
|
|
|
long oldTag = resolved.segment.get(resolved.ptr);
|
2014-10-26 22:56:58 +00:00
|
|
|
int oldPtr = resolved.ptr + Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
if (WirePointer.kind(oldTag) != WirePointer.STRUCT) {
|
|
|
|
throw new DecodeException("INLINE_COMPOSITE list with non-STRUCT elements not supported.");
|
|
|
|
}
|
|
|
|
int oldDataSize = StructPointer.dataSize(oldTag);
|
2014-10-26 23:24:30 +00:00
|
|
|
short oldPointerCount = StructPointer.ptrCount(oldTag);
|
2014-10-26 22:56:58 +00:00
|
|
|
int oldStep = (oldDataSize + oldPointerCount * Constants.POINTER_SIZE_IN_WORDS);
|
|
|
|
int elementCount = WirePointer.inlineCompositeListElementCount(oldTag);
|
|
|
|
|
|
|
|
if (oldDataSize >= elementSize.data && oldPointerCount >= elementSize.pointers) {
|
|
|
|
//# Old size is at least as large as we need. Ship it.
|
2014-10-27 12:54:41 +00:00
|
|
|
return factory.constructBuilder(resolved.segment, oldPtr * Constants.BYTES_PER_WORD,
|
2014-10-26 23:24:30 +00:00
|
|
|
elementCount,
|
|
|
|
oldStep * Constants.BITS_PER_WORD,
|
|
|
|
oldDataSize * Constants.BITS_PER_WORD, oldPointerCount);
|
2014-10-26 22:56:58 +00:00
|
|
|
}
|
2014-10-26 22:22:48 +00:00
|
|
|
|
2014-10-26 22:56:58 +00:00
|
|
|
//# The structs in this list are smaller than expected, probably written using an older
|
|
|
|
//# version of the protocol. We need to make a copy and expand them.
|
2014-11-17 02:17:56 +00:00
|
|
|
|
|
|
|
short newDataSize = (short)Math.max(oldDataSize, elementSize.data);
|
|
|
|
short newPointerCount = (short)Math.max(oldPointerCount, elementSize.pointers);
|
|
|
|
int newStep = newDataSize + newPointerCount * Constants.WORDS_PER_POINTER;
|
|
|
|
int totalSize = newStep * elementCount;
|
|
|
|
|
|
|
|
//# Don't let allocate() zero out the object just yet.
|
|
|
|
zeroPointerAndFars(origSegment, origRefOffset);
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(origRefOffset, origSegment, capTable,
|
2014-11-17 02:17:56 +00:00
|
|
|
totalSize + Constants.POINTER_SIZE_IN_WORDS,
|
|
|
|
WirePointer.LIST);
|
|
|
|
|
|
|
|
ListPointer.setInlineComposite(allocation.segment.buffer, allocation.refOffset, totalSize);
|
|
|
|
|
|
|
|
long tag = allocation.segment.get(allocation.ptr);
|
|
|
|
WirePointer.setKindAndInlineCompositeListElementCount(
|
|
|
|
allocation.segment.buffer, allocation.ptr,
|
|
|
|
WirePointer.STRUCT, elementCount);
|
|
|
|
StructPointer.set(allocation.segment.buffer, allocation.ptr,
|
|
|
|
newDataSize, newPointerCount);
|
|
|
|
int newPtr = allocation.ptr + Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
|
|
|
|
int src = oldPtr;
|
|
|
|
int dst = newPtr;
|
|
|
|
for (int ii = 0; ii < elementCount; ++ii) {
|
|
|
|
//# Copy data section.
|
|
|
|
memcpy(allocation.segment.buffer, dst * Constants.BYTES_PER_WORD,
|
|
|
|
resolved.segment.buffer, src * Constants.BYTES_PER_WORD,
|
|
|
|
oldDataSize * Constants.BYTES_PER_WORD);
|
|
|
|
|
|
|
|
//# Copy pointer section.
|
|
|
|
int newPointerSection = dst + newDataSize;
|
|
|
|
int oldPointerSection = src + oldDataSize;
|
|
|
|
for (int jj = 0; jj < oldPointerCount; ++jj) {
|
|
|
|
transferPointer(allocation.segment, newPointerSection + jj,
|
|
|
|
resolved.segment, oldPointerSection + jj);
|
|
|
|
}
|
|
|
|
|
|
|
|
dst += newStep;
|
|
|
|
src += oldStep;
|
|
|
|
}
|
|
|
|
|
|
|
|
//# Zero out old location. See explanation in getWritableStructPointer().
|
2017-02-27 23:42:38 +00:00
|
|
|
//# Make sure to include the tag word.
|
2014-11-17 02:17:56 +00:00
|
|
|
memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
|
2017-02-27 23:42:38 +00:00
|
|
|
(byte)0, (1 + oldStep * elementCount) * Constants.BYTES_PER_WORD);
|
2014-11-17 02:17:56 +00:00
|
|
|
|
|
|
|
return factory.constructBuilder(allocation.segment, newPtr * Constants.BYTES_PER_WORD,
|
|
|
|
elementCount,
|
|
|
|
newStep * Constants.BITS_PER_WORD,
|
|
|
|
newDataSize * Constants.BITS_PER_WORD,
|
|
|
|
newPointerCount);
|
2014-10-26 13:31:09 +00:00
|
|
|
} else {
|
2014-10-26 22:56:58 +00:00
|
|
|
//# We're upgrading from a non-struct list.
|
2014-11-17 00:06:35 +00:00
|
|
|
|
|
|
|
int oldDataSize = ElementSize.dataBitsPerElement(oldSize);
|
|
|
|
int oldPointerCount = ElementSize.pointersPerElement(oldSize);
|
|
|
|
int oldStep = oldDataSize + oldPointerCount * Constants.BITS_PER_POINTER;
|
|
|
|
int elementCount = ListPointer.elementCount(origRef);
|
|
|
|
|
|
|
|
if (oldSize == ElementSize.VOID) {
|
|
|
|
//# Nothing to copy, just allocate a new list.
|
2020-10-02 20:47:25 +00:00
|
|
|
return initStructListPointer(factory, capTable, origRefOffset, origSegment,
|
2014-11-17 00:06:35 +00:00
|
|
|
elementCount, elementSize);
|
|
|
|
} else {
|
|
|
|
//# Upgrading to an inline composite list.
|
|
|
|
|
|
|
|
if (oldSize == ElementSize.BIT) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("Found bit list where struct list was expected; " +
|
2014-11-17 00:06:35 +00:00
|
|
|
"upgrading boolean lists to struct is no longer supported.");
|
|
|
|
}
|
|
|
|
|
|
|
|
short newDataSize = elementSize.data;
|
|
|
|
short newPointerCount = elementSize.pointers;
|
|
|
|
|
|
|
|
if (oldSize == ElementSize.POINTER) {
|
|
|
|
newPointerCount = (short)Math.max(newPointerCount, 1);
|
|
|
|
} else {
|
|
|
|
//# Old list contains data elements, so we need at least 1 word of data.
|
|
|
|
newDataSize = (short)Math.max(newDataSize, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
int newStep = (newDataSize + newPointerCount * Constants.WORDS_PER_POINTER);
|
|
|
|
int totalWords = elementCount * newStep;
|
|
|
|
|
|
|
|
//# Don't let allocate() zero out the object just yet.
|
|
|
|
zeroPointerAndFars(origSegment, origRefOffset);
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(origRefOffset, origSegment, capTable,
|
2014-11-17 00:06:35 +00:00
|
|
|
totalWords + Constants.POINTER_SIZE_IN_WORDS,
|
|
|
|
WirePointer.LIST);
|
|
|
|
|
|
|
|
ListPointer.setInlineComposite(allocation.segment.buffer, allocation.refOffset, totalWords);
|
|
|
|
|
|
|
|
long tag = allocation.segment.get(allocation.ptr);
|
|
|
|
WirePointer.setKindAndInlineCompositeListElementCount(
|
|
|
|
allocation.segment.buffer, allocation.ptr,
|
|
|
|
WirePointer.STRUCT, elementCount);
|
|
|
|
StructPointer.set(allocation.segment.buffer, allocation.ptr,
|
|
|
|
newDataSize, newPointerCount);
|
|
|
|
int newPtr = allocation.ptr + Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
|
|
|
|
if (oldSize == ElementSize.POINTER) {
|
2014-11-17 02:17:56 +00:00
|
|
|
int dst = newPtr + newDataSize;
|
|
|
|
int src = resolved.ptr;
|
2014-11-17 00:06:35 +00:00
|
|
|
for (int ii = 0; ii < elementCount; ++ii) {
|
2014-11-17 02:17:56 +00:00
|
|
|
transferPointer(origSegment, dst, resolved.segment, src);
|
|
|
|
dst += newStep / Constants.WORDS_PER_POINTER;
|
|
|
|
src += 1;
|
2014-11-17 00:06:35 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int dst = newPtr;
|
|
|
|
int srcByteOffset = resolved.ptr * Constants.BYTES_PER_WORD;
|
|
|
|
int oldByteStep = oldDataSize / Constants.BITS_PER_BYTE;
|
|
|
|
for (int ii = 0; ii < elementCount; ++ii) {
|
|
|
|
memcpy(allocation.segment.buffer, dst * Constants.BYTES_PER_WORD,
|
|
|
|
resolved.segment.buffer, srcByteOffset, oldByteStep);
|
|
|
|
srcByteOffset += oldByteStep;
|
|
|
|
dst += newStep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//# Zero out old location. See explanation in getWritableStructPointer().
|
|
|
|
memset(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
|
|
|
|
(byte)0, roundBitsUpToBytes(oldStep * elementCount));
|
|
|
|
|
2014-11-17 02:17:56 +00:00
|
|
|
return factory.constructBuilder(allocation.segment, newPtr * Constants.BYTES_PER_WORD,
|
2014-11-17 00:06:35 +00:00
|
|
|
elementCount,
|
|
|
|
newStep * Constants.BITS_PER_WORD,
|
|
|
|
newDataSize * Constants.BITS_PER_WORD,
|
|
|
|
newPointerCount);
|
|
|
|
}
|
2014-10-26 13:31:09 +00:00
|
|
|
}
|
2014-10-07 21:05:37 +00:00
|
|
|
}
|
|
|
|
|
2014-05-15 23:05:20 +00:00
|
|
|
// size is in bytes
|
2014-10-05 14:01:43 +00:00
|
|
|
static Text.Builder initTextPointer(int refOffset,
|
|
|
|
SegmentBuilder segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
int size) {
|
2014-05-15 23:05:20 +00:00
|
|
|
//# The byte list must include a NUL terminator.
|
|
|
|
int byteSize = size + 1;
|
|
|
|
|
2014-05-17 20:57:13 +00:00
|
|
|
//# Allocate the space.
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, roundBytesUpToWords(byteSize),
|
2014-06-16 18:05:59 +00:00
|
|
|
WirePointer.LIST);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-05 14:01:43 +00:00
|
|
|
static Text.Builder setTextPointer(int refOffset,
|
|
|
|
SegmentBuilder segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
Text.Reader value) {
|
2020-10-02 20:47:25 +00:00
|
|
|
Text.Builder builder = initTextPointer(refOffset, segment, capTable, value.size);
|
2014-05-18 03:06:32 +00:00
|
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-05 14:01:43 +00:00
|
|
|
static Text.Builder getWritableTextPointer(int refOffset,
|
|
|
|
SegmentBuilder segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
ByteBuffer defaultBuffer,
|
|
|
|
int defaultOffset,
|
|
|
|
int defaultSize) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-06-13 20:14:16 +00:00
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
2014-10-03 16:01:09 +00:00
|
|
|
if (defaultBuffer == null) {
|
2014-10-26 23:24:30 +00:00
|
|
|
return new Text.Builder();
|
2014-10-03 16:01:09 +00:00
|
|
|
} else {
|
2020-10-02 20:47:25 +00:00
|
|
|
Text.Builder builder = initTextPointer(refOffset, segment, capTable, defaultSize);
|
2014-10-03 16:01:09 +00:00
|
|
|
// 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);
|
2014-06-16 23:46:33 +00:00
|
|
|
FollowBuilderFarsResult resolved = followBuilderFars(ref, refTarget, segment);
|
2014-06-13 20:14:16 +00:00
|
|
|
|
2014-06-16 23:46:33 +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.");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-09 00:09:56 +00:00
|
|
|
int size = ListPointer.elementCount(resolved.ref);
|
|
|
|
if (size == 0 ||
|
|
|
|
resolved.segment.buffer.get(resolved.ptr * Constants.BYTES_PER_WORD + size - 1) != 0) {
|
|
|
|
throw new DecodeException("Text blob missing NUL terminator.");
|
|
|
|
}
|
2014-06-16 23:46:33 +00:00
|
|
|
return new Text.Builder(resolved.segment.buffer, resolved.ptr * Constants.BYTES_PER_WORD,
|
2015-03-09 00:09:56 +00:00
|
|
|
size - 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,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
int size) {
|
2014-10-03 17:24:51 +00:00
|
|
|
//# Allocate the space.
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, roundBytesUpToWords(size),
|
2014-10-03 17:24:51 +00:00
|
|
|
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,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
Data.Reader value) {
|
2020-10-02 20:47:25 +00:00
|
|
|
Data.Builder builder = initDataPointer(refOffset, segment, capTable, value.size);
|
2014-10-03 17:24:51 +00:00
|
|
|
|
|
|
|
// 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,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableBuilder capTable,
|
2014-10-05 14:01:43 +00:00
|
|
|
ByteBuffer defaultBuffer,
|
|
|
|
int defaultOffset,
|
|
|
|
int defaultSize) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-10-03 17:24:51 +00:00
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
|
|
|
if (defaultBuffer == null) {
|
2014-10-26 23:24:30 +00:00
|
|
|
return new Data.Builder();
|
2014-10-03 17:24:51 +00:00
|
|
|
} else {
|
2020-10-02 20:47:25 +00:00
|
|
|
Data.Builder builder = initDataPointer(refOffset, segment, capTable, defaultSize);
|
2014-10-03 17:24:51 +00:00
|
|
|
// 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));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-10-07 21:47:42 +00:00
|
|
|
static <T> T readStructPointer(StructReader.Factory<T> factory,
|
2014-10-07 01:43:50 +00:00
|
|
|
SegmentReader segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableReader capTable,
|
2014-10-07 01:43:50 +00:00
|
|
|
int refOffset,
|
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset,
|
|
|
|
int nestingLimit) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-10-06 18:05:59 +00:00
|
|
|
if (WirePointer.isNull(ref)) {
|
|
|
|
if (defaultSegment == null) {
|
2014-10-26 14:13:12 +00:00
|
|
|
return factory.constructReader(SegmentReader.EMPTY, 0, 0, 0, (short) 0, 0x7fffffff);
|
2014-10-06 18:05:59 +00:00
|
|
|
} else {
|
2014-10-07 21:05:37 +00:00
|
|
|
segment = defaultSegment;
|
|
|
|
refOffset = defaultOffset;
|
2014-10-26 22:22:48 +00:00
|
|
|
ref = segment.get(refOffset);
|
2014-10-06 18:05:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
if (nestingLimit <= 0) {
|
2014-05-15 23:05:20 +00:00
|
|
|
throw new DecodeException("Message is too deeply nested or contains cycles.");
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2014-10-07 21:47:42 +00:00
|
|
|
return factory.constructReader(resolved.segment,
|
2020-10-02 20:47:25 +00:00
|
|
|
capTable,
|
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),
|
|
|
|
nestingLimit - 1);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-13 16:19:24 +00:00
|
|
|
static StructReader readStructPointer(SegmentReader segment,
|
|
|
|
CapTableReader capTable,
|
|
|
|
int refOffset,
|
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset,
|
|
|
|
int nestingLimit) {
|
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
if (WirePointer.isNull(ref)) {
|
|
|
|
if (defaultSegment == null) {
|
|
|
|
return new StructReader(SegmentReader.EMPTY, 0, 0, 0, (short) 0, 0x7fffffff);
|
|
|
|
} else {
|
|
|
|
segment = defaultSegment;
|
|
|
|
refOffset = defaultOffset;
|
|
|
|
ref = segment.get(refOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nestingLimit <= 0) {
|
|
|
|
throw new DecodeException("Message is too deeply nested or contains cycles.");
|
|
|
|
}
|
|
|
|
|
|
|
|
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.");
|
|
|
|
}
|
|
|
|
|
|
|
|
resolved.segment.arena.checkReadLimit(StructPointer.wordSize(resolved.ref));
|
|
|
|
|
|
|
|
return new StructReader(resolved.segment,
|
|
|
|
capTable,
|
|
|
|
resolved.ptr * Constants.BYTES_PER_WORD,
|
|
|
|
(resolved.ptr + dataSizeWords),
|
|
|
|
dataSizeWords * Constants.BITS_PER_WORD,
|
|
|
|
StructPointer.ptrCount(resolved.ref),
|
|
|
|
nestingLimit - 1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
static SegmentBuilder setStructPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, StructReader value) {
|
2014-10-16 22:54:11 +00:00
|
|
|
short dataSize = (short)roundBitsUpToWords(value.dataSize);
|
|
|
|
int totalSize = dataSize + value.pointerCount * Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize, WirePointer.STRUCT);
|
2014-10-18 00:53:51 +00:00
|
|
|
StructPointer.set(allocation.segment.buffer, allocation.refOffset,
|
2014-10-16 22:54:11 +00:00
|
|
|
dataSize, value.pointerCount);
|
|
|
|
|
|
|
|
if (value.dataSize == 1) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("single bit case not handled");
|
2014-10-16 22:54:11 +00:00
|
|
|
} else {
|
2014-10-18 00:53:51 +00:00
|
|
|
memcpy(allocation.segment.buffer, allocation.ptr * Constants.BYTES_PER_WORD,
|
2014-10-16 22:54:11 +00:00
|
|
|
value.segment.buffer, value.data, value.dataSize / Constants.BITS_PER_BYTE);
|
|
|
|
}
|
|
|
|
|
|
|
|
int pointerSection = allocation.ptr + dataSize;
|
|
|
|
for (int i = 0; i < value.pointerCount; ++i) {
|
2020-10-02 20:47:25 +00:00
|
|
|
copyPointer(allocation.segment, capTable, pointerSection + i, value.segment, value.capTable, value.pointers + i,
|
2014-10-16 22:54:11 +00:00
|
|
|
value.nestingLimit);
|
|
|
|
}
|
2014-10-17 13:36:16 +00:00
|
|
|
return allocation.segment;
|
2014-10-05 14:01:43 +00:00
|
|
|
};
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
static SegmentBuilder setListPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, ListReader value) {
|
2014-10-07 00:04:01 +00:00
|
|
|
int totalSize = roundBitsUpToWords(value.elementCount * value.step);
|
|
|
|
|
|
|
|
if (value.step <= Constants.BITS_PER_WORD) {
|
|
|
|
//# List of non-structs.
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize, WirePointer.LIST);
|
2014-10-07 00:04:01 +00:00
|
|
|
|
|
|
|
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) {
|
2020-10-02 20:47:25 +00:00
|
|
|
copyPointer(allocation.segment, capTable,allocation.ptr + i,
|
|
|
|
value.segment, value.capTable, value.ptr / Constants.BYTES_PER_WORD + i, value.nestingLimit);
|
2014-10-07 00:04:01 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//# List of data.
|
2014-10-16 01:43:57 +00:00
|
|
|
byte elementSize = ElementSize.VOID;
|
|
|
|
switch (value.step) {
|
|
|
|
case 0: elementSize = ElementSize.VOID; break;
|
|
|
|
case 1: elementSize = ElementSize.BIT; break;
|
|
|
|
case 8: elementSize = ElementSize.BYTE; break;
|
|
|
|
case 16: elementSize = ElementSize.TWO_BYTES; break;
|
|
|
|
case 32: elementSize = ElementSize.FOUR_BYTES; break;
|
|
|
|
case 64: elementSize = ElementSize.EIGHT_BYTES; break;
|
|
|
|
default:
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("invalid list step size: " + value.step);
|
2014-10-16 01:43:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ListPointer.set(allocation.segment.buffer, allocation.refOffset, elementSize, value.elementCount);
|
2014-10-16 22:54:11 +00:00
|
|
|
memcpy(allocation.segment.buffer, allocation.ptr * Constants.BYTES_PER_WORD,
|
2014-10-19 01:25:38 +00:00
|
|
|
value.segment.buffer, value.ptr, totalSize * Constants.BYTES_PER_WORD);
|
2014-10-07 00:04:01 +00:00
|
|
|
}
|
2014-10-16 22:54:11 +00:00
|
|
|
return allocation.segment;
|
2014-10-07 00:04:01 +00:00
|
|
|
} else {
|
|
|
|
//# List of structs.
|
2020-10-02 20:47:25 +00:00
|
|
|
AllocateResult allocation = allocate(refOffset, segment, capTable, totalSize + Constants.POINTER_SIZE_IN_WORDS, WirePointer.LIST);
|
2014-10-16 22:54:11 +00:00
|
|
|
ListPointer.setInlineComposite(allocation.segment.buffer, allocation.refOffset, totalSize);
|
|
|
|
short dataSize = (short)roundBitsUpToWords(value.structDataSize);
|
|
|
|
short pointerCount = value.structPointerCount;
|
|
|
|
|
|
|
|
WirePointer.setKindAndInlineCompositeListElementCount(allocation.segment.buffer, allocation.ptr,
|
|
|
|
WirePointer.STRUCT, value.elementCount);
|
|
|
|
StructPointer.set(allocation.segment.buffer, allocation.ptr,
|
|
|
|
dataSize, pointerCount);
|
|
|
|
|
|
|
|
int dstOffset = allocation.ptr + Constants.POINTER_SIZE_IN_WORDS;
|
2014-10-19 01:25:38 +00:00
|
|
|
int srcOffset = value.ptr / Constants.BYTES_PER_WORD;
|
2014-10-16 22:54:11 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < value.elementCount; ++i) {
|
|
|
|
memcpy(allocation.segment.buffer, dstOffset * Constants.BYTES_PER_WORD,
|
|
|
|
value.segment.buffer, srcOffset * Constants.BYTES_PER_WORD,
|
|
|
|
value.structDataSize / Constants.BITS_PER_BYTE);
|
|
|
|
dstOffset += dataSize;
|
|
|
|
srcOffset += dataSize;
|
|
|
|
|
|
|
|
for (int j = 0; j < pointerCount; ++j) {
|
2020-10-02 20:47:25 +00:00
|
|
|
copyPointer(allocation.segment, capTable, dstOffset, value.segment, value.capTable, srcOffset, value.nestingLimit);
|
2014-10-16 22:54:11 +00:00
|
|
|
dstOffset += Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
srcOffset += Constants.POINTER_SIZE_IN_WORDS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return allocation.segment;
|
2014-10-07 00:04:01 +00:00
|
|
|
}
|
2014-10-16 22:54:11 +00:00
|
|
|
}
|
2014-10-07 00:04:01 +00:00
|
|
|
|
2014-11-05 02:53:34 +00:00
|
|
|
static void memset(ByteBuffer dstBuffer, int dstByteOffset, byte value, int length) {
|
|
|
|
// TODO we can probably do this faster
|
|
|
|
for (int ii = dstByteOffset; ii < dstByteOffset + length; ++ii) {
|
|
|
|
dstBuffer.put(ii, value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-16 22:54:11 +00:00
|
|
|
static void memcpy(ByteBuffer dstBuffer, int dstByteOffset, ByteBuffer srcBuffer, int srcByteOffset, int length) {
|
|
|
|
ByteBuffer dstDup = dstBuffer.duplicate();
|
|
|
|
dstDup.position(dstByteOffset);
|
2014-10-17 13:36:16 +00:00
|
|
|
dstDup.limit(dstByteOffset + length);
|
2014-10-16 22:54:11 +00:00
|
|
|
ByteBuffer srcDup = srcBuffer.duplicate();
|
|
|
|
srcDup.position(srcByteOffset);
|
2014-10-17 13:36:16 +00:00
|
|
|
srcDup.limit(srcByteOffset + length);
|
2014-10-16 22:54:11 +00:00
|
|
|
dstDup.put(srcDup);
|
2014-10-07 00:04:01 +00:00
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
static SegmentBuilder copyPointer(SegmentBuilder dstSegment, CapTableBuilder dstCapTable, int dstOffset,
|
|
|
|
SegmentReader srcSegment, CapTableReader srcCapTable, int srcOffset, int nestingLimit) {
|
2014-10-07 00:04:01 +00:00
|
|
|
// 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-26 22:22:48 +00:00
|
|
|
long srcRef = srcSegment.get(srcOffset);
|
2014-10-11 13:18:41 +00:00
|
|
|
|
|
|
|
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));
|
2020-10-02 20:47:25 +00:00
|
|
|
return setStructPointer(dstSegment, dstCapTable, dstOffset,
|
2014-10-16 00:25:45 +00:00
|
|
|
new StructReader(resolved.segment,
|
2020-11-09 17:29:00 +00:00
|
|
|
srcCapTable,
|
2014-10-19 01:25:38 +00:00
|
|
|
resolved.ptr * Constants.BYTES_PER_WORD,
|
2014-10-15 22:34:05 +00:00
|
|
|
resolved.ptr + StructPointer.dataSize(resolved.ref),
|
|
|
|
StructPointer.dataSize(resolved.ref) * Constants.BITS_PER_WORD,
|
|
|
|
StructPointer.ptrCount(resolved.ref),
|
2014-10-26 14:13:12 +00:00
|
|
|
nestingLimit - 1));
|
2014-10-11 13:18:41 +00:00
|
|
|
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);
|
2014-10-26 22:22:48 +00:00
|
|
|
long tag = resolved.segment.get(resolved.ptr);
|
2014-10-15 22:34:05 +00:00
|
|
|
int ptr = resolved.ptr + 1;
|
|
|
|
|
|
|
|
resolved.segment.arena.checkReadLimit(wordCount + 1);
|
2014-10-11 13:18:41 +00:00
|
|
|
|
2014-10-15 22:34:05 +00:00
|
|
|
if (WirePointer.kind(tag) != WirePointer.STRUCT) {
|
|
|
|
throw new DecodeException("INLINE_COMPOSITE lists of non-STRUCT type are not supported.");
|
|
|
|
}
|
|
|
|
|
|
|
|
int elementCount = WirePointer.inlineCompositeListElementCount(tag);
|
2014-10-16 00:25:45 +00:00
|
|
|
int wordsPerElement = StructPointer.wordSize(tag);
|
2015-03-09 00:39:44 +00:00
|
|
|
if ((long)wordsPerElement * elementCount > wordCount) {
|
2014-10-16 00:25:45 +00:00
|
|
|
throw new DecodeException("INLINE_COMPOSITE list's elements overrun its word count.");
|
|
|
|
}
|
2015-03-03 15:46:42 +00:00
|
|
|
|
|
|
|
if (wordsPerElement == 0) {
|
|
|
|
// Watch out for lists of zero-sized structs, which can claim to be arbitrarily
|
|
|
|
// large without having sent actual data.
|
|
|
|
resolved.segment.arena.checkReadLimit(elementCount);
|
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
return setListPointer(dstSegment, dstCapTable, dstOffset,
|
2014-10-16 00:25:45 +00:00
|
|
|
new ListReader(resolved.segment,
|
2014-10-19 01:25:38 +00:00
|
|
|
ptr * Constants.BYTES_PER_WORD,
|
2014-10-16 00:25:45 +00:00
|
|
|
elementCount,
|
|
|
|
wordsPerElement * Constants.BITS_PER_WORD,
|
2014-10-18 00:53:51 +00:00
|
|
|
StructPointer.dataSize(tag) * Constants.BITS_PER_WORD,
|
|
|
|
StructPointer.ptrCount(tag),
|
2014-10-16 00:25:45 +00:00
|
|
|
nestingLimit - 1));
|
2014-10-15 22:34:05 +00:00
|
|
|
} else {
|
2014-10-16 00:25:45 +00:00
|
|
|
int dataSize = ElementSize.dataBitsPerElement(elementSize);
|
|
|
|
short pointerCount = ElementSize.pointersPerElement(elementSize);
|
|
|
|
int step = dataSize + pointerCount * Constants.BITS_PER_POINTER;
|
|
|
|
int elementCount = ListPointer.elementCount(resolved.ref);
|
|
|
|
int wordCount = roundBitsUpToWords((long) elementCount * step);
|
|
|
|
|
|
|
|
resolved.segment.arena.checkReadLimit(wordCount);
|
|
|
|
|
2015-03-03 15:46:42 +00:00
|
|
|
if (elementSize == ElementSize.VOID) {
|
|
|
|
// Watch out for lists of void, which can claim to be arbitrarily large without
|
|
|
|
// having sent actual data.
|
|
|
|
resolved.segment.arena.checkReadLimit(elementCount);
|
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
return setListPointer(dstSegment, dstCapTable, dstOffset,
|
2014-10-16 00:25:45 +00:00
|
|
|
new ListReader(resolved.segment,
|
2014-10-19 01:25:38 +00:00
|
|
|
resolved.ptr * Constants.BYTES_PER_WORD,
|
2014-10-16 00:25:45 +00:00
|
|
|
elementCount,
|
|
|
|
step,
|
|
|
|
dataSize,
|
|
|
|
pointerCount,
|
|
|
|
nestingLimit - 1));
|
2014-10-11 13:18:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case WirePointer.FAR :
|
2017-02-27 17:10:36 +00:00
|
|
|
throw new DecodeException("Unexpected FAR pointer.");
|
2014-10-11 13:18:41 +00:00
|
|
|
case WirePointer.OTHER :
|
2020-11-09 17:29:00 +00:00
|
|
|
if (WirePointer.isCapability(srcRef)) {
|
|
|
|
var cap = readCapabilityPointer(srcSegment, srcCapTable, srcOffset, 0);
|
|
|
|
setCapabilityPointer(dstSegment, dstCapTable, dstOffset, cap);
|
|
|
|
return dstSegment;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw new RuntimeException("copyPointer is unimplemented for OTHER pointers");
|
|
|
|
}
|
2014-10-11 13:18:41 +00:00
|
|
|
}
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new RuntimeException("unreachable");
|
2014-10-07 00:04:01 +00:00
|
|
|
}
|
|
|
|
|
2014-10-08 01:21:15 +00:00
|
|
|
static <T> T readListPointer(ListReader.Factory<T> factory,
|
2014-10-07 20:49:36 +00:00
|
|
|
SegmentReader segment,
|
|
|
|
int refOffset,
|
2020-10-02 20:47:25 +00:00
|
|
|
CapTableReader capTable,
|
2014-10-07 20:49:36 +00:00
|
|
|
SegmentReader defaultSegment,
|
|
|
|
int defaultOffset,
|
|
|
|
byte expectedElementSize,
|
|
|
|
int nestingLimit) {
|
2014-09-05 15:43:46 +00:00
|
|
|
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
2014-10-06 18:54:15 +00:00
|
|
|
if (defaultSegment == null) {
|
2015-03-03 14:44:04 +00:00
|
|
|
return factory.constructReader(SegmentReader.EMPTY, 0, 0, 0, 0, (short) 0, 0x7fffffff);
|
2014-10-06 18:54:15 +00:00
|
|
|
} else {
|
|
|
|
segment = defaultSegment;
|
|
|
|
refOffset = defaultOffset;
|
2014-10-26 22:22:48 +00:00
|
|
|
ref = segment.get(refOffset);
|
2014-10-06 18:54:15 +00:00
|
|
|
}
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
if (nestingLimit <= 0) {
|
2020-04-29 07:15:57 +00:00
|
|
|
throw new DecodeException("nesting limit exceeded");
|
2014-06-17 23:41:19 +00:00
|
|
|
}
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
int refTarget = WirePointer.target(refOffset, ref);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
FollowFarsResult resolved = followFars(ref, refTarget, segment);
|
|
|
|
|
2015-03-03 15:46:42 +00:00
|
|
|
byte elementSize = ListPointer.elementSize(resolved.ref);
|
|
|
|
switch (elementSize) {
|
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-05-15 23:05:20 +00:00
|
|
|
|
2014-10-26 22:22:48 +00:00
|
|
|
long tag = resolved.segment.get(resolved.ptr);
|
2014-06-17 23:41:19 +00:00
|
|
|
int ptr = resolved.ptr + 1;
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2014-10-08 19:16:17 +00:00
|
|
|
resolved.segment.arena.checkReadLimit(wordCount + 1);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
|
|
|
int size = WirePointer.inlineCompositeListElementCount(tag);
|
|
|
|
|
2014-06-17 00:00:01 +00:00
|
|
|
int wordsPerElement = StructPointer.wordSize(tag);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2015-03-09 00:39:44 +00:00
|
|
|
if ((long)size * wordsPerElement > wordCount) {
|
|
|
|
throw new DecodeException("INLINE_COMPOSITE list's elements overrun its word count.");
|
|
|
|
}
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2015-03-03 15:46:42 +00:00
|
|
|
if (wordsPerElement == 0) {
|
|
|
|
// Watch out for lists of zero-sized structs, which can claim to be arbitrarily
|
|
|
|
// large without having sent actual data.
|
|
|
|
resolved.segment.arena.checkReadLimit(size);
|
|
|
|
}
|
|
|
|
|
2014-05-15 23:05:20 +00:00
|
|
|
// 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-05-15 23:05:20 +00:00
|
|
|
}
|
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));
|
2015-03-03 15:46:42 +00:00
|
|
|
int elementCount = ListPointer.elementCount(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(
|
2015-03-03 15:46:42 +00:00
|
|
|
roundBitsUpToWords(elementCount * step));
|
|
|
|
|
|
|
|
if (elementSize == ElementSize.VOID) {
|
|
|
|
// Watch out for lists of void, which can claim to be arbitrarily large without
|
|
|
|
// having sent actual data.
|
|
|
|
resolved.segment.arena.checkReadLimit(elementCount);
|
|
|
|
}
|
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-05-15 23:05:20 +00:00
|
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-10-05 14:01:43 +00:00
|
|
|
static Text.Reader readTextPointer(SegmentReader segment,
|
|
|
|
int refOffset,
|
|
|
|
ByteBuffer defaultBuffer,
|
|
|
|
int defaultOffset,
|
|
|
|
int defaultSize) {
|
2014-10-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
2014-10-03 16:01:09 +00:00
|
|
|
if (defaultBuffer == null) {
|
2014-10-26 23:24:30 +00:00
|
|
|
return new Text.Reader();
|
2014-10-03 16:01:09 +00:00
|
|
|
} else {
|
|
|
|
return new Text.Reader(defaultBuffer, defaultOffset, defaultSize);
|
|
|
|
}
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
int refTarget = WirePointer.target(refOffset, ref);
|
|
|
|
|
|
|
|
FollowFarsResult resolved = followFars(ref, refTarget, segment);
|
2014-05-15 23:05:20 +00:00
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
int size = ListPointer.elementCount(resolved.ref);
|
|
|
|
|
|
|
|
if (WirePointer.kind(resolved.ref) != WirePointer.LIST) {
|
2014-05-15 23:05:20 +00:00
|
|
|
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) {
|
2014-05-15 23:05:20 +00:00
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
|
2014-06-17 23:41:19 +00:00
|
|
|
if (size == 0 || resolved.segment.buffer.get(8 * resolved.ptr + size - 1) != 0) {
|
2014-05-15 23:05:20 +00:00
|
|
|
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-05-15 23:05:20 +00:00
|
|
|
}
|
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-26 22:22:48 +00:00
|
|
|
long ref = segment.get(refOffset);
|
2014-10-03 17:24:51 +00:00
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
|
|
|
if (defaultBuffer == null) {
|
2014-10-26 23:24:30 +00:00
|
|
|
return new Data.Reader();
|
2014-10-03 17:24:51 +00:00
|
|
|
} 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);
|
|
|
|
}
|
|
|
|
|
2020-09-27 22:09:16 +00:00
|
|
|
static void setCapabilityPointer(SegmentBuilder segment, CapTableBuilder capTable, int refOffset, ClientHook cap) {
|
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
|
|
|
|
if (!WirePointer.isNull(ref)) {
|
2020-10-02 20:47:25 +00:00
|
|
|
zeroObject(segment, capTable, refOffset);
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (cap == null) {
|
|
|
|
// TODO check zeroMemory behaviour
|
|
|
|
zeroPointerAndFars(segment, refOffset);
|
|
|
|
}
|
2020-10-02 20:47:25 +00:00
|
|
|
else if (capTable != null) {
|
2020-10-07 16:31:44 +00:00
|
|
|
WirePointer.setCapability(segment.buffer, refOffset, capTable.injectCap(cap));
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
2020-10-02 20:47:25 +00:00
|
|
|
else {
|
|
|
|
assert false: "Cannot set capability pointer without capTable";
|
|
|
|
}
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ClientHook readCapabilityPointer(SegmentReader segment, CapTableReader capTable, int refOffset, int maxValue) {
|
|
|
|
long ref = segment.get(refOffset);
|
|
|
|
|
|
|
|
if (WirePointer.isNull(ref)) {
|
2020-09-29 13:08:23 +00:00
|
|
|
return Capability.newNullCap();
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (WirePointer.kind(ref) != WirePointer.OTHER) {
|
2020-09-29 13:08:23 +00:00
|
|
|
return Capability.newBrokenCap("Calling capability extracted from a non-capability pointer.");
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
|
|
|
|
2020-10-02 20:47:25 +00:00
|
|
|
if (capTable == null) {
|
|
|
|
return Capability.newBrokenCap("Cannot read capability pointer without capTable.");
|
|
|
|
}
|
|
|
|
|
2020-10-13 16:19:24 +00:00
|
|
|
int index = WirePointer.upper32Bits(ref);
|
|
|
|
var cap = capTable.extractCap(index);
|
2020-09-27 22:09:16 +00:00
|
|
|
if (cap == null) {
|
2020-09-29 13:08:23 +00:00
|
|
|
return Capability.newBrokenCap("Calling invalid capability pointer.");
|
2020-09-27 22:09:16 +00:00
|
|
|
}
|
|
|
|
return cap;
|
|
|
|
}
|
|
|
|
|
2014-05-15 23:05:20 +00:00
|
|
|
}
|