From c6762ff0f763bd91fbdad21cdc1187b3c74520a8 Mon Sep 17 00:00:00 2001 From: David Renshaw Date: Sat, 23 Mar 2019 10:32:01 -0400 Subject: [PATCH] implement StructList.Builder.setWithCaveats() --- .../scala/org/capnproto/EncodingSuite.scala | 24 ++++++++ .../java/org/capnproto/StructBuilder.java | 59 +++++++++++++++++++ .../main/java/org/capnproto/StructList.java | 16 ++++- 3 files changed, 98 insertions(+), 1 deletion(-) diff --git a/compiler/src/test/scala/org/capnproto/EncodingSuite.scala b/compiler/src/test/scala/org/capnproto/EncodingSuite.scala index 494a45e..42b6cc3 100644 --- a/compiler/src/test/scala/org/capnproto/EncodingSuite.scala +++ b/compiler/src/test/scala/org/capnproto/EncodingSuite.scala @@ -817,6 +817,30 @@ class EncodingSuite extends FunSuite { } } + test("setWithCaveats") { + val builder = new MessageBuilder() + val root = builder.initRoot(TestAllTypes.factory) + val list = root.initStructList(2) + + { + val message1 = new MessageBuilder() + val root1 = message1.initRoot(TestAllTypes.factory) + root1.setInt8Field(11) + list.setWithCaveats(TestAllTypes.factory, 0, root1.asReader()) + } + + { + val message2 = new MessageBuilder() + val root2 = message2.initRoot(TestAllTypes.factory) + TestUtil.initTestMessage(root2) + list.setWithCaveats(TestAllTypes.factory, 1, root2.asReader()) + } + + val listReader = list.asReader(TestAllTypes.factory) + listReader.get(0).getInt8Field() should equal (11) + TestUtil.checkTestMessage(listReader.get(1)) + } + // to debug, do this: //Serialize.write((new java.io.FileOutputStream("/Users/dwrensha/Desktop/test.dat")).getChannel(), // message) diff --git a/runtime/src/main/java/org/capnproto/StructBuilder.java b/runtime/src/main/java/org/capnproto/StructBuilder.java index caa3e6a..59f921e 100644 --- a/runtime/src/main/java/org/capnproto/StructBuilder.java +++ b/runtime/src/main/java/org/capnproto/StructBuilder.java @@ -197,4 +197,63 @@ public class StructBuilder { protected final void _setPointerField(SetPointerBuilder factory, int index, Reader value) { factory.setPointerBuilder(this.segment, this.pointers + index, value); } + + protected final void _copyContentFrom(StructReader other) { + // Determine the amount of data the builders have in common. + int sharedDataSize = java.lang.Math.min(this.dataSize, other.dataSize); + int sharedPointerCount = java.lang.Math.min(this.pointerCount, other.pointerCount); + + if (other.segment == this.segment && + ((sharedDataSize > 0 && other.data == this.data) || + (sharedPointerCount > 0 && other.pointers == this.pointers))) { + // At least one of the section pointers is pointing to ourself. Verify that the other is too + // (but ignore empty sections). + if ((sharedDataSize == 0 || other.data == this.data) && + (sharedPointerCount == 0 || other.pointers == this.pointers)) { + throw new Error("Only one of the section pointers is pointing to ourself"); + } + + // So `other` appears to be a reader for this same struct. No copying is needed. + return; + } + + if (this.dataSize > sharedDataSize) { + // Since the target is larger than the source, make sure to zero out the extra bits that the + // source doesn't have. + if (this.dataSize == 1) { + this._setBooleanField(0, false); + } else { + int unshared = this.data + sharedDataSize / Constants.BITS_PER_BYTE; + WireHelpers.memset(this.segment.buffer, + unshared, + (byte)0, + (this.dataSize - sharedDataSize) / Constants.BITS_PER_BYTE); + } + } + + // Copy over the shared part. + if (sharedDataSize == 1) { + this._setBooleanField(0, other._getBooleanField(0)); + } else { + WireHelpers.memcpy(this.segment.buffer, + this.data, + other.segment.buffer, + other.data, + sharedDataSize / Constants.BITS_PER_BYTE); + } + + // Zero out all pointers in the target. + for (int ii = 0; ii < this.pointerCount; ++ii) { + WireHelpers.zeroObject(this.segment, this.pointers + ii); + } + this.segment.buffer.putLong(this.pointers * Constants.BYTES_PER_WORD, 0); + + for (int ii = 0; ii < sharedPointerCount; ++ii) { + WireHelpers.copyPointer(this.segment, + this.pointers + ii, + other.segment, + other.pointers + ii, + other.nestingLimit); + } + } } diff --git a/runtime/src/main/java/org/capnproto/StructList.java b/runtime/src/main/java/org/capnproto/StructList.java index 32c1bc1..b14e7f8 100644 --- a/runtime/src/main/java/org/capnproto/StructList.java +++ b/runtime/src/main/java/org/capnproto/StructList.java @@ -118,7 +118,7 @@ public final class StructList { } } - public static final class Builder extends ListBuilder implements Iterable { + public static final class Builder extends ListBuilder implements Iterable { public final StructBuilder.Factory factory; public Builder(StructBuilder.Factory factory, @@ -134,6 +134,20 @@ public final class StructList { } // TODO: rework generics so that we don't need this factory parameter + public final void setWithCaveats(StructFactory factory, + int index, + U value) { + this._getStructElement(this.factory, index)._copyContentFrom(value); + } + + /** + * Sets the list element, with the following limitation based on the fact that structs in a + * struct list are allocated inline: if the source struct is larger than the target struct + * (as can happen if it was created with a newer version of the schema), then it will be + * truncated, losing fields. + * + * TODO: rework generics, so that we don't need this factory parameter + */ public final Reader asReader(StructFactory factory) { return new Reader(factory, this.segment, this.ptr, this.elementCount, this.step,