diff --git a/compiler/src/main/cpp/capnpc-java.c++ b/compiler/src/main/cpp/capnpc-java.c++ index dfc6486..aac8078 100644 --- a/compiler/src/main/cpp/capnpc-java.c++ +++ b/compiler/src/main/cpp/capnpc-java.c++ @@ -1098,13 +1098,21 @@ private: spaces(indent), " public final boolean has", titleCase, "() {\n", spaces(indent), " return !_builder.getPointerField(", offset, ").isNull();\n", spaces(indent), " }\n", + spaces(indent), " public final ", type, ".", builderClass, " get", titleCase, "() {\n", - spaces(indent), " throw new Error();\n", + spaces(indent), " return new ", type, ".", builderClass, " (\n", + spaces(indent), " ", builderFactoryArg, "_builder.getPointerField(", offset, ").get", + (isStructList ? + kj::strTree("StructList(", typeName(typeBody.getList().getElementType()),".STRUCT_SIZE)") : + kj::strTree("List(", fieldSize, ")")), + ");\n", spaces(indent), " }\n", + spaces(indent), " public final void set", titleCase, "(", type, ".Reader value) {\n", spaces(indent), " throw new Error();\n", spaces(indent), " }\n", + spaces(indent), " public final ", type, ".", builderClass, " init", titleCase, "(int size) {\n", spaces(indent), " return new ", type, ".", builderClass, "(\n", diff --git a/compiler/src/test/scala/org/capnproto/TestUtil.scala b/compiler/src/test/scala/org/capnproto/TestUtil.scala index dfe884e..ef5c7fd 100644 --- a/compiler/src/test/scala/org/capnproto/TestUtil.scala +++ b/compiler/src/test/scala/org/capnproto/TestUtil.scala @@ -45,7 +45,7 @@ object TestUtil { builder.setEnumField(TestEnum.CORGE); - //builder.initVoidList(6); + builder.initVoidList(6); } @@ -85,6 +85,8 @@ object TestUtil { assert(subSubBuilder.getTextField().toString() == "nested") } } + + assert(builder.getVoidList().size() == 6); } } diff --git a/runtime/src/main/java/org/capnproto/InternalError.java b/runtime/src/main/java/org/capnproto/InternalError.java new file mode 100644 index 0000000..e11663c --- /dev/null +++ b/runtime/src/main/java/org/capnproto/InternalError.java @@ -0,0 +1,7 @@ +package org.capnproto; + +public final class InternalError extends RuntimeException { + public InternalError(String message) { + super(message); + } +} diff --git a/runtime/src/main/java/org/capnproto/ListBuilder.java b/runtime/src/main/java/org/capnproto/ListBuilder.java index fda22be..4958b7b 100644 --- a/runtime/src/main/java/org/capnproto/ListBuilder.java +++ b/runtime/src/main/java/org/capnproto/ListBuilder.java @@ -19,6 +19,10 @@ public final class ListBuilder { this.structPointerCount = structPointerCount; } + public int size() { + return this.elementCount; + } + public final StructBuilder getStructElement(int index) { int indexBit = index * this.step; int structData = this.ptr + indexBit / 8 ; diff --git a/runtime/src/main/java/org/capnproto/PointerBuilder.java b/runtime/src/main/java/org/capnproto/PointerBuilder.java index e942add..e2bcc16 100644 --- a/runtime/src/main/java/org/capnproto/PointerBuilder.java +++ b/runtime/src/main/java/org/capnproto/PointerBuilder.java @@ -21,6 +21,14 @@ public final class PointerBuilder { return WireHelpers.getWritableStructPointer(this.pointer, this.segment, size); } + public final ListBuilder getList(byte elementSize) { + return WireHelpers.getWritableListPointer(this.pointer, this.segment, elementSize); + } + + public final ListBuilder getStructList(StructSize elementSize) { + throw new Error("unimplemented"); + } + public final Text.Builder getText() { return WireHelpers.getWritableTextPointer( this.pointer, this.segment); @@ -35,7 +43,7 @@ public final class PointerBuilder { } public final ListBuilder initList(byte elementSize, int elementCount) { - throw new Error("unimplemented"); + return WireHelpers.initListPointer(this.pointer, this.segment, elementCount, elementSize); } public final ListBuilder initStructList(int elementCount, StructSize elementSize) { diff --git a/runtime/src/main/java/org/capnproto/PrimitiveList.java b/runtime/src/main/java/org/capnproto/PrimitiveList.java index d2220fe..ad18ff5 100644 --- a/runtime/src/main/java/org/capnproto/PrimitiveList.java +++ b/runtime/src/main/java/org/capnproto/PrimitiveList.java @@ -25,6 +25,11 @@ public class PrimitiveList { this.builder = builder; } + + public int size() { + return this.builder.size(); + } + } } @@ -52,6 +57,10 @@ public class PrimitiveList { this.builder = builder; } + public int size() { + return this.builder.size(); + } + } } diff --git a/runtime/src/main/java/org/capnproto/WireHelpers.java b/runtime/src/main/java/org/capnproto/WireHelpers.java index 35498e6..03c6152 100644 --- a/runtime/src/main/java/org/capnproto/WireHelpers.java +++ b/runtime/src/main/java/org/capnproto/WireHelpers.java @@ -71,7 +71,7 @@ final class WireHelpers { int elementCount, byte elementSize) { if (elementSize == FieldSize.INLINE_COMPOSITE) { - throw new DecodeException("Should have called initStructListPointer instead"); + throw new InternalError("Should have called initStructListPointer instead"); } int dataSize = FieldSize.dataBitsPerElement(elementSize); @@ -115,6 +115,65 @@ final class WireHelpers { elementSize.data * 64, elementSize.pointers); } + public static ListBuilder getWritableListPointer(int origRefOffset, + SegmentBuilder origSegment, + byte elementSize) { + if (elementSize == FieldSize.INLINE_COMPOSITE) { + throw new InternalError("Use getStructList{Element,Field} for structs"); + } + + 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. + + long ref = origRef; + SegmentBuilder segment = origSegment; + int ptr = origRefTarget; // TODO follow fars. + + if (WirePointer.kind(ref) != WirePointer.LIST) { + throw new DecodeException("Called getList{Field,Element}() but existing pointer is not a list"); + } + + byte oldSize = ListPointer.elementSize(WirePointer.listPointer(ref)); + + if (oldSize == FieldSize.INLINE_COMPOSITE) { + //# 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 { + int dataSize = FieldSize.dataBitsPerElement(oldSize); + int pointerCount = FieldSize.pointersPerElement(oldSize); + + if (dataSize < FieldSize.dataBitsPerElement(elementSize)) { + throw new DecodeException("Existing list value is incompatible with expected type."); + } + if (pointerCount < FieldSize.pointersPerElement(elementSize)) { + throw new DecodeException("Existing list value is incompatible with expected type."); + } + + int step = dataSize + pointerCount * Constants.BITS_PER_POINTER; + + return new ListBuilder(segment, ptr * Constants.BYTES_PER_WORD, + ListPointer.elementCount(WirePointer.listPointer(ref)), + step, dataSize, (short) pointerCount); + } + } + // size is in bytes public static Text.Builder initTextPointer(int refOffset, SegmentBuilder segment,