Commit f966fc41 authored by Kenton Varda's avatar Kenton Varda

Store primitive fields XOR'd with their defaults.

parent f3c4121b
......@@ -45,46 +45,58 @@ TEST(WireFormat, SimpleRawDataStruct) {
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
}};
StructReader reader = StructReader::readRootTrusted(data.words, data.words);
EXPECT_EQ(0xefcdab8967452301ull, reader.getDataField<uint64_t>(0 * ELEMENTS, 321u));
StructReader reader = StructReader::readRootTrusted(data.words);
EXPECT_EQ(0xefcdab8967452301ull, reader.getDataField<uint64_t>(0 * ELEMENTS));
EXPECT_EQ(0u, reader.getDataField<uint64_t>(1 * ELEMENTS));
EXPECT_EQ(0x67452301u, reader.getDataField<uint32_t>(0 * ELEMENTS));
EXPECT_EQ(0xefcdab89u, reader.getDataField<uint32_t>(1 * ELEMENTS));
EXPECT_EQ(0u, reader.getDataField<uint32_t>(2 * ELEMENTS));
EXPECT_EQ(0x2301u, reader.getDataField<uint16_t>(0 * ELEMENTS));
EXPECT_EQ(0x6745u, reader.getDataField<uint16_t>(1 * ELEMENTS));
EXPECT_EQ(0xab89u, reader.getDataField<uint16_t>(2 * ELEMENTS));
EXPECT_EQ(0xefcdu, reader.getDataField<uint16_t>(3 * ELEMENTS));
EXPECT_EQ(0u, reader.getDataField<uint16_t>(4 * ELEMENTS));
EXPECT_EQ(321u ^ 0xefcdab8967452301ull, reader.getDataField<uint64_t>(0 * ELEMENTS, 321u));
EXPECT_EQ(321u ^ 0x67452301u, reader.getDataField<uint32_t>(0 * ELEMENTS, 321u));
EXPECT_EQ(321u ^ 0x2301u, reader.getDataField<uint16_t>(0 * ELEMENTS, 321u));
EXPECT_EQ(321u, reader.getDataField<uint64_t>(1 * ELEMENTS, 321u));
EXPECT_EQ(0x67452301u, reader.getDataField<uint32_t>(0 * ELEMENTS, 321u));
EXPECT_EQ(0xefcdab89u, reader.getDataField<uint32_t>(1 * ELEMENTS, 321u));
EXPECT_EQ(321u, reader.getDataField<uint32_t>(2 * ELEMENTS, 321u));
EXPECT_EQ(0x2301u, reader.getDataField<uint16_t>(0 * ELEMENTS, 321u));
EXPECT_EQ(0x6745u, reader.getDataField<uint16_t>(1 * ELEMENTS, 321u));
EXPECT_EQ(0xab89u, reader.getDataField<uint16_t>(2 * ELEMENTS, 321u));
EXPECT_EQ(0xefcdu, reader.getDataField<uint16_t>(3 * ELEMENTS, 321u));
EXPECT_EQ(321u, reader.getDataField<uint16_t>(4 * ELEMENTS, 321u));
// Bits
EXPECT_TRUE (reader.getDataField<bool>(0 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(1 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(2 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(3 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(4 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(5 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(6 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(7 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>( 8 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>( 9 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(10 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(11 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(12 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(13 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(14 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(15 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(63 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(64 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(0 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(1 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(2 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(3 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(4 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(5 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(6 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(7 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>( 8 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>( 9 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(10 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(11 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(12 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(13 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(14 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(15 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(63 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(63 * ELEMENTS, true ));
EXPECT_FALSE(reader.getDataField<bool>(64 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(64 * ELEMENTS, true ));
EXPECT_FALSE(reader.getDataField<bool>(0 * ELEMENTS, true));
EXPECT_TRUE (reader.getDataField<bool>(1 * ELEMENTS, true));
EXPECT_FALSE(reader.getDataField<bool>(63 * ELEMENTS, true));
EXPECT_TRUE (reader.getDataField<bool>(64 * ELEMENTS, true));
}
static const AlignedData<6> STRUCT_DEFAULT = {{0,0,0,0,2,0,4,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 =
{{0,0,0,0,1,0,1,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}};
......@@ -106,7 +118,8 @@ static void setupStruct(StructBuilder builder) {
builder.setDataField<bool>(127 * ELEMENTS, false);
{
StructBuilder subStruct = builder.initStructField(0 * REFERENCES, SUBSTRUCT_DEFAULT.words);
StructBuilder subStruct = builder.initStructField(
0 * REFERENCES, StructSize(1 * WORDS, 0 * REFERENCES));
subStruct.setDataField<uint32_t>(0 * ELEMENTS, 123);
}
......@@ -120,12 +133,12 @@ static void setupStruct(StructBuilder builder) {
{
ListBuilder list = builder.initStructListField(
2 * REFERENCES, 4 * ELEMENTS, STRUCTLIST_ELEMENT_DEFAULT.words);
2 * REFERENCES, 4 * ELEMENTS, StructSize(1 * WORDS, 1 * REFERENCES));
EXPECT_EQ(4 * ELEMENTS, list.size());
for (int i = 0; i < 4; i++) {
StructBuilder element = list.getStructElement(i * ELEMENTS, 2 * WORDS / ELEMENTS, 1 * WORDS);
element.setDataField<int32_t>(0 * ELEMENTS, 300 + i);
element.initStructField(0 * REFERENCES, STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT.words)
element.initStructField(0 * REFERENCES, StructSize(1 * WORDS, 0 * REFERENCES))
.setDataField<int32_t>(0 * ELEMENTS, 400 + i);
}
}
......@@ -159,7 +172,8 @@ static void checkStruct(StructBuilder builder) {
EXPECT_FALSE(builder.getDataField<bool>(127 * ELEMENTS));
{
StructBuilder subStruct = builder.getStructField(0 * REFERENCES, SUBSTRUCT_DEFAULT.words);
StructBuilder subStruct = builder.getStructField(
0 * REFERENCES, StructSize(1 * WORDS, 0 * REFERENCES), SUBSTRUCT_DEFAULT.words);
EXPECT_EQ(123u, subStruct.getDataField<uint32_t>(0 * ELEMENTS));
}
......@@ -178,7 +192,8 @@ static void checkStruct(StructBuilder builder) {
StructBuilder element = list.getStructElement(i * ELEMENTS, 2 * WORDS / ELEMENTS, 1 * WORDS);
EXPECT_EQ(300 + i, element.getDataField<int32_t>(0 * ELEMENTS));
EXPECT_EQ(400 + i,
element.getStructField(0 * REFERENCES, STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT.words)
element.getStructField(0 * REFERENCES, StructSize(1 * WORDS, 0 * REFERENCES),
STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT.words)
.getDataField<int32_t>(0 * ELEMENTS));
}
}
......@@ -197,22 +212,22 @@ static void checkStruct(StructBuilder builder) {
}
static void checkStruct(StructReader reader) {
EXPECT_EQ(0x1011121314151617ull, reader.getDataField<uint64_t>(0 * ELEMENTS, 1616));
EXPECT_EQ(0x20212223u, reader.getDataField<uint32_t>(2 * ELEMENTS, 1616));
EXPECT_EQ(0x3031u, reader.getDataField<uint16_t>(6 * ELEMENTS, 1616));
EXPECT_EQ(0x40u, reader.getDataField<uint8_t>(14 * ELEMENTS, 16));
EXPECT_FALSE(reader.getDataField<bool>(120 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(121 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(122 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(123 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(124 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(125 * ELEMENTS, false));
EXPECT_TRUE (reader.getDataField<bool>(126 * ELEMENTS, false));
EXPECT_FALSE(reader.getDataField<bool>(127 * ELEMENTS, false));
EXPECT_EQ(0x1011121314151617ull, reader.getDataField<uint64_t>(0 * ELEMENTS));
EXPECT_EQ(0x20212223u, reader.getDataField<uint32_t>(2 * ELEMENTS));
EXPECT_EQ(0x3031u, reader.getDataField<uint16_t>(6 * ELEMENTS));
EXPECT_EQ(0x40u, reader.getDataField<uint8_t>(14 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(120 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(121 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(122 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(123 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(124 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(125 * ELEMENTS));
EXPECT_TRUE (reader.getDataField<bool>(126 * ELEMENTS));
EXPECT_FALSE(reader.getDataField<bool>(127 * ELEMENTS));
{
StructReader subStruct = reader.getStructField(0 * REFERENCES, SUBSTRUCT_DEFAULT.words);
EXPECT_EQ(123u, subStruct.getDataField<uint32_t>(0 * ELEMENTS, 456));
EXPECT_EQ(123u, subStruct.getDataField<uint32_t>(0 * ELEMENTS));
}
{
......@@ -227,11 +242,11 @@ static void checkStruct(StructReader reader) {
ListReader list = reader.getListField(2 * REFERENCES, FieldSize::INLINE_COMPOSITE, nullptr);
ASSERT_EQ(4 * ELEMENTS, list.size());
for (int i = 0; i < 4; i++) {
StructReader element = list.getStructElement(i * ELEMENTS, STRUCTLIST_ELEMENT_DEFAULT.words);
EXPECT_EQ(300 + i, element.getDataField<int32_t>(0 * ELEMENTS, 1616));
StructReader element = list.getStructElement(i * ELEMENTS);
EXPECT_EQ(300 + i, element.getDataField<int32_t>(0 * ELEMENTS));
EXPECT_EQ(400 + i,
element.getStructField(0 * REFERENCES, STRUCTLIST_ELEMENT_SUBSTRUCT_DEFAULT.words)
.getDataField<int32_t>(0 * ELEMENTS, 1616));
.getDataField<int32_t>(0 * ELEMENTS));
}
}
......@@ -255,7 +270,8 @@ TEST(WireFormat, StructRoundTrip_OneSegment) {
SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
StructBuilder builder = StructBuilder::initRoot(
segment, rootLocation, StructSize(2 * WORDS, 4 * REFERENCES));
setupStruct(builder);
// word count:
......@@ -280,8 +296,8 @@ TEST(WireFormat, StructRoundTrip_OneSegment) {
checkStruct(builder);
checkStruct(builder.asReader());
checkStruct(StructReader::readRootTrusted(segment->getStartPtr(), nullptr));
checkStruct(StructReader::readRoot(segment->getStartPtr(), nullptr, segment, 4));
checkStruct(StructReader::readRootTrusted(segment->getStartPtr()));
checkStruct(StructReader::readRoot(segment->getStartPtr(), segment, 4));
}
TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) {
......@@ -290,7 +306,8 @@ TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) {
SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
StructBuilder builder = StructBuilder::initRoot(
segment, rootLocation, StructSize(2 * WORDS, 4 * REFERENCES));
setupStruct(builder);
// Verify that we made 15 segments.
......@@ -317,7 +334,7 @@ TEST(WireFormat, StructRoundTrip_OneSegmentPerAllocation) {
checkStruct(builder);
checkStruct(builder.asReader());
checkStruct(StructReader::readRoot(segment->getStartPtr(), nullptr, segment, 4));
checkStruct(StructReader::readRoot(segment->getStartPtr(), segment, 4));
}
TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) {
......@@ -326,7 +343,8 @@ TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) {
SegmentBuilder* segment = arena.getSegmentWithAvailable(1 * WORDS);
word* rootLocation = segment->allocate(1 * WORDS);
StructBuilder builder = StructBuilder::initRoot(segment, rootLocation, STRUCT_DEFAULT.words);
StructBuilder builder = StructBuilder::initRoot(
segment, rootLocation, StructSize(2 * WORDS, 4 * REFERENCES));
setupStruct(builder);
// Verify that we made 6 segments.
......@@ -344,7 +362,7 @@ TEST(WireFormat, StructRoundTrip_MultipleSegmentsWithMultipleAllocations) {
checkStruct(builder);
checkStruct(builder.asReader());
checkStruct(StructReader::readRoot(segment->getStartPtr(), nullptr, segment, 4));
checkStruct(StructReader::readRoot(segment->getStartPtr(), segment, 4));
}
} // namespace
......
......@@ -129,6 +129,10 @@ struct WireReference {
dataSize.set(ds);
refCount.set(rc);
}
CAPNPROTO_ALWAYS_INLINE(void set(StructSize size)) {
dataSize.set(size.data);
refCount.set(size.pointers);
}
} structRef;
// Also covers capabilities.
......@@ -403,47 +407,42 @@ struct WireHelpers {
// -----------------------------------------------------------------
static CAPNPROTO_ALWAYS_INLINE(StructBuilder initStructReference(
WireReference* ref, SegmentBuilder* segment, const word* typeDefaultValue)) {
const WireReference* defaultRef = reinterpret_cast<const WireReference*>(typeDefaultValue);
// Allocate space for the new struct.
word* ptr = allocate(ref, segment, defaultRef->structRef.wordSize(), WireReference::STRUCT);
// Copy over the data segment from the default value. We don't have to copy the reference
// segment because it is presumed to be all-null.
memcpy(ptr, defaultRef->target(),
defaultRef->structRef.dataSize.get() * BYTES_PER_WORD / BYTES);
WireReference* ref, SegmentBuilder* segment, StructSize size)) {
// Allocate space for the new struct. Newly-allocated space is automatically zeroed.
word* ptr = allocate(ref, segment, size.total(), WireReference::STRUCT);
// Initialize the reference.
ref->structRef.set(defaultRef->structRef.dataSize.get(), defaultRef->structRef.refCount.get());
ref->structRef.set(size);
// Build the StructBuilder.
return StructBuilder(segment, ptr,
reinterpret_cast<WireReference*>(ptr + defaultRef->structRef.dataSize.get()));
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data));
}
static CAPNPROTO_ALWAYS_INLINE(StructBuilder getWritableStructReference(
WireReference* ref, SegmentBuilder* segment, const word* defaultValue)) {
const WireReference* defaultRef = reinterpret_cast<const WireReference*>(defaultValue);
WireReference* ref, SegmentBuilder* segment, StructSize size, const word* defaultValue)) {
word* ptr;
if (ref->isNull()) {
ptr = copyMessage(segment, ref, defaultRef);
if (defaultValue == nullptr) {
ptr = allocate(ref, segment, size.total(), WireReference::STRUCT);
ref->structRef.set(size);
} else {
ptr = copyMessage(segment, ref, reinterpret_cast<const WireReference*>(defaultValue));
}
} else {
ptr = followFars(ref, segment);
CAPNPROTO_DEBUG_ASSERT(ref->kind() == WireReference::STRUCT,
"Called getStruct{Field,Element}() but existing reference is not a struct.");
CAPNPROTO_DEBUG_ASSERT(
ref->structRef.dataSize.get() == defaultRef->structRef.dataSize.get(),
ref->structRef.dataSize.get() == size.data,
"Trying to update struct with incorrect data size.");
CAPNPROTO_DEBUG_ASSERT(
ref->structRef.refCount.get() == defaultRef->structRef.refCount.get(),
ref->structRef.refCount.get() == size.pointers,
"Trying to update struct with incorrect reference count.");
}
return StructBuilder(segment, ptr,
reinterpret_cast<WireReference*>(ptr + defaultRef->structRef.dataSize.get()));
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + size.data));
}
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference(
......@@ -468,10 +467,8 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initStructListReference(
WireReference* ref, SegmentBuilder* segment, ElementCount elementCount,
const word* elementDefaultValue)) {
const WireReference* defaultRef = reinterpret_cast<const WireReference*>(elementDefaultValue);
auto wordsPerElement = defaultRef->structRef.wordSize() / ELEMENTS;
StructSize elementSize)) {
auto wordsPerElement = elementSize.total() / ELEMENTS;
// Allocate the list, prefixed by a single WireReference.
WordCount wordCount = elementCount * wordsPerElement;
......@@ -484,22 +481,9 @@ struct WireHelpers {
// Initialize the list tag.
reinterpret_cast<WireReference*>(ptr)->setKindAndInlineCompositeListElementCount(
WireReference::STRUCT, elementCount);
reinterpret_cast<WireReference*>(ptr)->structRef.set(
defaultRef->structRef.dataSize.get(),
defaultRef->structRef.refCount.get());
reinterpret_cast<WireReference*>(ptr)->structRef.set(elementSize);
ptr += REFERENCE_SIZE_IN_WORDS;
// Initialize the elements. We only have to copy the data segments, as the reference segment
// in a struct type default value is always all-null.
ByteCount elementDataByteSize = defaultRef->structRef.dataSize.get() * BYTES_PER_WORD;
const word* defaultData = defaultRef->target();
word* elementPtr = ptr;
uint n = elementCount / ELEMENTS;
for (uint i = 0; i < n; i++) {
memcpy(elementPtr, defaultData, elementDataByteSize / BYTES);
elementPtr += 1 * ELEMENTS * wordsPerElement;
}
// Build the ListBuilder.
return ListBuilder(segment, ptr, elementCount);
}
......@@ -618,6 +602,10 @@ struct WireHelpers {
if (ref == nullptr || ref->isNull()) {
useDefault:
if (defaultValue == nullptr) {
return StructReader(nullptr, nullptr, nullptr, 0 * WORDS, 0 * REFERENCES, 0 * BITS,
std::numeric_limits<int>::max());
}
segment = nullptr;
ref = reinterpret_cast<const WireReference*>(defaultValue);
ptr = ref->target();
......@@ -940,25 +928,26 @@ struct WireHelpers {
// =======================================================================================
StructBuilder StructBuilder::initRoot(
SegmentBuilder* segment, word* location, const word* defaultValue) {
SegmentBuilder* segment, word* location, StructSize size) {
return WireHelpers::initStructReference(
reinterpret_cast<WireReference*>(location), segment, defaultValue);
reinterpret_cast<WireReference*>(location), segment, size);
}
StructBuilder StructBuilder::getRoot(
SegmentBuilder* segment, word* location, const word* defaultValue) {
SegmentBuilder* segment, word* location, StructSize size) {
return WireHelpers::getWritableStructReference(
reinterpret_cast<WireReference*>(location), segment, defaultValue);
reinterpret_cast<WireReference*>(location), segment, size, nullptr);
}
StructBuilder StructBuilder::initStructField(
WireReferenceCount refIndex, const word* typeDefaultValue) const {
return WireHelpers::initStructReference(references + refIndex, segment, typeDefaultValue);
WireReferenceCount refIndex, StructSize size) const {
return WireHelpers::initStructReference(references + refIndex, segment, size);
}
StructBuilder StructBuilder::getStructField(
WireReferenceCount refIndex, const word* defaultValue) const {
return WireHelpers::getWritableStructReference(references + refIndex, segment, defaultValue);
WireReferenceCount refIndex, StructSize size, const word* defaultValue) const {
return WireHelpers::getWritableStructReference(
references + refIndex, segment, size, defaultValue);
}
ListBuilder StructBuilder::initListField(
......@@ -969,10 +958,9 @@ ListBuilder StructBuilder::initListField(
}
ListBuilder StructBuilder::initStructListField(
WireReferenceCount refIndex, ElementCount elementCount,
const word* elementDefaultValue) const {
WireReferenceCount refIndex, ElementCount elementCount, StructSize elementSize) const {
return WireHelpers::initStructListReference(
references + refIndex, segment, elementCount, elementDefaultValue);
references + refIndex, segment, elementCount, elementSize);
}
ListBuilder StructBuilder::getListField(
......@@ -1016,20 +1004,25 @@ StructReader StructBuilder::asReader() const {
0xffff * WORDS, 0xffff * REFERENCES, 0 * BITS, std::numeric_limits<int>::max());
}
StructReader StructReader::readRootTrusted(const word* location, const word* defaultValue) {
StructReader StructReader::readRootTrusted(const word* location) {
return WireHelpers::readStructReference(nullptr, reinterpret_cast<const WireReference*>(location),
defaultValue, std::numeric_limits<int>::max());
nullptr, std::numeric_limits<int>::max());
}
StructReader StructReader::readRoot(const word* location, const word* defaultValue,
SegmentReader* segment, int nestingLimit) {
StructReader StructReader::readRoot(
const word* location, SegmentReader* segment, int nestingLimit) {
if (!segment->containsInterval(location, location + REFERENCE_SIZE_IN_WORDS)) {
segment->getArena()->reportInvalidData("Root location out-of-bounds.");
location = nullptr;
}
return WireHelpers::readStructReference(segment, reinterpret_cast<const WireReference*>(location),
defaultValue, nestingLimit);
nullptr, nestingLimit);
}
StructReader StructReader::readEmpty() {
return StructReader(nullptr, nullptr, nullptr, 0 * WORDS, 0 * REFERENCES, 0 * BITS,
std::numeric_limits<int>::max());
}
StructReader StructReader::getStructField(
......@@ -1072,10 +1065,10 @@ ListBuilder ListBuilder::initListElement(
}
ListBuilder ListBuilder::initStructListElement(
WireReferenceCount index, ElementCount elementCount, const word* elementDefaultValue) const {
WireReferenceCount index, ElementCount elementCount, StructSize elementSize) const {
return WireHelpers::initStructListReference(
reinterpret_cast<WireReference*>(ptr) + index, segment,
elementCount, elementDefaultValue);
elementCount, elementSize);
}
ListBuilder ListBuilder::getListElement(WireReferenceCount index) const {
......@@ -1123,11 +1116,11 @@ ListReader ListBuilder::asReader(WordCount dataSize, WireReferenceCount referenc
dataSize, referenceCount, std::numeric_limits<int>::max());
}
StructReader ListReader::getStructElement(ElementCount index, const word* defaultValue) const {
StructReader ListReader::getStructElement(ElementCount index) const {
if (CAPNPROTO_EXPECT_FALSE((segment != nullptr) & (nestingLimit == 0))) {
segment->getArena()->reportInvalidData(
"Message is too deeply-nested or contains cycles. See capnproto::ReadOptions.");
return WireHelpers::readStructReference(nullptr, nullptr, defaultValue, nestingLimit);
return StructReader::readEmpty();
} else {
BitCount64 indexBit = ElementCount64(index) * stepBits;
const byte* structPtr = reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE;
......
......@@ -123,6 +123,91 @@ union AlignedData {
word words[wordCount];
};
struct StructSize {
WordCount16 data;
WireReferenceCount16 pointers;
inline constexpr WordCount total() const { return data + pointers * WORDS_PER_REFERENCE; }
StructSize() = default;
inline constexpr StructSize(WordCount data, WireReferenceCount pointers)
: data(data), pointers(pointers) {}
};
template <typename T>
class IsEnum {
// Detects whether a primitive value is an enum.
typedef char no;
typedef long yes;
static no test(int i);
static yes test(...);
public:
static constexpr bool value = sizeof(test(T())) == sizeof(yes);
};
// -------------------------------------------------------------------
// Masking of default values
template <typename T, bool isEnum = IsEnum<T>::value> struct MaskType { typedef T Type; };
template <typename T> struct MaskType<T, false> { typedef T Type; };
template <typename T> struct MaskType<T, true> { typedef uint16_t Type; };
template <> struct MaskType<float, false> { typedef uint32_t Type; };
template <> struct MaskType<double, false> { typedef uint64_t Type; };
template <typename T>
CAPNPROTO_ALWAYS_INLINE(
typename MaskType<T>::Type mask(T value, typename MaskType<T>::Type mask));
template <typename T>
CAPNPROTO_ALWAYS_INLINE(
T unmask(typename MaskType<T>::Type value, typename MaskType<T>::Type mask));
template <typename T>
inline typename MaskType<T>::Type mask(T value, typename MaskType<T>::Type mask) {
return static_cast<typename MaskType<T>::Type>(value) ^ mask;
}
template <>
inline uint32_t mask<float>(float value, uint32_t mask) {
uint32_t i;
static_assert(sizeof(i) == sizeof(value), "float is not 32 bits?");
memcpy(&i, &value, sizeof(value));
return i ^ mask;
}
template <>
inline uint64_t mask<double>(double value, uint64_t mask) {
uint64_t i;
static_assert(sizeof(i) == sizeof(value), "double is not 64 bits?");
memcpy(&i, &value, sizeof(value));
return i ^ mask;
}
template <typename T>
inline T unmask(typename MaskType<T>::Type value, typename MaskType<T>::Type mask) {
return static_cast<T>(value ^ mask);
}
template <>
inline float unmask<float>(uint32_t value, uint32_t mask) {
value ^= mask;
float result;
static_assert(sizeof(result) == sizeof(value), "float is not 32 bits?");
memcpy(&result, &value, sizeof(value));
return result;
}
template <>
inline double unmask<double>(uint64_t value, uint64_t mask) {
value ^= mask;
double result;
static_assert(sizeof(result) == sizeof(value), "double is not 64 bits?");
memcpy(&result, &value, sizeof(value));
return result;
}
// -------------------------------------------------------------------
template <typename T>
......@@ -154,30 +239,42 @@ class StructBuilder {
public:
inline StructBuilder(): segment(nullptr), data(nullptr), references(nullptr) {}
static StructBuilder initRoot(SegmentBuilder* segment, word* location, const word* defaultValue);
static StructBuilder getRoot(SegmentBuilder* segment, word* location, const word* defaultValue);
static StructBuilder initRoot(SegmentBuilder* segment, word* location, StructSize size);
static StructBuilder getRoot(SegmentBuilder* segment, word* location, StructSize size);
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
// Gets the data field value of the given type at the given offset. The offset is measured in
// multiples of the field size, determined by the type.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(
ElementCount offset, typename MaskType<T>::Type mask) const);
// Like getDataField() but applies the given XOR mask to the data on load. Used for reading
// fields with non-zero default values.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(void setDataField(
ElementCount offset, typename NoInfer<T>::Type value) const);
// Sets the data field value at the given offset.
StructBuilder initStructField(WireReferenceCount refIndex, const word* typeDefaultValue) const;
template <typename T>
CAPNPROTO_ALWAYS_INLINE(void setDataField(
ElementCount offset, typename NoInfer<T>::Type value, typename MaskType<T>::Type mask) const);
// Like setDataField() but applies the given XOR mask before storing. Used for writing fields
// with non-zero default values.
StructBuilder initStructField(WireReferenceCount refIndex, StructSize size) const;
// Initializes the struct field at the given index in the reference segment. If it is already
// initialized, the previous value is discarded or overwritten. The struct is initialized to
// match the given default value (a trusted message). This must be the default value for the
// *type*, not the specific field, and in particular its reference segment is expected to be
// all nulls (only the data segment is copied). Use getStructField() if you want the struct
// to be initialized as a copy of the field's default value (which may have non-null references).
// the type's default state (all-zero). Use getStructField() if you want the struct to be
// initialized as a copy of the field's default value (which may have non-null references).
StructBuilder getStructField(WireReferenceCount refIndex, const word* defaultValue) const;
StructBuilder getStructField(WireReferenceCount refIndex, StructSize size,
const word* defaultValue) const;
// Gets the struct field at the given index in the reference segment. If the field is not already
// initialized, it is initialized as a deep copy of the given default value (a trusted message).
// initialized, it is initialized as a deep copy of the given default value (a trusted message),
// or to the empty state if defaultValue is nullptr.
ListBuilder initListField(WireReferenceCount refIndex, FieldSize elementSize,
ElementCount elementCount) const;
......@@ -185,11 +282,9 @@ public:
// segment, and return a pointer to it. All elements are initialized to zero.
ListBuilder initStructListField(WireReferenceCount refIndex, ElementCount elementCount,
const word* elementDefaultValue) const;
StructSize size) const;
// Allocates a new list of the given size for the field at the given index in the reference
// segment, and return a pointer to it. Each element is initialized as a copy of
// elementDefaultValue. As with initStructField(), this should be the default value for the
// *type*, with all-null references.
// segment, and return a pointer to it. Each element is initialized to its empty state.
ListBuilder getListField(WireReferenceCount refIndex, const word* defaultValue) const;
// Gets the already-allocated list field for the given reference index. If the list is not
......@@ -234,21 +329,27 @@ public:
: segment(nullptr), data(nullptr), references(nullptr), dataSize(0),
referenceCount(0), bit0Offset(0 * BITS), nestingLimit(0) {}
static StructReader readRootTrusted(const word* location, const word* defaultValue);
static StructReader readRoot(const word* location, const word* defaultValue,
SegmentReader* segment, int nestingLimit);
static StructReader readRootTrusted(const word* location);
static StructReader readRoot(const word* location, SegmentReader* segment, int nestingLimit);
static StructReader readEmpty();
template <typename T>
CAPNPROTO_ALWAYS_INLINE(
T getDataField(ElementCount offset, typename NoInfer<T>::Type defaultValue) const);
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
// Get the data field value of the given type at the given offset. The offset is measured in
// 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.
// multiples of the field size, determined by the type. Returns zero if the offset is past the
// end of the struct's data segment.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(
T getDataField(ElementCount offset, typename MaskType<T>::Type mask) const);
// Like getDataField(offset), but applies the given XOR mask to the result. Used for reading
// fields with non-zero default values.
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
// initialized. defaultValue will be interpreted as a trusted message -- it must point at a
// struct reference, which in turn points at the struct value.
// struct reference, which in turn points at the struct value. The default value is allowed to
// be null, in which case an empty struct is used.
ListReader getListField(WireReferenceCount refIndex, FieldSize expectedElementSize,
const word* defaultValue) const;
......@@ -322,11 +423,9 @@ public:
// to zero.
ListBuilder initStructListElement(WireReferenceCount index, ElementCount elementCount,
const word* elementDefaultValue) const;
StructSize size) const;
// Allocates a new list of the given size for the field at the given index in the reference
// segment, and return a pointer to it. Each element is initialized as a copy of
// elementDefaultValue. As with StructBuilder::initStructListElement(), this should be the
// default value for the *type*, with all-null references.
// segment, and return a pointer to it. Each element is initialized to its empty state.
ListBuilder getListElement(WireReferenceCount index) const;
// Get the existing list element at the given index. Returns an empty list if the element is
......@@ -378,7 +477,7 @@ public:
CAPNPROTO_ALWAYS_INLINE(T getDataElement(ElementCount index) const);
// Get the element of the given type at the given index.
StructReader getStructElement(ElementCount index, const word* defaultValue) const;
StructReader getStructElement(ElementCount index) const;
// Get the struct element at the given index.
ListReader getListElement(WireReferenceCount index, FieldSize expectedElementSize) const;
......@@ -451,6 +550,11 @@ inline Void StructBuilder::getDataField<Void>(ElementCount offset) const {
return Void::VOID;
}
template <typename T>
inline T StructBuilder::getDataField(ElementCount offset, typename MaskType<T>::Type mask) const {
return unmask<T>(getDataField<typename MaskType<T>::Type>(offset), mask);
}
template <typename T>
inline void StructBuilder::setDataField(
ElementCount offset, typename NoInfer<T>::Type value) const {
......@@ -469,19 +573,25 @@ inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) c
template <>
inline void StructBuilder::setDataField<Void>(ElementCount offset, Void value) const {}
template <typename T>
inline void StructBuilder::setDataField(
ElementCount offset, typename NoInfer<T>::Type value, typename MaskType<T>::Type m) const {
setDataField<typename MaskType<T>::Type>(offset, mask<T>(value, m));
}
// -------------------------------------------------------------------
template <typename T>
T StructReader::getDataField(ElementCount offset, typename NoInfer<T>::Type defaultValue) const {
T StructReader::getDataField(ElementCount offset) const {
if (offset * bytesPerElement<T>() < dataSize * BYTES_PER_WORD) {
return reinterpret_cast<const WireValue<T>*>(data)[offset / ELEMENTS].get();
} else {
return defaultValue;
return static_cast<T>(0);
}
}
template <>
inline bool StructReader::getDataField<bool>(ElementCount offset, bool defaultValue) const {
inline bool StructReader::getDataField<bool>(ElementCount offset) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS);
// This branch should always be optimized away when inlining.
......@@ -491,15 +601,20 @@ inline bool StructReader::getDataField<bool>(ElementCount offset, bool defaultVa
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;
return false;
}
}
template <>
inline Void StructReader::getDataField<Void>(ElementCount offset, Void defaultValue) const {
inline Void StructReader::getDataField<Void>(ElementCount offset) const {
return Void::VOID;
}
template <typename T>
T StructReader::getDataField(ElementCount offset, typename MaskType<T>::Type mask) const {
return unmask<T>(getDataField<typename MaskType<T>::Type>(offset), mask);
}
// -------------------------------------------------------------------
inline ElementCount ListBuilder::size() { return elementCount; }
......
......@@ -216,7 +216,7 @@ struct List<T, false> {
inline uint size() { return reader.size() / ELEMENTS; }
inline typename T::Reader operator[](uint index) {
return typename T::Reader(reader.getStructElement(index * ELEMENTS, T::DEFAULT.words));
return typename T::Reader(reader.getStructElement(index * ELEMENTS));
}
typedef internal::IndexingIterator<Reader, typename T::Reader> iterator;
......@@ -235,8 +235,7 @@ struct List<T, false> {
inline uint size() { return builder.size() / ELEMENTS; }
inline typename T::Builder operator[](uint index) {
return typename T::Builder(builder.getStructElement(index * ELEMENTS,
(T::DATA_SIZE + T::REFERENCE_COUNT * WORDS_PER_REFERENCE) / ELEMENTS,
T::DATA_SIZE));
T::STRUCT_SIZE.total() / ELEMENTS, T::STRUCT_SIZE.data));
}
typedef internal::IndexingIterator<Builder, typename T::Builder> iterator;
......
......@@ -38,7 +38,7 @@ MessageReader::~MessageReader() {
}
}
internal::StructReader MessageReader::getRoot(const word* defaultValue) {
internal::StructReader MessageReader::getRootInternal() {
if (!allocatedArena) {
static_assert(sizeof(internal::ReaderArena) <= sizeof(arenaSpace),
"arenaSpace is too small to hold a ReaderArena. Please increase it. This will break "
......@@ -51,10 +51,9 @@ internal::StructReader MessageReader::getRoot(const word* defaultValue) {
if (segment == nullptr ||
!segment->containsInterval(segment->getStartPtr(), segment->getStartPtr() + 1)) {
arena()->reportInvalidData("Message did not contain a root pointer.");
return internal::StructReader::readRootTrusted(defaultValue, defaultValue);
return internal::StructReader::readEmpty();
} else {
return internal::StructReader::readRoot(
segment->getStartPtr(), defaultValue, segment, options.nestingLimit);
return internal::StructReader::readRoot(segment->getStartPtr(), segment, options.nestingLimit);
}
}
......@@ -88,16 +87,16 @@ internal::SegmentBuilder* MessageBuilder::getRootSegment() {
}
}
internal::StructBuilder MessageBuilder::initRoot(const word* defaultValue) {
internal::StructBuilder MessageBuilder::initRoot(internal::StructSize size) {
internal::SegmentBuilder* rootSegment = getRootSegment();
return internal::StructBuilder::initRoot(
rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), defaultValue);
rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), size);
}
internal::StructBuilder MessageBuilder::getRoot(const word* defaultValue) {
internal::StructBuilder MessageBuilder::getRoot(internal::StructSize size) {
internal::SegmentBuilder* rootSegment = getRootSegment();
return internal::StructBuilder::getRoot(
rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), defaultValue);
rootSegment, rootSegment->getPtrUnchecked(0 * WORDS), size);
}
ArrayPtr<const ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
......
......@@ -144,7 +144,7 @@ private:
bool allocatedArena;
internal::ReaderArena* arena() { return reinterpret_cast<internal::ReaderArena*>(arenaSpace); }
internal::StructReader getRoot(const word* defaultValue);
internal::StructReader getRootInternal();
};
class MessageBuilder {
......@@ -175,8 +175,8 @@ private:
internal::BuilderArena* arena() { return reinterpret_cast<internal::BuilderArena*>(arenaSpace); }
internal::SegmentBuilder* getRootSegment();
internal::StructBuilder initRoot(const word* defaultValue);
internal::StructBuilder getRoot(const word* defaultValue);
internal::StructBuilder initRoot(internal::StructSize size);
internal::StructBuilder getRoot(internal::StructSize size);
};
template <typename RootType>
......@@ -297,23 +297,22 @@ inline const ReaderOptions& MessageReader::getOptions() {
template <typename RootType>
inline typename RootType::Reader MessageReader::getRoot() {
return typename RootType::Reader(getRoot(RootType::DEFAULT.words));
return typename RootType::Reader(getRootInternal());
}
template <typename RootType>
inline typename RootType::Builder MessageBuilder::initRoot() {
return typename RootType::Builder(initRoot(RootType::DEFAULT.words));
return typename RootType::Builder(initRoot(RootType::STRUCT_SIZE));
}
template <typename RootType>
inline typename RootType::Builder MessageBuilder::getRoot() {
return typename RootType::Builder(getRoot(RootType::DEFAULT.words));
return typename RootType::Builder(getRoot(RootType::STRUCT_SIZE));
}
template <typename RootType>
typename RootType::Reader readMessageTrusted(const word* data) {
return typename RootType::Reader(internal::StructReader::readRootTrusted(
data, RootType::DEFAULT.words));
return typename RootType::Reader(internal::StructReader::readRootTrusted(data));
}
} // namespace capnproto
......
......@@ -67,7 +67,7 @@ struct TestAllTypes {
dataList @30 : List(Data);
structList @31 : List(TestAllTypes);
enumList @32 : List(TestEnum);
interfaceList @33 : Void; # TODO
interfaceList @33 : List(Void); # TODO
}
struct TestDefaults {
......
......@@ -29,6 +29,7 @@ import qualified Data.ByteString.UTF8 as ByteStringUTF8
import Data.FileEmbed(embedFile)
import Data.Word(Word8)
import qualified Data.Digest.MD5 as MD5
import Data.Binary.IEEE754(floatToWord, doubleToWord)
import Text.Printf(printf)
import Text.Hastache
import Text.Hastache.Context
......@@ -106,25 +107,41 @@ cxxFieldSizeString Size64 = "EIGHT_BYTES";
cxxFieldSizeString SizeReference = "REFERENCE";
cxxFieldSizeString (SizeInlineComposite _ _) = "INLINE_COMPOSITE";
cxxValueString VoidDesc = " ::capnproto::Void::VOID"
cxxValueString (BoolDesc b) = if b then "true" else "false"
cxxValueString (Int8Desc i) = show i
cxxValueString (Int16Desc i) = show i
cxxValueString (Int32Desc i) = show i
cxxValueString (Int64Desc i) = show i ++ "ll"
cxxValueString (UInt8Desc i) = show i
cxxValueString (UInt16Desc i) = show i
cxxValueString (UInt32Desc i) = show i ++ "u"
cxxValueString (UInt64Desc i) = show i ++ "llu"
cxxValueString (Float32Desc x) = show x ++ "f"
cxxValueString (Float64Desc x) = show x
cxxValueString (EnumValueValueDesc v) =
cxxTypeString (EnumType $ enumValueParent v) ++ "::" ++
toUpperCaseWithUnderscores (enumValueName v)
cxxValueString (TextDesc _) = error "No default value literal for aggregate type."
cxxValueString (DataDesc _) = error "No default value literal for aggregate type."
cxxValueString (StructValueDesc _) = error "No default value literal for aggregate type."
cxxValueString (ListDesc _) = error "No default value literal for aggregate type."
isDefaultZero VoidDesc = True
isDefaultZero (BoolDesc b) = not b
isDefaultZero (Int8Desc i) = i == 0
isDefaultZero (Int16Desc i) = i == 0
isDefaultZero (Int32Desc i) = i == 0
isDefaultZero (Int64Desc i) = i == 0
isDefaultZero (UInt8Desc i) = i == 0
isDefaultZero (UInt16Desc i) = i == 0
isDefaultZero (UInt32Desc i) = i == 0
isDefaultZero (UInt64Desc i) = i == 0
isDefaultZero (Float32Desc x) = x == 0
isDefaultZero (Float64Desc x) = x == 0
isDefaultZero (EnumValueValueDesc v) = enumValueNumber v == 0
isDefaultZero (TextDesc _) = error "Can't call isDefaultZero on aggregate types."
isDefaultZero (DataDesc _) = error "Can't call isDefaultZero on aggregate types."
isDefaultZero (StructValueDesc _) = error "Can't call isDefaultZero on aggregate types."
isDefaultZero (ListDesc _) = error "Can't call isDefaultZero on aggregate types."
defaultMask VoidDesc = "0"
defaultMask (BoolDesc b) = if b then "true" else "false"
defaultMask (Int8Desc i) = show i
defaultMask (Int16Desc i) = show i
defaultMask (Int32Desc i) = show i
defaultMask (Int64Desc i) = show i ++ "ll"
defaultMask (UInt8Desc i) = show i
defaultMask (UInt16Desc i) = show i
defaultMask (UInt32Desc i) = show i ++ "u"
defaultMask (UInt64Desc i) = show i ++ "llu"
defaultMask (Float32Desc x) = show (floatToWord x) ++ "u"
defaultMask (Float64Desc x) = show (doubleToWord x) ++ "ul"
defaultMask (EnumValueValueDesc v) = show (enumValueNumber v)
defaultMask (TextDesc _) = error "Can't call defaultMask on aggregate types."
defaultMask (DataDesc _) = error "Can't call defaultMask on aggregate types."
defaultMask (StructValueDesc _) = error "Can't call defaultMask on aggregate types."
defaultMask (ListDesc _) = error "Can't call defaultMask on aggregate types."
defaultValueBytes _ (TextDesc s) = Just (UTF8.encode s ++ [0])
defaultValueBytes _ (DataDesc d) = Just d
......@@ -132,25 +149,6 @@ defaultValueBytes t v@(StructValueDesc _) = Just $ encodeMessage t v
defaultValueBytes t v@(ListDesc _) = Just $ encodeMessage t v
defaultValueBytes _ _ = Nothing
cxxDefaultDefault (BuiltinType BuiltinVoid) = " ::capnproto::Void::VOID"
cxxDefaultDefault (BuiltinType BuiltinBool) = "false"
cxxDefaultDefault (BuiltinType BuiltinInt8) = "0"
cxxDefaultDefault (BuiltinType BuiltinInt16) = "0"
cxxDefaultDefault (BuiltinType BuiltinInt32) = "0"
cxxDefaultDefault (BuiltinType BuiltinInt64) = "0"
cxxDefaultDefault (BuiltinType BuiltinUInt8) = "0"
cxxDefaultDefault (BuiltinType BuiltinUInt16) = "0"
cxxDefaultDefault (BuiltinType BuiltinUInt32) = "0"
cxxDefaultDefault (BuiltinType BuiltinUInt64) = "0"
cxxDefaultDefault (BuiltinType BuiltinFloat32) = "0"
cxxDefaultDefault (BuiltinType BuiltinFloat64) = "0"
cxxDefaultDefault (BuiltinType BuiltinText) = "\"\""
cxxDefaultDefault (EnumType desc) = cxxValueString $ EnumValueValueDesc $ head $ enumValues desc
cxxDefaultDefault (BuiltinType BuiltinData) = error "No default value literal for aggregate type."
cxxDefaultDefault (StructType _) = error "No default value literal for aggregate type."
cxxDefaultDefault (InterfaceType _) = error "No default value literal for aggregate type."
cxxDefaultDefault (ListType _) = error "No default value literal for aggregate type."
elementType (ListType t) = t
elementType _ = error "Called elementType on non-list."
......@@ -197,9 +195,9 @@ fieldContext parent desc = mkStrContext context where
context "fieldType" = MuVariable $ cxxTypeString $ fieldType desc
context "fieldBlobType" = MuVariable $ blobTypeString $ fieldType desc
context "fieldOffset" = MuVariable $ fieldOffset desc
context "fieldDefaultValue" = case fieldDefaultValue desc of
Just v -> MuVariable $ cxxValueString v
Nothing -> MuVariable $ cxxDefaultDefault $ fieldType desc
context "fieldDefaultMask" = case fieldDefaultValue desc of
Nothing -> MuVariable ""
Just v -> MuVariable (if isDefaultZero v then "" else ", " ++ defaultMask v)
context "fieldElementSize" =
MuVariable $ cxxFieldSizeString $ elementSize $ elementType $ fieldType desc
context "fieldElementType" =
......@@ -212,8 +210,6 @@ structContext parent desc = mkStrContext context where
context "structDataSize" = MuVariable $ packingDataSize $ structPacking desc
context "structReferenceCount" = MuVariable $ packingReferenceCount $ structPacking desc
context "structChildren" = MuList [] -- TODO
context "structDefault" = MuList [defaultBytesContext context (StructType desc)
(encodeMessage (StructType desc) (StructValueDesc []))]
context s = parent s
fileContext desc = mkStrContext context where
......
......@@ -25,8 +25,7 @@ module WireFormat(encodeMessage) where
import Data.List(sortBy, genericLength, genericReplicate)
import Data.Word
import Data.Bits(shiftL, shiftR, Bits, setBit)
import qualified Data.Set as Set
import Data.Bits(shiftL, shiftR, Bits, setBit, xor)
import Semantics
import Data.Binary.IEEE754(floatToWord, doubleToWord)
import qualified Codec.Binary.UTF8.String as UTF8
......@@ -69,27 +68,34 @@ encodeDataValue (EnumValueValueDesc v) = bytes (enumValueNumber v) 2
encodeDataValue (StructValueDesc _) = error "Not fixed-width data."
encodeDataValue (ListDesc _) = error "Not fixed-width data."
packBits :: Bits a => Int -> [Bool] -> a
encodeMaskedDataValue v Nothing = encodeDataValue v
encodeMaskedDataValue v (Just d) = zipWith xor (encodeDataValue v) (encodeDataValue d)
packBits :: Bits a => Int -> [(Bool, Maybe Bool)] -> a
packBits _ [] = 0
packBits offset (True:bits) = setBit (packBits (offset + 1) bits) offset
packBits offset (False:bits) = packBits (offset + 1) bits
packBits offset ((True, Nothing):bits) = setBit (packBits (offset + 1) bits) offset
packBits offset ((False, Nothing):bits) = packBits (offset + 1) bits
packBits offset ((b, Just d):bits) = packBits offset ((b /= d, Nothing):bits)
encodeData :: Integer -> [(Integer, TypeDesc, ValueDesc)] -> [Word8]
-- The tuples are (offsetInBits, type, value, defaultValue) for each field to encode.
encodeData :: Integer -> [(Integer, TypeDesc, ValueDesc, Maybe ValueDesc)] -> [Word8]
encodeData size = loop 0 where
loop bit [] | bit == size = []
loop bit [] | bit > size = error "Data values overran size."
loop bit [] = 0:loop (bit + 8) []
loop bit rest@((valuePos, _, BoolDesc _):_) | valuePos == bit = let
loop bit rest@((valuePos, _, BoolDesc _, _):_) | valuePos == bit = let
(bits, rest2) = popBits (bit + 8) rest
in packBits 0 bits : loop (bit + 8) rest2
loop bit ((valuePos, _, value):rest) | valuePos == bit =
encodeDataValue value ++ loop (bit + sizeInBits (fieldValueSize value)) rest
loop bit rest@((valuePos, _, _):_) | valuePos > bit = 0 : loop (bit + 8) rest
loop bit ((valuePos, _, value, defaultValue):rest) | valuePos == bit =
encodeMaskedDataValue value defaultValue ++
loop (bit + sizeInBits (fieldValueSize value)) rest
loop bit rest@((valuePos, _, _, _):_) | valuePos > bit = 0 : loop (bit + 8) rest
loop _ _ = error "Data values were out-of-order."
popBits limit ((valuePos, _, BoolDesc b):rest) | valuePos < limit = let
popBits limit ((valuePos, _, BoolDesc b, d):rest) | valuePos < limit = let
(restBits, rest2) = popBits limit rest
in (b:restBits, rest2)
defaultB = fmap (\(BoolDesc b2) -> b2) d
in ((b, defaultB):restBits, rest2)
popBits _ rest = ([], rest)
encodeReferences :: Integer -> Integer -> [(Integer, TypeDesc, ValueDesc)] -> ([Word8], [Word8])
......@@ -156,10 +162,6 @@ fieldSizeEnum (SizeInlineComposite _ _) = 7
structTag = 0
listTag = 1
-- Is this field a non-retroactive member of a union? If so, its default value is not written.
isNonRetroUnionMember (FieldDesc {fieldNumber = n, fieldUnion = Just u}) = n > unionNumber u
isNonRetroUnionMember _ = False
-- What is this union's default tag value? If there is a retroactive field, it is that field's
-- number, otherwise it is the union's number (meaning no field set).
unionDefault desc = UInt8Desc $ fromIntegral $
......@@ -168,49 +170,24 @@ unionDefault desc = UInt8Desc $ fromIntegral $
-- childOffset = number of words between the last reference and the location where children will
-- be allocated.
encodeStruct desc assignments childOffset = (dataBytes, referenceBytes, children) where
explicitlyAssignedNums = Set.fromList [fieldNumber f | (f, _) <- assignments]
explicitlyAssignedUnions = Set.fromList
[unionNumber u | (FieldDesc {fieldUnion = Just u}, _) <- assignments]
-- Was this field explicitly assigned, or was another member of the same union explicitly
-- assigned? If so, its default value is not written.
isExplicitlyAssigned (FieldDesc {fieldNumber = n, fieldUnion = u}) =
Set.member n explicitlyAssignedNums ||
maybe False (flip Set.member explicitlyAssignedUnions . unionNumber) u
-- Values explicitly assigned.
explicitValues = [(fieldOffset f, fieldType f, v) | (f, v) <- assignments]
-- Values from defaults.
defaultValues = [(o, fieldType field, v)
| field@(FieldDesc { fieldOffset = o, fieldDefaultValue = Just v}) <- structFields desc
-- Don't include default values for fields that were explicitly assigned.
, not $ isExplicitlyAssigned field
-- Don't encode defaults for union members since they'd overwrite each other, and anyway
-- they wouldn't be valid unless the union tag specified them, which by default it doesn't,
-- except of course in the case of retroactively-added fields. So do include retro fields.
, not $ isNonRetroUnionMember field
-- Don't encode defaults for references. Setting them to null has the same effect.
, isDataFieldSize $ fieldValueSize v ]
explicitValues = [(fieldOffset f, fieldType f, v, fieldDefaultValue f) | (f, v) <- assignments]
-- Values of union tags.
unionValues = [(unionTagOffset u, BuiltinType BuiltinUInt8, UInt8Desc $ fromIntegral n)
unionValues = [(unionTagOffset u, BuiltinType BuiltinUInt8, UInt8Desc $ fromIntegral n,
Just $ unionDefault u)
| (FieldDesc {fieldUnion = Just u, fieldNumber = n}, _) <- assignments]
-- Default values of union tags.
unionDefaultValues = [(unionTagOffset u, BuiltinType BuiltinUInt8, unionDefault u)
| u <- structUnions desc
, not $ Set.member (unionNumber u) explicitlyAssignedUnions]
allValues = explicitValues ++ defaultValues ++ unionValues ++ unionDefaultValues
allData = [ (o * sizeInBits (fieldValueSize v), t, v)
| (o, t, v) <- allValues, isDataFieldSize $ fieldValueSize v ]
allReferences = [ (o, t, v) | (o, t, v) <- allValues
allValues = explicitValues ++ unionValues
allData = [ (o * sizeInBits (fieldValueSize v), t, v, d)
| (o, t, v, d) <- allValues, isDataFieldSize $ fieldValueSize v ]
allReferences = [ (o, t, v) | (o, t, v, _) <- allValues
, not $ isDataFieldSize $ fieldValueSize v ]
sortedData = sortBy compareValues allData
sortedReferences = sortBy compareValues allReferences
compareValues (o1, _, _) (o2, _, _) = compare o1 o2
sortedData = sortBy compareDataValues allData
compareDataValues (o1, _, _, _) (o2, _, _, _) = compare o1 o2
sortedReferences = sortBy compareReferenceValues allReferences
compareReferenceValues (o1, _, _) (o2, _, _) = compare o1 o2
dataBytes = encodeData (packingDataSize (structPacking desc) * 64) sortedData
(referenceBytes, children) = encodeReferences childOffset
......@@ -228,7 +205,7 @@ encodeList elementType elements = case elementSize elementType of
(refBytes, childBytes) = encodeReferences 0 (genericLength elements)
$ zipWith (\i v -> (i, elementType, v)) [0..] elements
size -> encodeData (roundUpToMultiple 64 (genericLength elements * sizeInBits size))
$ zipWith (\i v -> (i * sizeInBits size, elementType, v)) [0..] elements
$ zipWith (\i v -> (i * sizeInBits size, elementType, v, Nothing)) [0..] elements
encodeMessage (StructType desc) (StructValueDesc assignments) = let
(dataBytes, refBytes, childBytes) = encodeStruct desc assignments 0
......
......@@ -41,12 +41,9 @@ struct {{structName}} {
struct {{structChildName}};
{{/structChildren}}
static constexpr ::capnproto::WordCount DATA_SIZE = {{structDataSize}} * ::capnproto::WORDS;
static constexpr ::capnproto::WireReferenceCount REFERENCE_COUNT =
{{structReferenceCount}} * ::capnproto::REFERENCES;
{{#structDefault}}
static const ::capnproto::internal::AlignedData<{{defaultWordCount}}> DEFAULT;
{{/structDefault}}
static constexpr ::capnproto::internal::StructSize STRUCT_SIZE =
::capnproto::internal::StructSize({{structDataSize}} * ::capnproto::WORDS,
{{structReferenceCount}} * ::capnproto::REFERENCES);
{{#structFields}}
{{#fieldDefaultBytes}}
static const ::capnproto::internal::AlignedData<{{defaultWordCount}}> DEFAULT_{{fieldUpperCase}};
......@@ -140,7 +137,7 @@ private:
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Reader::get{{fieldTitleCase}}() {
return _reader.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS, {{fieldDefaultValue}});
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
......@@ -159,7 +156,7 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
return {{fieldType}}::Reader(_reader.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES,
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}{{fieldType}}::DEFAULT.words{{/fieldDefaultBytes}}));
{{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}}));
}
{{/fieldIsStruct}}
{{#fieldIsList}}
......@@ -179,11 +176,12 @@ inline {{fieldType}}::Reader {{structName}}::Reader::get{{fieldTitleCase}}() {
// {{structName}}.{{fieldDecl}}
{{#fieldIsPrimitive}}
inline {{fieldType}} {{structName}}::Builder::get{{fieldTitleCase}}() {
return _builder.getDataField<{{fieldType}}>({{fieldOffset}} * ::capnproto::ELEMENTS);
return _builder.getDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS{{fieldDefaultMask}});
}
inline void {{structName}}::Builder::set{{fieldTitleCase}}({{fieldType}} value) {
return _builder.setDataField<{{fieldType}}>(
{{fieldOffset}} * ::capnproto::ELEMENTS, value);
{{fieldOffset}} * ::capnproto::ELEMENTS, value{{fieldDefaultMask}});
}
{{/fieldIsPrimitive}}
{{#fieldIsBlob}}
......@@ -205,14 +203,14 @@ inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(un
{{#fieldIsStruct}}
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}() {
return {{fieldType}}::Builder(_builder.initStructField(
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::DEFAULT.words));
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE));
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
{{! TODO: Support per-field default values. }}
return {{fieldType}}::Builder(_builder.getStructField(
{{fieldOffset}} * ::capnproto::REFERENCES,
{{fieldOffset}} * ::capnproto::REFERENCES, {{fieldType}}::STRUCT_SIZE,
{{#fieldDefaultBytes}}DEFAULT_{{fieldUpperCase}}.words{{/fieldDefaultBytes}}
{{^fieldDefaultBytes}}{{fieldType}}::DEFAULT.words{{/fieldDefaultBytes}}));
{{^fieldDefaultBytes}}nullptr{{/fieldDefaultBytes}}));
}
{{/fieldIsStruct}}
{{#fieldIsNonStructList}}
......@@ -249,7 +247,7 @@ inline void {{structName}}::Builder::set{{fieldTitleCase}}(
inline {{fieldType}}::Builder {{structName}}::Builder::init{{fieldTitleCase}}(unsigned int size) {
return {{fieldType}}::Builder(_builder.initStructListField(
{{fieldOffset}} * ::capnproto::REFERENCES, size * ::capnproto::ELEMENTS,
{{fieldElementType}}::DEFAULT.words));
{{fieldElementType}}::STRUCT_SIZE));
}
inline {{fieldType}}::Builder {{structName}}::Builder::get{{fieldTitleCase}}() {
return {{fieldType}}::Builder(_builder.getListField(
......
......@@ -32,13 +32,7 @@ namespace {{namespaceName}} {
{{/fileNamespaces}}
{{#fileStructs}}
constexpr ::capnproto::WordCount {{structName}}::DATA_SIZE;
constexpr ::capnproto::WireReferenceCount {{structName}}::REFERENCE_COUNT;
{{#structDefault}}
const ::capnproto::internal::AlignedData<{{defaultWordCount}}> {{structName}}::DEFAULT = {
{ {{defaultByteList}} }
};
{{/structDefault}}
constexpr ::capnproto::internal::StructSize {{structName}}::STRUCT_SIZE;
{{#structFields}}
{{#fieldDefaultBytes}}
......
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