Commit 95be334d authored by Kenton Varda's avatar Kenton Varda

Update implemented pointer format to match docs: Don't store field count in…

Update implemented pointer format to match docs:  Don't store field count in struct pointers because it isn't needed.  Instead extend data and pointer section sizes to 16 bits, which should be enough for anyone.  Also, store offsets from the end of the pointer rather than the beginning, because this makes it possible for them to be zero, which compresses better.  In particular, the root reference will always have a zero offset, so this will shave a byte off of very small packed messages.
parent 8ac9494c
...@@ -98,7 +98,7 @@ TEST(Encoding, DefaultInitializationMultiSegment) { ...@@ -98,7 +98,7 @@ TEST(Encoding, DefaultInitializationMultiSegment) {
} }
TEST(Encoding, DefaultsFromEmptyMessage) { TEST(Encoding, DefaultsFromEmptyMessage) {
AlignedData<1> emptyMessage = {{4, 0, 0, 0, 0, 0, 0, 0}}; AlignedData<1> emptyMessage = {{0, 0, 0, 0, 0, 0, 0, 0}};
ArrayPtr<const word> segments[1] = {arrayPtr(emptyMessage.words, 1)}; ArrayPtr<const word> segments[1] = {arrayPtr(emptyMessage.words, 1)};
SegmentArrayMessageReader reader(arrayPtr(segments, 1)); SegmentArrayMessageReader reader(arrayPtr(segments, 1));
......
...@@ -39,8 +39,8 @@ namespace { ...@@ -39,8 +39,8 @@ namespace {
TEST(WireFormat, SimpleRawDataStruct) { TEST(WireFormat, SimpleRawDataStruct) {
AlignedData<2> data = {{ AlignedData<2> data = {{
// Struct ref, offset = 1, fieldCount = 1, dataSize = 1, referenceCount = 0 // Struct ref, offset = 1, dataSize = 1, referenceCount = 0
0x04, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
// Content for the data segment. // Content for the data segment.
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
}}; }};
...@@ -81,28 +81,15 @@ TEST(WireFormat, SimpleRawDataStruct) { ...@@ -81,28 +81,15 @@ TEST(WireFormat, SimpleRawDataStruct) {
EXPECT_TRUE (reader.getDataField<bool>(63 * ELEMENTS, true )); EXPECT_TRUE (reader.getDataField<bool>(63 * ELEMENTS, true ));
EXPECT_FALSE(reader.getDataField<bool>(64 * ELEMENTS, false)); EXPECT_FALSE(reader.getDataField<bool>(64 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(64 * ELEMENTS, true )); EXPECT_TRUE (reader.getDataField<bool>(64 * ELEMENTS, true ));
// Field number guards.
EXPECT_EQ(0xefcdab89u,
reader.getDataFieldCheckingNumber<uint32_t>(FieldNumber(0), 1 * ELEMENTS, 321u));
EXPECT_EQ(321u,
reader.getDataFieldCheckingNumber<uint32_t>(FieldNumber(1), 1 * ELEMENTS, 321u));
EXPECT_TRUE (reader.getDataFieldCheckingNumber<bool>(FieldNumber(0), 0 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataFieldCheckingNumber<bool>(FieldNumber(0), 0 * ELEMENTS, true ));
EXPECT_FALSE(reader.getDataFieldCheckingNumber<bool>(FieldNumber(0), 1 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataFieldCheckingNumber<bool>(FieldNumber(0), 1 * ELEMENTS, true ));
EXPECT_FALSE(reader.getDataFieldCheckingNumber<bool>(FieldNumber(1), 0 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataFieldCheckingNumber<bool>(FieldNumber(1), 0 * ELEMENTS, true ));
} }
static const AlignedData<2> STRUCT_DEFAULT = {{8,0,0,0,16,2,4,0, 0}}; static const AlignedData<6> STRUCT_DEFAULT = {{0,0,0,0,2,0,4,0, 0}};
static const AlignedData<2> SUBSTRUCT_DEFAULT = {{8,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0}}; static const AlignedData<2> SUBSTRUCT_DEFAULT = {{0,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0}};
static const AlignedData<3> STRUCTLIST_ELEMENT_DEFAULT = static const AlignedData<3> STRUCTLIST_ELEMENT_DEFAULT =
{{8,0,0,0,1,1,1,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}}; {{0,0,0,0,1,0,1,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}};
static const AlignedData<2> STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT = static const AlignedData<2> STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT =
{{8,0,0,0,1,1,0,0, 0,0,0,0,0,0,0,0}}; {{0,0,0,0,1,0,0,0, 0,0,0,0,0,0,0,0}};
static void setupStruct(StructBuilder builder) { static void setupStruct(StructBuilder builder) {
builder.setDataField<uint64_t>(0 * ELEMENTS, 0x1011121314151617ull); builder.setDataField<uint64_t>(0 * ELEMENTS, 0x1011121314151617ull);
......
...@@ -45,7 +45,9 @@ struct WireReference { ...@@ -45,7 +45,9 @@ struct WireReference {
// //
// Actually this is not terribly common. The "offset" could actually be different things // Actually this is not terribly common. The "offset" could actually be different things
// depending on the context: // depending on the context:
// - For a regular (e.g. struct/list) reference, a signed word offset from the reference pointer. // - For a regular (e.g. struct/list) reference, a signed word offset from the word immediately
// following the reference pointer. (The off-by-one means the offset is more often zero, saving
// bytes on the wire when packed.)
// - For an inline composite list tag (not really a reference, but structured similarly), an // - For an inline composite list tag (not really a reference, but structured similarly), an
// element count. // element count.
// - For a FAR reference, an unsigned offset into the target segment. // - For a FAR reference, an unsigned offset into the target segment.
...@@ -70,19 +72,19 @@ struct WireReference { ...@@ -70,19 +72,19 @@ struct WireReference {
WireValue<uint32_t> offsetAndKind; WireValue<uint32_t> offsetAndKind;
CAPNPROTO_ALWAYS_INLINE(bool isNull() const) { return offsetAndKind.get() == 0; }
CAPNPROTO_ALWAYS_INLINE(Kind kind() const) { CAPNPROTO_ALWAYS_INLINE(Kind kind() const) {
return static_cast<Kind>(offsetAndKind.get() & 3); return static_cast<Kind>(offsetAndKind.get() & 3);
} }
CAPNPROTO_ALWAYS_INLINE(word* target()) { CAPNPROTO_ALWAYS_INLINE(word* target()) {
return reinterpret_cast<word*>(this) + (static_cast<int32_t>(offsetAndKind.get()) >> 2); return reinterpret_cast<word*>(this) + 1 + (static_cast<int32_t>(offsetAndKind.get()) >> 2);
} }
CAPNPROTO_ALWAYS_INLINE(const word* target() const) { CAPNPROTO_ALWAYS_INLINE(const word* target() const) {
return reinterpret_cast<const word*>(this) + (static_cast<int32_t>(offsetAndKind.get()) >> 2); return reinterpret_cast<const word*>(this) + 1 +
(static_cast<int32_t>(offsetAndKind.get()) >> 2);
} }
CAPNPROTO_ALWAYS_INLINE(void setKindAndTarget(Kind kind, word* target)) { CAPNPROTO_ALWAYS_INLINE(void setKindAndTarget(Kind kind, word* target)) {
offsetAndKind.set(((target - reinterpret_cast<word*>(this)) << 2) | kind); offsetAndKind.set(((target - reinterpret_cast<word*>(this) - 1) << 2) | kind);
} }
CAPNPROTO_ALWAYS_INLINE(ElementCount inlineCompositeListElementCount() const) { CAPNPROTO_ALWAYS_INLINE(ElementCount inlineCompositeListElementCount() const) {
...@@ -113,21 +115,19 @@ struct WireReference { ...@@ -113,21 +115,19 @@ struct WireReference {
// Part of reference that depends on the kind. // Part of reference that depends on the kind.
union { union {
uint32_t upper32Bits;
struct { struct {
WireValue<FieldNumber> fieldCount; WireValue<WordCount16> dataSize;
WireValue<WordCount8> dataSize; WireValue<WireReferenceCount16> refCount;
WireValue<WireReferenceCount8> refCount;
WireValue<uint8_t> reserved0;
inline WordCount wordSize() const { inline WordCount wordSize() const {
return dataSize.get() + refCount.get() * WORDS_PER_REFERENCE; return dataSize.get() + refCount.get() * WORDS_PER_REFERENCE;
} }
CAPNPROTO_ALWAYS_INLINE(void set(FieldNumber fc, WordCount ds, WireReferenceCount rc)) { CAPNPROTO_ALWAYS_INLINE(void set(WordCount ds, WireReferenceCount rc)) {
fieldCount.set(fc);
dataSize.set(ds); dataSize.set(ds);
refCount.set(rc); refCount.set(rc);
reserved0.set(0);
} }
} structRef; } structRef;
// Also covers capabilities. // Also covers capabilities.
...@@ -167,6 +167,14 @@ struct WireReference { ...@@ -167,6 +167,14 @@ struct WireReference {
} }
} farRef; } farRef;
}; };
CAPNPROTO_ALWAYS_INLINE(bool isNull() const) {
// If the upper 32 bits are zero, this is a pointer to an empty struct. We consider that to be
// our "null" value.
// TODO: Maybe this would be faster, but violates aliasing rules; does it matter?:
// return *reinterpret_cast<const uint64_t*>(this) == 0;
return (offsetAndKind.get() == 0) & (upper32Bits == 0);
}
}; };
static_assert(sizeof(WireReference) == sizeof(word), static_assert(sizeof(WireReference) == sizeof(word),
"capnproto::WireReference is not exactly one word. This will probably break everything."); "capnproto::WireReference is not exactly one word. This will probably break everything.");
...@@ -314,8 +322,7 @@ struct WireHelpers { ...@@ -314,8 +322,7 @@ struct WireHelpers {
copyStruct(segment, dstPtr, srcPtr, src->structRef.dataSize.get(), copyStruct(segment, dstPtr, srcPtr, src->structRef.dataSize.get(),
src->structRef.refCount.get()); src->structRef.refCount.get());
dst->structRef.set(src->structRef.fieldCount.get(), src->structRef.dataSize.get(), dst->structRef.set(src->structRef.dataSize.get(), src->structRef.refCount.get());
src->structRef.refCount.get());
return dstPtr; return dstPtr;
} }
} }
...@@ -408,8 +415,7 @@ struct WireHelpers { ...@@ -408,8 +415,7 @@ struct WireHelpers {
defaultRef->structRef.dataSize.get() * BYTES_PER_WORD / BYTES); defaultRef->structRef.dataSize.get() * BYTES_PER_WORD / BYTES);
// Initialize the reference. // Initialize the reference.
ref->structRef.set(defaultRef->structRef.fieldCount.get(), defaultRef->structRef.dataSize.get(), ref->structRef.set(defaultRef->structRef.dataSize.get(), defaultRef->structRef.refCount.get());
defaultRef->structRef.refCount.get());
// Build the StructBuilder. // Build the StructBuilder.
return StructBuilder(segment, ptr, return StructBuilder(segment, ptr,
...@@ -428,9 +434,6 @@ struct WireHelpers { ...@@ -428,9 +434,6 @@ struct WireHelpers {
CAPNPROTO_DEBUG_ASSERT(ref->kind() == WireReference::STRUCT, CAPNPROTO_DEBUG_ASSERT(ref->kind() == WireReference::STRUCT,
"Called getStruct{Field,Element}() but existing reference is not a struct."); "Called getStruct{Field,Element}() but existing reference is not a struct.");
CAPNPROTO_DEBUG_ASSERT(
ref->structRef.fieldCount.get() == defaultRef->structRef.fieldCount.get(),
"Trying to update struct with incorrect field count.");
CAPNPROTO_DEBUG_ASSERT( CAPNPROTO_DEBUG_ASSERT(
ref->structRef.dataSize.get() == defaultRef->structRef.dataSize.get(), ref->structRef.dataSize.get() == defaultRef->structRef.dataSize.get(),
"Trying to update struct with incorrect data size."); "Trying to update struct with incorrect data size.");
...@@ -482,7 +485,6 @@ struct WireHelpers { ...@@ -482,7 +485,6 @@ struct WireHelpers {
reinterpret_cast<WireReference*>(ptr)->setKindAndInlineCompositeListElementCount( reinterpret_cast<WireReference*>(ptr)->setKindAndInlineCompositeListElementCount(
WireReference::STRUCT, elementCount); WireReference::STRUCT, elementCount);
reinterpret_cast<WireReference*>(ptr)->structRef.set( reinterpret_cast<WireReference*>(ptr)->structRef.set(
defaultRef->structRef.fieldCount.get(),
defaultRef->structRef.dataSize.get(), defaultRef->structRef.dataSize.get(),
defaultRef->structRef.refCount.get()); defaultRef->structRef.refCount.get());
ptr += REFERENCE_SIZE_IN_WORDS; ptr += REFERENCE_SIZE_IN_WORDS;
...@@ -651,7 +653,6 @@ struct WireHelpers { ...@@ -651,7 +653,6 @@ struct WireHelpers {
return StructReader( return StructReader(
segment, ptr, reinterpret_cast<const WireReference*>(ptr + ref->structRef.dataSize.get()), segment, ptr, reinterpret_cast<const WireReference*>(ptr + ref->structRef.dataSize.get()),
ref->structRef.fieldCount.get(),
ref->structRef.dataSize.get(), ref->structRef.dataSize.get(),
ref->structRef.refCount.get(), ref->structRef.refCount.get(),
0 * BITS, nestingLimit - 1); 0 * BITS, nestingLimit - 1);
...@@ -776,10 +777,7 @@ struct WireHelpers { ...@@ -776,10 +777,7 @@ struct WireHelpers {
} }
return ListReader(segment, ptr, size, wordsPerElement * BITS_PER_WORD, return ListReader(segment, ptr, size, wordsPerElement * BITS_PER_WORD,
tag->structRef.fieldCount.get(), tag->structRef.dataSize.get(), tag->structRef.refCount.get(), nestingLimit - 1);
tag->structRef.dataSize.get(),
tag->structRef.refCount.get(),
nestingLimit - 1);
} else { } else {
// The elements of the list are NOT structs. // The elements of the list are NOT structs.
...@@ -830,7 +828,7 @@ struct WireHelpers { ...@@ -830,7 +828,7 @@ struct WireHelpers {
break; break;
} }
return ListReader(segment, ptr, ref->listRef.elementCount(), step, FieldNumber(1), return ListReader(segment, ptr, ref->listRef.elementCount(), step,
dataSize, referenceCount, nestingLimit - 1); dataSize, referenceCount, nestingLimit - 1);
} else { } else {
CAPNPROTO_ASSERT(segment != nullptr, "Trusted message had incompatible list element type."); CAPNPROTO_ASSERT(segment != nullptr, "Trusted message had incompatible list element type.");
...@@ -1008,17 +1006,14 @@ Data::Builder StructBuilder::getDataField( ...@@ -1008,17 +1006,14 @@ Data::Builder StructBuilder::getDataField(
} }
StructReader StructBuilder::asReader() const { StructReader StructBuilder::asReader() const {
// HACK: We just give maxed-out field, data size, and reference counts because they are only // HACK: We just give maxed-out data size and reference counts because they are only
// used for checking for field presence. // used for checking for field presence.
static_assert(sizeof(WireReference::structRef.fieldCount) == 1, static_assert(sizeof(WireReference::structRef.dataSize) == 2,
"Has the maximum field count changed?");
static_assert(sizeof(WireReference::structRef.dataSize) == 1,
"Has the maximum data size changed?"); "Has the maximum data size changed?");
static_assert(sizeof(WireReference::structRef.refCount) == 1, static_assert(sizeof(WireReference::structRef.refCount) == 2,
"Has the maximum reference count changed?"); "Has the maximum reference count changed?");
return StructReader(segment, data, references, return StructReader(segment, data, references,
FieldNumber(0xff), 0xff * WORDS, 0xff * REFERENCES, 0xffff * WORDS, 0xffff * REFERENCES, 0 * BITS, std::numeric_limits<int>::max());
0 * BITS, std::numeric_limits<int>::max());
} }
StructReader StructReader::readRootTrusted(const word* location, const word* defaultValue) { StructReader StructReader::readRootTrusted(const word* location, const word* defaultValue) {
...@@ -1122,11 +1117,10 @@ ListReader ListBuilder::asReader(FieldSize elementSize) const { ...@@ -1122,11 +1117,10 @@ ListReader ListBuilder::asReader(FieldSize elementSize) const {
std::numeric_limits<int>::max()); std::numeric_limits<int>::max());
} }
ListReader ListBuilder::asReader(FieldNumber fieldCount, WordCount dataSize, ListReader ListBuilder::asReader(WordCount dataSize, WireReferenceCount referenceCount) const {
WireReferenceCount referenceCount) const {
return ListReader(segment, ptr, elementCount, return ListReader(segment, ptr, elementCount,
(dataSize + referenceCount * WORDS_PER_REFERENCE) * BITS_PER_WORD / ELEMENTS, (dataSize + referenceCount * WORDS_PER_REFERENCE) * BITS_PER_WORD / ELEMENTS,
fieldCount, dataSize, referenceCount, std::numeric_limits<int>::max()); dataSize, referenceCount, std::numeric_limits<int>::max());
} }
StructReader ListReader::getStructElement(ElementCount index, const word* defaultValue) const { StructReader ListReader::getStructElement(ElementCount index, const word* defaultValue) const {
...@@ -1140,8 +1134,7 @@ StructReader ListReader::getStructElement(ElementCount index, const word* defaul ...@@ -1140,8 +1134,7 @@ StructReader ListReader::getStructElement(ElementCount index, const word* defaul
return StructReader( return StructReader(
segment, structPtr, segment, structPtr,
reinterpret_cast<const WireReference*>(structPtr + structDataSize * BYTES_PER_WORD), reinterpret_cast<const WireReference*>(structPtr + structDataSize * BYTES_PER_WORD),
structFieldCount, structDataSize, structReferenceCount, indexBit % BITS_PER_BYTE, structDataSize, structReferenceCount, indexBit % BITS_PER_BYTE, nestingLimit - 1);
nestingLimit - 1);
} }
} }
......
...@@ -231,7 +231,7 @@ private: ...@@ -231,7 +231,7 @@ private:
class StructReader { class StructReader {
public: public:
inline StructReader() inline StructReader()
: segment(nullptr), data(nullptr), references(nullptr), fieldCount(0), dataSize(0), : segment(nullptr), data(nullptr), references(nullptr), dataSize(0),
referenceCount(0), bit0Offset(0 * BITS), nestingLimit(0) {} referenceCount(0), bit0Offset(0 * BITS), nestingLimit(0) {}
static StructReader readRootTrusted(const word* location, const word* defaultValue); static StructReader readRootTrusted(const word* location, const word* defaultValue);
...@@ -245,14 +245,6 @@ public: ...@@ -245,14 +245,6 @@ public:
// multiples of the field size, determined by the type. Returns the default value if the offset // multiples of the field size, determined by the type. Returns the default value if the offset
// is past the end of the struct's data segment. // is past the end of the struct's data segment.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataFieldCheckingNumber(
FieldNumber fieldNumber, ElementCount offset, typename NoInfer<T>::Type defaultValue) const);
// Like getDataField() but also returns the default if the field number not less than the field
// count. This is needed in cases where the field was packed into a hole preceding other fields
// with later numbers, and therefore the offset being in-bounds alone does not prove that the
// struct contains the field.
StructReader getStructField(WireReferenceCount refIndex, const word* defaultValue) const; StructReader getStructField(WireReferenceCount refIndex, const word* defaultValue) const;
// Get the struct field at the given index in the reference segment, or the default value if not // Get the struct field at the given index in the reference segment, or the default value if not
// initialized. defaultValue will be interpreted as a trusted message -- it must point at a // initialized. defaultValue will be interpreted as a trusted message -- it must point at a
...@@ -277,7 +269,6 @@ private: ...@@ -277,7 +269,6 @@ private:
const void* data; const void* data;
const WireReference* references; const WireReference* references;
FieldNumber fieldCount; // Number of fields the struct is reported to have.
WordCount8 dataSize; // Size of data segment. WordCount8 dataSize; // Size of data segment.
WireReferenceCount8 referenceCount; // Size of the reference segment. WireReferenceCount8 referenceCount; // Size of the reference segment.
...@@ -291,9 +282,9 @@ private: ...@@ -291,9 +282,9 @@ private:
// Once this reaches zero, further pointers will be pruned. // Once this reaches zero, further pointers will be pruned.
inline StructReader(SegmentReader* segment, const void* data, const WireReference* references, inline StructReader(SegmentReader* segment, const void* data, const WireReference* references,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount, WordCount dataSize, WireReferenceCount referenceCount,
BitCount bit0Offset, int nestingLimit) BitCount bit0Offset, int nestingLimit)
: segment(segment), data(data), references(references), fieldCount(fieldCount), : segment(segment), data(data), references(references),
dataSize(dataSize), referenceCount(referenceCount), bit0Offset(bit0Offset), dataSize(dataSize), referenceCount(referenceCount), bit0Offset(bit0Offset),
nestingLimit(nestingLimit) {} nestingLimit(nestingLimit) {}
...@@ -358,8 +349,7 @@ public: ...@@ -358,8 +349,7 @@ public:
ListReader asReader(FieldSize elementSize) const; ListReader asReader(FieldSize elementSize) const;
// Get a ListReader pointing at the same memory. Use this version only for non-struct lists. // Get a ListReader pointing at the same memory. Use this version only for non-struct lists.
ListReader asReader(FieldNumber fieldCount, WordCount dataSize, ListReader asReader(WordCount dataSize, WireReferenceCount referenceCount) const;
WireReferenceCount referenceCount) const;
// Get a ListReader pointing at the same memory. Use this version only for struct lists. // Get a ListReader pointing at the same memory. Use this version only for struct lists.
private: private:
...@@ -378,7 +368,7 @@ class ListReader { ...@@ -378,7 +368,7 @@ class ListReader {
public: public:
inline ListReader() inline ListReader()
: segment(nullptr), ptr(nullptr), elementCount(0), : segment(nullptr), ptr(nullptr), elementCount(0),
stepBits(0 * BITS / ELEMENTS), structFieldCount(0), structDataSize(0), stepBits(0 * BITS / ELEMENTS), structDataSize(0),
structReferenceCount(0), nestingLimit(0) {} structReferenceCount(0), nestingLimit(0) {}
inline ElementCount size(); inline ElementCount size();
...@@ -414,10 +404,9 @@ private: ...@@ -414,10 +404,9 @@ private:
// if the sender upgraded a data list to a struct list. It will always be aligned properly for // if the sender upgraded a data list to a struct list. It will always be aligned properly for
// the type. Unsigned so that division by a constant power of 2 is efficient. // the type. Unsigned so that division by a constant power of 2 is efficient.
FieldNumber structFieldCount;
WordCount structDataSize; WordCount structDataSize;
WireReferenceCount structReferenceCount; WireReferenceCount structReferenceCount;
// If the elements are structs, the properties of the struct. The field and reference counts are // If the elements are structs, the properties of the struct. The reference count is
// only used to check for field presence; the data size is also used to compute the reference // only used to check for field presence; the data size is also used to compute the reference
// pointer. // pointer.
...@@ -428,15 +417,14 @@ private: ...@@ -428,15 +417,14 @@ private:
inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount, inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount,
decltype(BITS / ELEMENTS) stepBits, int nestingLimit) decltype(BITS / ELEMENTS) stepBits, int nestingLimit)
: segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits), : segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits),
structFieldCount(0), structDataSize(0), structReferenceCount(0), structDataSize(0), structReferenceCount(0),
nestingLimit(nestingLimit) {} nestingLimit(nestingLimit) {}
inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount, inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount,
decltype(BITS / ELEMENTS) stepBits, decltype(BITS / ELEMENTS) stepBits, WordCount structDataSize,
FieldNumber structFieldCount, WordCount structDataSize,
WireReferenceCount structReferenceCount, int nestingLimit) WireReferenceCount structReferenceCount, int nestingLimit)
: segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits), : segment(segment), ptr(ptr), elementCount(elementCount), stepBits(stepBits),
structFieldCount(structFieldCount), structDataSize(structDataSize), structDataSize(structDataSize), structReferenceCount(structReferenceCount),
structReferenceCount(structReferenceCount), nestingLimit(nestingLimit) {} nestingLimit(nestingLimit) {}
friend class StructReader; friend class StructReader;
friend class ListBuilder; friend class ListBuilder;
...@@ -512,41 +500,6 @@ inline Void StructReader::getDataField<Void>(ElementCount offset, Void defaultVa ...@@ -512,41 +500,6 @@ inline Void StructReader::getDataField<Void>(ElementCount offset, Void defaultVa
return Void::VOID; return Void::VOID;
} }
template <typename T>
T StructReader::getDataFieldCheckingNumber(
FieldNumber fieldNumber, ElementCount offset, typename NoInfer<T>::Type defaultValue) const {
// Intentionally use & rather than && to reduce branches.
if ((fieldNumber < fieldCount) &
(offset * bytesPerElement<T>() < dataSize * BYTES_PER_WORD)) {
return reinterpret_cast<const WireValue<T>*>(data)[offset / ELEMENTS].get();
} else {
return defaultValue;
}
}
template <>
inline bool StructReader::getDataFieldCheckingNumber<bool>(
FieldNumber fieldNumber, ElementCount offset, bool defaultValue) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS);
// This branch should always be optimized away when inlining.
if (boffset == 0 * BITS) boffset = bit0Offset;
// Intentionally use & rather than && to reduce branches.
if ((fieldNumber < fieldCount) & (boffset < dataSize * BITS_PER_WORD)) {
const byte* b = reinterpret_cast<const byte*>(data) + boffset / BITS_PER_BYTE;
return (*reinterpret_cast<const uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
} else {
return defaultValue;
}
}
template <>
inline Void StructReader::getDataFieldCheckingNumber<Void>(
FieldNumber fieldNumber, ElementCount offset, Void defaultValue) const {
return Void::VOID;
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
inline ElementCount ListBuilder::size() { return elementCount; } inline ElementCount ListBuilder::size() { return elementCount; }
......
...@@ -388,8 +388,10 @@ TEST(Packed, RoundTripAllZero) { ...@@ -388,8 +388,10 @@ TEST(Packed, RoundTripAllZero) {
PackedMessageReader reader(pipe); PackedMessageReader reader(pipe);
checkTestMessageAllZero(reader.getRoot<TestAllTypes>()); checkTestMessageAllZero(reader.getRoot<TestAllTypes>());
// Most of the bytes are the segment table and root reference. // Segment table packs to 2 bytes.
EXPECT_LE(pipe.getData().size(), 9u); // Root reference packs to 3 bytes.
// Content packs to 2 bytes (zero span).
EXPECT_LE(pipe.getData().size(), 7u);
} }
TEST(Packed, RoundTripAllZeroScratchSpace) { TEST(Packed, RoundTripAllZeroScratchSpace) {
......
...@@ -100,6 +100,9 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) { ...@@ -100,6 +100,9 @@ Array<word> messageToFlatArray(ArrayPtr<const ArrayPtr<const word>> segments) {
internal::WireValue<uint32_t>* table = internal::WireValue<uint32_t>* table =
reinterpret_cast<internal::WireValue<uint32_t>*>(result.begin()); reinterpret_cast<internal::WireValue<uint32_t>*>(result.begin());
// We write the segment count - 1 because this makes the first word zero for single-segment
// messages, improving compression. We don't bother doing this with segment sizes because
// one-word segments are rare anyway.
table[0].set(segments.size() - 1); table[0].set(segments.size() - 1);
for (uint i = 0; i < segments.size(); i++) { for (uint i = 0; i < segments.size(); i++) {
...@@ -219,6 +222,9 @@ void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> seg ...@@ -219,6 +222,9 @@ void writeMessage(OutputStream& output, ArrayPtr<const ArrayPtr<const word>> seg
internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)]; internal::WireValue<uint32_t> table[(segments.size() + 2) & ~size_t(1)];
// We write the segment count - 1 because this makes the first word zero for single-segment
// messages, improving compression. We don't bother doing this with segment sizes because
// one-word segments are rare anyway.
table[0].set(segments.size() - 1); table[0].set(segments.size() - 1);
for (uint i = 0; i < segments.size(); i++) { for (uint i = 0; i < segments.size(); i++) {
table[i + 1].set(segments[i].size()); table[i + 1].set(segments[i].size());
......
...@@ -93,7 +93,7 @@ encodeData size = loop 0 where ...@@ -93,7 +93,7 @@ encodeData size = loop 0 where
popBits _ rest = ([], rest) popBits _ rest = ([], rest)
encodeReferences :: Integer -> Integer -> [(Integer, TypeDesc, ValueDesc)] -> ([Word8], [Word8]) encodeReferences :: Integer -> Integer -> [(Integer, TypeDesc, ValueDesc)] -> ([Word8], [Word8])
encodeReferences o size = loop 0 (o + size) where encodeReferences o size = loop 0 (o + size - 1) where
loop idx offset ((pos, t, v):rest) | idx == pos = let loop idx offset ((pos, t, v):rest) | idx == pos = let
(ref, obj) = case (t, v) of (ref, obj) = case (t, v) of
(StructType desc, StructValueDesc assignments) -> let (StructType desc, StructValueDesc assignments) -> let
...@@ -134,10 +134,8 @@ encodeStructList o desc elements = loop (o + eSize * genericLength elements) ele ...@@ -134,10 +134,8 @@ encodeStructList o desc elements = loop (o + eSize * genericLength elements) ele
encodeStructReference desc offset = encodeStructReference desc offset =
bytes (offset * 4 + structTag) 4 ++ bytes (offset * 4 + structTag) 4 ++
[ fromIntegral (length (structFields desc) + length (structUnions desc)) bytes (packingDataSize $ structPacking desc) 2 ++
, fromIntegral $ packingDataSize $ structPacking desc bytes (packingReferenceCount $ structPacking desc) 2
, fromIntegral $ packingReferenceCount $ structPacking desc
, 0 ]
encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset = encodeListReference elemSize@(SizeInlineComposite ds rc) elementCount offset =
bytes (offset * 4 + listTag) 4 ++ bytes (offset * 4 + listTag) 4 ++
...@@ -234,8 +232,8 @@ encodeList elementType elements = case elementSize elementType of ...@@ -234,8 +232,8 @@ encodeList elementType elements = case elementSize elementType of
encodeMessage (StructType desc) (StructValueDesc assignments) = let encodeMessage (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0 (dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
in concat [encodeStructReference desc (1::Integer), dataBytes, refBytes, childBytes] in concat [encodeStructReference desc (0::Integer), dataBytes, refBytes, childBytes]
encodeMessage (ListType elementType) (ListDesc elements) = encodeMessage (ListType elementType) (ListDesc elements) =
encodeListReference (elementSize elementType) (genericLength elements) (1::Integer) ++ encodeListReference (elementSize elementType) (genericLength elements) (0::Integer) ++
encodeList elementType elements encodeList elementType elements
encodeMessage _ _ = error "Not a message." encodeMessage _ _ = error "Not a message."
...@@ -77,7 +77,7 @@ A list value is encoded as a pointer to a flat array of values. ...@@ -77,7 +77,7 @@ A list value is encoded as a pointer to a flat array of values.
+-+-----------------------------+--+----------------------------+ +-+-----------------------------+--+----------------------------+
A (2 bits) = 1, to indicate that this is a list pointer. A (2 bits) = 1, to indicate that this is a list pointer.
B (30 bits) = Offset, in words, from the start of the pointer to the B (30 bits) = Offset, in words, from the end of the pointer to the
start of the first element of the list. Signed. start of the first element of the list. Signed.
C (3 bits) = Size of each element: C (3 bits) = Size of each element:
0 = 0 (e.g. List(Void)) 0 = 0 (e.g. List(Void))
...@@ -123,7 +123,7 @@ A struct pointer looks like this: ...@@ -123,7 +123,7 @@ A struct pointer looks like this:
+-+-----------------------------+---------------+---------------+ +-+-----------------------------+---------------+---------------+
A (2 bits) = 0, to indicate that this is a struct pointer. A (2 bits) = 0, to indicate that this is a struct pointer.
B (30 bits) = Offset, in words, from the start of the pointer to the B (30 bits) = Offset, in words, from the end of the pointer to the
start of the struct's data section. Signed. start of the struct's data section. Signed.
C (16 bits) = Size of the struct's data section, in words. C (16 bits) = Size of the struct's data section, in words.
D (16 bits) = Size of the struct's pointer section, in words. D (16 bits) = Size of the struct's pointer section, in words.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment