Support signed offsets
Both Structs and Lists support two's complement "Signed" offsets: > B (30 bits) = Offset, in words, from the end > of the pointer to the start of the struct's > data section. Signed. > B (30 bits) = Offset, in words, from the end > of the pointer to the start of the first > element of the list. Signed. The prior code only supported positive offsets because it used the Java >>> operator, which is an unsigned shift operator. The Java >> operator is the signed shift operator. Audited the remaining uses of the shift operator; they were correct and they are documented as such. Positive offsets are only guaranteed in a Canonical message.
This commit is contained in:
parent
c7d5354f23
commit
27ea6745e5
5 changed files with 64 additions and 1 deletions
|
@ -29,10 +29,14 @@ final class FarPointer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int positionInSegment(long ref) {
|
public static int positionInSegment(long ref) {
|
||||||
|
/* The [ref] Offset (the "C" section) is "Unsigned",
|
||||||
|
so use unsigned >>> operator. */
|
||||||
return WirePointer.offsetAndKind(ref) >>> 3;
|
return WirePointer.offsetAndKind(ref) >>> 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDoubleFar(long ref) {
|
public static boolean isDoubleFar(long ref) {
|
||||||
|
/* The [ref] Offset (the "C" section) is "Unsigned",
|
||||||
|
so use unsigned >>> operator. */
|
||||||
return ((WirePointer.offsetAndKind(ref) >>> 2) & 1) != 0;
|
return ((WirePointer.offsetAndKind(ref) >>> 2) & 1) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,9 @@ final class ListPointer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int elementCount(long ref) {
|
public static int elementCount(long ref) {
|
||||||
|
/* The [ref] List Size (the "D" section) is not specified
|
||||||
|
as Signed or Unsigned, but number of elements is inherently non-negative.
|
||||||
|
So use unsigned >>> operator. */
|
||||||
return WirePointer.upper32Bits(ref) >>> 3;
|
return WirePointer.upper32Bits(ref) >>> 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,9 @@ final class StructPointer{
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int ptrCount(long ref) {
|
public static int ptrCount(long ref) {
|
||||||
|
/* The [ref] Pointer Section Size (the "D" section) is not specified
|
||||||
|
as Signed or Unsigned, but section size is inherently non-negative.
|
||||||
|
So use unsigned >>> operator. */
|
||||||
return WirePointer.upper32Bits(ref) >>> 16;
|
return WirePointer.upper32Bits(ref) >>> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ final class WirePointer {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int target(int offset, long wirePointer) {
|
public static int target(int offset, long wirePointer) {
|
||||||
return offset + 1 + (offsetAndKind(wirePointer) >>> 2);
|
/* The [wirePointer] Offset (the "B" section) is "Signed",
|
||||||
|
so use signed >> operator. */
|
||||||
|
return offset + 1 + (offsetAndKind(wirePointer) >> 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setKindAndTarget(ByteBuffer buffer, int offset, byte kind, int targetOffset) {
|
public static void setKindAndTarget(ByteBuffer buffer, int offset, byte kind, int targetOffset) {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.capnproto;
|
package org.capnproto;
|
||||||
|
|
||||||
|
import org.capnproto.WireHelpers.FollowFarsResult;
|
||||||
import org.hamcrest.MatcherAssert;
|
import org.hamcrest.MatcherAssert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
import static org.hamcrest.CoreMatchers.is;
|
import static org.hamcrest.CoreMatchers.is;
|
||||||
|
|
||||||
|
@ -29,4 +31,53 @@ public class SegmentReaderTest {
|
||||||
SegmentReader segmentReader = new SegmentReader(byteBuffer, null);
|
SegmentReader segmentReader = new SegmentReader(byteBuffer, null);
|
||||||
MatcherAssert.assertThat(segmentReader.isInBounds(7, 2), is(false));
|
MatcherAssert.assertThat(segmentReader.isInBounds(7, 2), is(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void validSegmentWithNegativeOffsetShouldBeInBounds() {
|
||||||
|
int refOffset;
|
||||||
|
long ref;
|
||||||
|
int refTarget;
|
||||||
|
int dataSizeWords;
|
||||||
|
int wordSize;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Binary data:
|
||||||
|
echo -n
|
||||||
|
'FAAAAAEAAQDJqtK2cBpJhZ2LUEVMkYblyarStnAaSYWdi1BFTJGG4e3///8CAQAAAgAAAAAAAAD0////AAABAA=='
|
||||||
|
| base64 -d
|
||||||
|
|
||||||
|
Verify it is valid with:
|
||||||
|
capnp decode --flat dksdk_std_schema.capnp GenericReturn
|
||||||
|
where the .capnp comes from
|
||||||
|
https://gitlab.com/diskuv/dksdk-schema/-/blob/afbf9564a60f2670f6b9dfb3c423fc55dd4c3013/src/dksdk_std_schema.capnp
|
||||||
|
*/
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {
|
||||||
|
(byte)0x14, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x01, (byte)0x00,
|
||||||
|
(byte)0xC9, (byte)0xAA, (byte)0xD2, (byte)0xB6, (byte)0x70, (byte)0x1A, (byte)0x49, (byte)0x85,
|
||||||
|
(byte)0x9D, (byte)0x8B, (byte)0x50, (byte)0x45, (byte)0x4C, (byte)0x91, (byte)0x86, (byte)0xE5,
|
||||||
|
(byte)0xC9, (byte)0xAA, (byte)0xD2, (byte)0xB6, (byte)0x70, (byte)0x1A, (byte)0x49, (byte)0x85,
|
||||||
|
(byte)0x9D, (byte)0x8B, (byte)0x50, (byte)0x45, (byte)0x4C, (byte)0x91, (byte)0x86, (byte)0xE1,
|
||||||
|
(byte)0xED, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x02, (byte)0x01, (byte)0x00, (byte)0x00,
|
||||||
|
(byte)0x02, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
|
||||||
|
(byte)0xF4, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00
|
||||||
|
}).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
SegmentReader segment = new SegmentReader(byteBuffer, null);
|
||||||
|
|
||||||
|
/* Read root Struct: GenericReturn. */
|
||||||
|
refOffset = 0; /* At the root STRUCT POINTER */
|
||||||
|
ref = segment.get(refOffset);
|
||||||
|
refTarget = WirePointer.target(refOffset, ref);
|
||||||
|
dataSizeWords = StructPointer.dataSize(ref);
|
||||||
|
wordSize = dataSizeWords + StructPointer.ptrCount(ref);
|
||||||
|
MatcherAssert.assertThat(segment.isInBounds(refTarget, wordSize), is(true));
|
||||||
|
|
||||||
|
/* Read inner Struct: ComObject. */
|
||||||
|
refOffset = refTarget + dataSizeWords; /* At the inner STRUCT POINTER */
|
||||||
|
ref = segment.get(refOffset);
|
||||||
|
refTarget = WirePointer.target(refOffset, ref);
|
||||||
|
dataSizeWords = StructPointer.dataSize(ref);
|
||||||
|
wordSize = dataSizeWords + StructPointer.ptrCount(ref);
|
||||||
|
MatcherAssert.assertThat(segment.isInBounds(refTarget, wordSize), is(true));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue