Commit 648d267d authored by Kenton Varda's avatar Kenton Varda

Finish rewriting WireFormat.

parent 36a60467
......@@ -31,28 +31,23 @@ namespace {
const int READONLY_SEGMENT_START = 123;
const FieldDescriptor TEST_FIELDS[2] = {
{ 1 * WORDS, 0, 0, FieldSize::FOUR_BYTES, 1, 0, 0, 0 },
{ 1 * WORDS, 1, 0, FieldSize::REFERENCE, 1, 0, 0, 0 }
{ 1*WORDS, 0*REFERENCES, FieldSize::FOUR_BYTES, 0*ELEMENTS, 0*BYTES, 1, 0, 0*BYTES, 0*BITS, nullptr, nullptr },
{ 1*WORDS, 1*REFERENCES, FieldSize::REFERENCE , 0*ELEMENTS, 0*BYTES, 1, 0, 0*BYTES, 0*BITS, nullptr, nullptr }
};
extern const StructDescriptor TEST_STRUCT;
extern const Descriptor* const TEST_STRUCT_DEFAULT_REFS[1] = {
&TEST_STRUCT.base
};
const AlignedData<1> TEST_STRUCT_DEFAULT_DATA = {
const AlignedData<1> TEST_STRUCT_DEFAULT_VALUE = {
{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 }
};
const StructDescriptor TEST_STRUCT = {
{ Descriptor::Kind::STRUCT },
2,
WordCount8(1 * WORDS),
1,
FieldNumber(2),
1 * WORDS,
1 * REFERENCES,
TEST_FIELDS,
TEST_STRUCT_DEFAULT_DATA.bytes,
TEST_STRUCT_DEFAULT_REFS
TEST_STRUCT_DEFAULT_VALUE.words
};
const int READONLY_SEGMENT_END = 321;
......@@ -66,10 +61,8 @@ TEST(Descriptors, InReadOnlySegment) {
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_FIELDS);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_FIELDS);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT_DEFAULT_DATA);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT_DEFAULT_DATA);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT_DEFAULT_REFS);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT_DEFAULT_REFS);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT_DEFAULT_VALUE);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT_DEFAULT_VALUE);
EXPECT_LE((const void*)&READONLY_SEGMENT_START, (const void*)&TEST_STRUCT);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT);
}
......
......@@ -90,6 +90,10 @@ public:
// This constructor was intended to be private, but GCC complains about it being private in a
// bunch of places that don't appear to even call it, so I made it public. Oh well.
template <typename OtherNumber>
inline constexpr UnitRatio(const UnitRatio<OtherNumber, Unit1, Unit2>& other)
: unit1PerUnit2(other.unit1PerUnit2) {}
template <typename OtherNumber, typename Unit3>
inline constexpr UnitRatio<decltype(Number(1)*OtherNumber(1)), Unit3, Unit2>
operator*(UnitRatio<OtherNumber, Unit3, Unit1> other) {
......@@ -422,72 +426,38 @@ inline constexpr decltype(BYTES / ELEMENTS) bytesPerElement() {
#ifndef __CDT_PARSER__
template <typename T>
inline constexpr byte* operator+(byte* ptr, Quantity<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+(const byte* ptr, Quantity<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr byte* operator+=(byte*& ptr, Quantity<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+=(const byte* ptr, Quantity<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr word* operator+(word* ptr, Quantity<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+(const word* ptr, Quantity<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr word* operator+=(word*& ptr, Quantity<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+=(const word*& ptr, Quantity<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr byte* operator-(byte* ptr, Quantity<T, byte> offset) {
return ptr - offset / BYTES;
template <typename T, typename U>
inline constexpr U* operator+(U* ptr, Quantity<T, U> offset) {
return ptr + offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr const byte* operator-(const byte* ptr, Quantity<T, byte> offset) {
return ptr - offset / BYTES;
template <typename T, typename U>
inline constexpr const U* operator+(const U* ptr, Quantity<T, U> offset) {
return ptr + offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr byte* operator-=(byte*& ptr, Quantity<T, byte> offset) {
return ptr = ptr - offset / BYTES;
template <typename T, typename U>
inline constexpr U* operator+=(U*& ptr, Quantity<T, U> offset) {
return ptr = ptr + offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr const byte* operator-=(const byte* ptr, Quantity<T, byte> offset) {
return ptr = ptr - offset / BYTES;
template <typename T, typename U>
inline constexpr const U* operator+=(const U*& ptr, Quantity<T, U> offset) {
return ptr = ptr + offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr word* operator-(word* ptr, Quantity<T, word> offset) {
return ptr - offset / WORDS;
template <typename T, typename U>
inline constexpr U* operator-(U* ptr, Quantity<T, U> offset) {
return ptr - offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr const word* operator-(const word* ptr, Quantity<T, word> offset) {
return ptr - offset / WORDS;
template <typename T, typename U>
inline constexpr const U* operator-(const U* ptr, Quantity<T, U> offset) {
return ptr - offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr word* operator-=(word*& ptr, Quantity<T, word> offset) {
return ptr = ptr - offset / WORDS;
template <typename T, typename U>
inline constexpr U* operator-=(U*& ptr, Quantity<T, U> offset) {
return ptr = ptr - offset / unit<Quantity<T, U>>();
}
template <typename T>
inline constexpr const word* operator-=(const word*& ptr, Quantity<T, word> offset) {
return ptr = ptr - offset / WORDS;
template <typename T, typename U>
inline constexpr const U* operator-=(const U*& ptr, Quantity<T, U> offset) {
return ptr = ptr - offset / unit<Quantity<T, U>>();
}
#endif
......
// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "wire-format.h"
#include "descriptor.h"
#include <gtest/gtest.h>
namespace capnproto {
namespace internal {
namespace {
TEST(StructReader, RawData) {
AlignedData<2> data = {
{
// Struct ref, offset = 1, fieldCount = 1, dataSize = 1, referenceCount = 0
0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
// Content for the data segment.
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));
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, 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_FALSE(reader.getDataField<bool>(64 * ELEMENTS, false));
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 ));
}
} // namespace
} // namespace internal
} // namespace capnproto
......@@ -134,29 +134,13 @@ static_assert(REFERENCES * BITS_PER_REFERENCE / BITS_PER_BYTE / BYTES == sizeof(
// =======================================================================================
template <typename T, typename U>
static inline decltype(T() / U()) divRoundingUp(T a, U b) {
return (a + b - 1) / b;
}
template <typename T, typename U>
static CAPNPROTO_ALWAYS_INLINE(T divRoundingUp(Quantity<T, U> a, Quantity<T, U> b));
template <typename T, typename U>
static inline T divRoundingUp(Quantity<T, U> a, Quantity<T, U> b) {
return (a + b - unit<Quantity<T, U>>()) / b;
}
template <typename T, typename T2, typename U, typename U2>
static CAPNPROTO_ALWAYS_INLINE(
decltype(Quantity<T, U>() / UnitRatio<T2, U, U2>())
divRoundingUp(Quantity<T, U> a, UnitRatio<T2, U, U2> b));
template <typename T, typename T2, typename U, typename U2>
static inline decltype(Quantity<T, U>() / UnitRatio<T2, U, U2>())
divRoundingUp(Quantity<T, U> a, UnitRatio<T2, U, U2> b) {
return (a + (unit<Quantity<T2, U2>>() * b - unit<Quantity<T2, U>>())) / b;
}
struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(WordCount roundUpToWords(BitCount64 bits)) {
static_assert(sizeof(word) == 8, "This code assumes 64-bit words.");
uint64_t bits2 = bits / BITS;
return ((bits2 >> 6) + ((bits2 & 63) != 0)) * WORDS;
}
static CAPNPROTO_ALWAYS_INLINE(word* allocate(
WireReference*& ref, SegmentBuilder*& segment, WordCount amount)) {
word* ptr = segment->allocate(amount);
......@@ -208,23 +192,6 @@ struct WireHelpers {
return true;
}
static CAPNPROTO_ALWAYS_INLINE(bool isStructCompatible(
const StructDescriptor* descriptor, const WireReference* ref)) {
if (ref->structRef.fieldCount.get() >= descriptor->fieldCount) {
// The incoming struct has all of the fields that we know about.
return ref->structRef.dataSize.get() >= descriptor->dataSize &&
ref->structRef.refCount.get() >= descriptor->referenceCount;
} else if (ref->structRef.fieldCount.get() > FieldNumber(0)) {
// We know about more fields than the struct has, and the struct is non-empty.
const FieldDescriptor* field = &descriptor->fields[ref->structRef.fieldCount.get().value - 1];
return ref->structRef.dataSize.get() >= field->requiredDataSize &&
ref->structRef.refCount.get() >= field->requiredReferenceCount;
} else {
// The incoming struct has no fields, so is necessarily compatible.
return true;
}
}
static CAPNPROTO_ALWAYS_INLINE(StructBuilder initStructReference(
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount,
WireReference* ref, SegmentBuilder* segment)) {
......@@ -232,27 +199,25 @@ struct WireHelpers {
// Allocate space for the new struct.
word* ptr = allocate(ref, segment, dataSize + referenceCount * WORDS_PER_REFERENCE);
// Advance the pointer to point between the data and reference segments.
ptr += dataSize;
// Initialize the reference.
ref->setStruct(fieldCount, dataSize, referenceCount, segment->getOffsetTo(ptr));
// Build the StructBuilder.
return StructBuilder(segment, ptr);
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + dataSize));
} else {
followFars(ref, segment);
CAPNPROTO_ASSERT(ref->tag() == WireReference::STRUCT,
CAPNPROTO_DEBUG_ASSERT(ref->tag() == WireReference::STRUCT,
"Called getStruct{Field,Element}() but existing reference is not a struct.");
CAPNPROTO_ASSERT(ref->structRef.fieldCount.get() == fieldCount,
CAPNPROTO_DEBUG_ASSERT(ref->structRef.fieldCount.get() == fieldCount,
"Trying to update struct with incorrect field count.");
CAPNPROTO_ASSERT(ref->structRef.dataSize.get() == dataSize,
CAPNPROTO_DEBUG_ASSERT(ref->structRef.dataSize.get() == dataSize,
"Trying to update struct with incorrect data size.");
CAPNPROTO_ASSERT(ref->structRef.refCount.get() == referenceCount,
CAPNPROTO_DEBUG_ASSERT(ref->structRef.refCount.get() == referenceCount,
"Trying to update struct with incorrect reference count.");
return StructBuilder(segment, segment->getPtrUnchecked(ref->offset()));
word* ptr = segment->getPtrUnchecked(ref->offset());
return StructBuilder(segment, ptr, reinterpret_cast<WireReference*>(ptr + dataSize));
}
}
......@@ -262,10 +227,9 @@ struct WireHelpers {
CAPNPROTO_DEBUG_ASSERT(elementSize != FieldSize::STRUCT,
"Should have called initStructListReference() instead.");
// Calculate size of the list. Need to cast to uint here because a list can be up to
// 2**32-1 bits, so int would overflow. Plus uint division by a power of 2 is a bit shift.
WordCount wordCount = divRoundingUp(
elementCount * bitsPerElement(elementSize), BITS_PER_WORD);
// Calculate size of the list.
WordCount wordCount = roundUpToWords(
ElementCount64(elementCount) * bitsPerElement(elementSize));
// Allocate the list.
word* ptr = allocate(ref, segment, wordCount);
......@@ -321,15 +285,14 @@ struct WireHelpers {
}
static CAPNPROTO_ALWAYS_INLINE(StructReader readStructReference(
SegmentReader* segment, const WireReference* ref, const WireReference* defaultValue,
SegmentReader* segment, const WireReference* ref, const word* defaultValue,
int recursionLimit)) {
const word* ptr;
const word* ptr = ref->target();
if (ref == nullptr || ref->isNull()) {
useDefault:
segment = nullptr;
ref = defaultValue;
ptr = ref->target();
ref = reinterpret_cast<const WireReference*>(defaultValue);
} else if (segment != nullptr) {
if (CAPNPROTO_EXPECT_FALSE(recursionLimit == 0)) {
segment->getMessage()->reportInvalidData(
......@@ -352,8 +315,6 @@ struct WireHelpers {
WordCount size = ref->structRef.dataSize.get() +
ref->structRef.refCount.get() * WORDS_PER_REFERENCE;
ptr = ref->target();
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval(ptr, ptr + size))) {
segment->getMessage()->reportInvalidData(
"Message contained out-of-bounds struct reference.");
......@@ -369,12 +330,12 @@ struct WireHelpers {
}
static CAPNPROTO_ALWAYS_INLINE(ListReader readListReference(
SegmentReader* segment, const WireReference* ref, const WireReference* defaultValue,
SegmentReader* segment, const WireReference* ref, const word* defaultValue,
FieldSize expectedElementSize, int recursionLimit)) {
if (ref == nullptr || ref->isNull()) {
useDefault:
segment = nullptr;
ref = defaultValue;
ref = reinterpret_cast<const WireReference*>(defaultValue);
} else if (segment != nullptr) {
if (CAPNPROTO_EXPECT_FALSE(recursionLimit == 0)) {
segment->getMessage()->reportInvalidData(
......@@ -490,7 +451,7 @@ struct WireHelpers {
if (segment != nullptr) {
if (CAPNPROTO_EXPECT_FALSE(!segment->containsInterval(ptr, ptr +
divRoundingUp(ElementCount64(ref->listRef.elementCount()) * step, BITS_PER_WORD)))) {
roundUpToWords(ElementCount64(ref->listRef.elementCount()) * step)))) {
segment->getMessage()->reportInvalidData(
"Message contained out-of-bounds list reference.");
goto useDefault;
......@@ -547,114 +508,145 @@ struct WireHelpers {
// =======================================================================================
#if 0
StructBuilder StructBuilder::initRoot(SegmentBuilder* segment, word* location,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount) {
return WireHelpers::initStructReference(
fieldCount, dataSize, referenceCount,
reinterpret_cast<WireReference*>(location), segment);
}
StructBuilder StructBuilder::getStructFieldInternal(int refIndex) const {
StructBuilder StructBuilder::getStructField(
WireReferenceCount refIndex, FieldNumber fieldCount,
WordCount dataSize, WireReferenceCount referenceCount) const {
return WireHelpers::initStructReference(
descriptor->defaultReferences[refIndex]->asStruct(),
reinterpret_cast<WireReference*>(ptr) + refIndex, segment);
fieldCount, dataSize, referenceCount,
references + refIndex, segment);
}
ListBuilder StructBuilder::initListFieldInternal(int refIndex, uint32_t elementCount) const {
ListBuilder StructBuilder::initListField(
WireReferenceCount refIndex, FieldSize elementSize, ElementCount elementCount) const {
return WireHelpers::initListReference(
descriptor->defaultReferences[refIndex]->asList(),
reinterpret_cast<WireReference*>(ptr) + refIndex, segment, elementCount);
references + refIndex, segment,
elementCount, elementSize);
}
ListBuilder StructBuilder::getListFieldInternal(int refIndex) const {
ListBuilder StructBuilder::initStructListField(
WireReferenceCount refIndex, ElementCount elementCount,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount) const {
return WireHelpers::initStructListReference(
references + refIndex, segment,
elementCount, fieldCount, dataSize, referenceCount);
}
ListBuilder StructBuilder::getListField(WireReferenceCount refIndex, FieldSize elementSize) const {
return WireHelpers::getWritableListReference(
descriptor->defaultReferences[refIndex]->asList(),
reinterpret_cast<WireReference*>(ptr) + refIndex, segment);
elementSize, references + refIndex, segment);
}
StructReader StructBuilder::asReader() const {
return StructReader(descriptor, segment, ptr, descriptor->defaultData,
descriptor->fieldCount, 0 * BITS, std::numeric_limits<int>::max());
// HACK: We just give maxed-out field and reference counts because they are only used for
// checking for field presence.
static_assert(sizeof(WireReference::structRef.fieldCount) == 1,
"Has the maximum field count changed?");
static_assert(sizeof(WireReference::structRef.refCount) == 1,
"Has the maximum reference count changed?");
return StructReader(segment, data, FieldNumber(0xff),
intervalLength(data, reinterpret_cast<word*>(references)), 0xff * REFERENCES,
0 * BITS, std::numeric_limits<int>::max());
}
StructReader StructReader::readRootTrusted(const word* location, const word* defaultValue) {
return WireHelpers::readStructReference(nullptr, reinterpret_cast<const WireReference*>(location),
defaultValue, std::numeric_limits<int>::max());
}
StructReader StructReader::getStructFieldInternal(int fieldNumber, unsigned int refIndex) const {
return WireHelpers::readStructReference(
descriptor->defaultReferences[refIndex]->asStruct(),
fieldNumber < fieldCount
? reinterpret_cast<const WireReference*>(ptr) + refIndex
: nullptr,
segment, recursionLimit);
StructReader StructReader::readRoot(const word* location, const word* defaultValue,
SegmentReader* segment, int recursionLimit) {
if (segment->containsInterval(location, location + 1 * REFERENCES * WORDS_PER_REFERENCE)) {
segment->getMessage()->reportInvalidData("Root location out-of-bounds.");
location = nullptr;
}
return WireHelpers::readStructReference(segment, reinterpret_cast<const WireReference*>(location),
defaultValue, recursionLimit);
}
ListReader StructReader::getListFieldInternal(int fieldNumber, unsigned int refIndex) const {
StructReader StructReader::getStructField(
WireReferenceCount refIndex, const word* defaultValue) const {
const WireReference* ref = refIndex >= referenceCount ? nullptr :
reinterpret_cast<const WireReference*>(
reinterpret_cast<const word*>(ptr) + dataSize) + refIndex;
return WireHelpers::readStructReference(segment, ref, defaultValue, recursionLimit);
}
ListReader StructReader::getListField(
WireReferenceCount refIndex, FieldSize expectedElementSize, const word* defaultValue) const {
const WireReference* ref = refIndex >= referenceCount ? nullptr :
reinterpret_cast<const WireReference*>(
reinterpret_cast<const word*>(ptr) + dataSize) + refIndex;
return WireHelpers::readListReference(
descriptor->defaultReferences[refIndex]->asList(),
fieldNumber < fieldCount
? reinterpret_cast<const WireReference*>(ptr) + refIndex
: nullptr,
segment, recursionLimit);
segment, ref, defaultValue, expectedElementSize, recursionLimit);
}
StructBuilder ListBuilder::getStructElementInternal(
unsigned int index, WordCount elementSize) const {
return StructBuilder(
descriptor->elementDescriptor->asStruct(), segment,
ptr + elementSize * index);
StructBuilder ListBuilder::getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const {
word* structPtr = ptr + elementSize * index;
return StructBuilder(segment, structPtr,
reinterpret_cast<WireReference*>(structPtr + structDataSize));
}
ListBuilder ListBuilder::initListElementInternal(unsigned int index, uint32_t size) const {
ListBuilder ListBuilder::initListElement(
WireReferenceCount index, FieldSize elementSize, ElementCount elementCount) const {
return WireHelpers::initListReference(
descriptor->elementDescriptor->asList(),
reinterpret_cast<WireReference*>(ptr) + index,
segment, size);
reinterpret_cast<WireReference*>(ptr) + index, segment,
elementCount, elementSize);
}
ListBuilder ListBuilder::getListElementInternal(unsigned int index) const {
ListBuilder ListBuilder::initStructListElement(
WireReferenceCount index, ElementCount elementCount,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount) const {
return WireHelpers::initStructListReference(
reinterpret_cast<WireReference*>(ptr) + index, segment,
elementCount, fieldCount, dataSize, referenceCount);
}
ListBuilder ListBuilder::getListElement(WireReferenceCount index, FieldSize elementSize) const {
return WireHelpers::getWritableListReference(
descriptor->elementDescriptor->asList(),
reinterpret_cast<WireReference*>(ptr) + index,
segment);
elementSize, reinterpret_cast<WireReference*>(ptr) + index, segment);
}
ListReader ListBuilder::asReader() const {
return ListReader(descriptor, segment, ptr, elementCount,
sizeInBits(descriptor->elementSize),
descriptor->elementSize == FieldSize::STRUCT
? descriptor->elementDescriptor->asStruct()->fieldCount : 0,
1 << 30);
ListReader ListBuilder::asReader(FieldSize elementSize) const {
return ListReader(segment, ptr, elementCount, bitsPerElement(elementSize),
std::numeric_limits<int>::max());
}
StructReader ListReader::getStructElementInternal(unsigned int index) const {
const StructDescriptor* elementDescriptor;
if (ptr == nullptr) {
elementDescriptor = descriptor->defaultReferences()[index]->asStruct();
} else {
elementDescriptor = descriptor->elementDescriptor->asStruct();
ListReader ListBuilder::asReader(FieldNumber fieldCount, WordCount dataSize,
WireReferenceCount referenceCount) const {
return ListReader(segment, ptr, elementCount,
(dataSize + referenceCount * WORDS_PER_REFERENCE) * BITS_PER_WORD / ELEMENTS,
fieldCount, dataSize, referenceCount, std::numeric_limits<int>::max());
}
if (CAPNPROTO_EXPECT_FALSE(recursionLimit == 0)) {
StructReader ListReader::getStructElement(ElementCount index, const word* defaultValue) const {
if (CAPNPROTO_EXPECT_FALSE((segment != nullptr) & (recursionLimit == 0))) {
segment->getMessage()->reportInvalidData(
"Message is too deeply-nested or contains cycles.");
return WireHelpers::readStructReference(nullptr, nullptr, defaultValue, recursionLimit);
} else {
BitCount64 indexBit = static_cast<uint64_t>(index) * stepBits;
BitCount64 indexBit = ElementCount64(index) * stepBits;
return StructReader(
elementDescriptor, segment,
reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE,
descriptor->defaultData, structFieldCount, indexBit % BITS_PER_BYTE,
segment, reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE,
structFieldCount, structDataSize, structReferenceCount, indexBit % BITS_PER_BYTE,
recursionLimit - 1);
}
}
return StructReader(elementDescriptor, segment, nullptr, descriptor->defaultData, 0, 0 * BITS, 0);
}
ListReader ListReader::getListElementInternal(unsigned int index) const {
if (ptr == nullptr) {
return WireHelpers::readListReference(
descriptor->defaultReferences()[index]->asList(),
nullptr, segment, recursionLimit);
} else {
ListReader ListReader::getListElement(
WireReferenceCount index, FieldSize expectedElementSize, const word* defaultValue) const {
return WireHelpers::readListReference(
descriptor->elementDescriptor->asList(),
reinterpret_cast<const WireReference*>(ptr) +
index * (stepBits / BITS_PER_WORD / WORDS_PER_REFERENCE),
segment, recursionLimit);
}
segment, reinterpret_cast<const WireReference*>(ptr) + index,
defaultValue, expectedElementSize, recursionLimit);
}
#endif
} // namespace internal
} // namespace capnproto
......@@ -81,9 +81,10 @@ private:
class StructBuilder {
public:
inline StructBuilder(): segment(nullptr), ptr(nullptr) {}
inline StructBuilder(): segment(nullptr), data(nullptr), references(nullptr) {}
static StructBuilder initRoot(SegmentBuilder* segment, word* location);
static StructBuilder initRoot(SegmentBuilder* segment, word* location,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount);
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataField(ElementCount offset) const);
......@@ -95,16 +96,21 @@ public:
ElementCount offset, typename NoInfer<T>::Type value) const);
// Set the data field value at the given offset.
inline StructBuilder getStructField(WireReferenceCount refIndex) const;
StructBuilder getStructField(
WireReferenceCount refIndex, FieldNumber fieldCount,
WordCount dataSize, WireReferenceCount referenceCount) const;
// Get the struct field at the given index in the reference segment. Allocates space for the
// struct if necessary.
inline ListBuilder initListField(WireReferenceCount refIndex, FieldSize fieldSize,
ListBuilder initListField(WireReferenceCount refIndex, FieldSize elementSize,
ElementCount elementCount) const;
ListBuilder initStructListField(
WireReferenceCount refIndex, ElementCount elementCount,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount) const;
// Allocate a new list of the given size for the field at the given index in the reference
// segment, and return a pointer to it.
inline ListBuilder getListField(WireReferenceCount refIndex) const;
ListBuilder getListField(WireReferenceCount refIndex, FieldSize elementSize) const;
// Get the already-allocated list field for the given reference index. Returns an empty list --
// NOT necessarily the default value -- if the field is not initialized.
......@@ -113,10 +119,11 @@ public:
private:
SegmentBuilder* segment; // Memory segment in which the struct resides.
word* ptr; // Pointer to the encoded struct (data followed by references).
word* data; // Pointer to the encoded data.
WireReference* references; // Pointer to the encoded references.
inline StructBuilder(SegmentBuilder* segment, word* ptr)
: segment(segment), ptr(ptr) {}
inline StructBuilder(SegmentBuilder* segment, word* data, WireReference* references)
: segment(segment), data(data), references(references) {}
friend class ListBuilder;
friend struct WireHelpers;
......@@ -128,6 +135,10 @@ public:
: segment(nullptr), ptr(nullptr), fieldCount(0), dataSize(0), referenceCount(0),
bit0Offset(0 * BITS), recursionLimit(0) {}
static StructReader readRootTrusted(const word* location, const word* defaultValue);
static StructReader readRoot(const word* location, const word* defaultValue,
SegmentReader* segment, int recursionLimit);
template <typename T>
CAPNPROTO_ALWAYS_INLINE(
T getDataField(ElementCount offset, typename NoInfer<T>::Type defaultValue) const);
......@@ -143,11 +154,13 @@ public:
// with later numbers, and therefore the offset being in-bounds alone does not prove that the
// struct contains the field.
inline StructReader getStructField(WireReferenceCount refIndex) 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
// initialized.
// initialized. defaultValue will be interpreted as a trusted message -- it must point at a
// struct reference, which in turn points at the struct value.
inline ListReader getListField(WireReferenceCount refIndex, FieldSize expectedElementSize) const;
ListReader getListField(WireReferenceCount refIndex, FieldSize expectedElementSize,
const word* defaultValue) const;
// Get the list field at the given index in the reference segment, or the default value if not
// initialized.
......@@ -208,20 +221,27 @@ public:
ElementCount index, typename NoInfer<T>::Type value) const);
// Set the element at the given index.
CAPNPROTO_ALWAYS_INLINE(StructBuilder getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize) const);
StructBuilder getStructElement(
ElementCount index, decltype(WORDS/ELEMENTS) elementSize, WordCount structDataSize) const;
// Get the struct element at the given index. elementSize is the size, in 64-bit words, of
// each element.
CAPNPROTO_ALWAYS_INLINE(
ListBuilder initListElement(WireReferenceCount index, ElementCount size) const);
ListBuilder initListElement(
WireReferenceCount index, FieldSize elementSize, ElementCount elementCount) const;
ListBuilder initStructListElement(
WireReferenceCount index, ElementCount elementCount,
FieldNumber fieldCount, WordCount dataSize, WireReferenceCount referenceCount) const;
// Create a new list element of the given size at the given index.
CAPNPROTO_ALWAYS_INLINE(ListBuilder getListElement(WireReferenceCount index) const);
ListBuilder getListElement(WireReferenceCount index, FieldSize elementSize) const;
// Get the existing list element at the given index.
ListReader asReader() const;
// Get a ListReader pointing at the same memory.
ListReader asReader(FieldSize elementSize) const;
// Get a ListReader pointing at the same memory. Use this version only for non-struct lists.
ListReader asReader(FieldNumber fieldCount, WordCount dataSize,
WireReferenceCount referenceCount) const;
// Get a ListReader pointing at the same memory. Use this version only for struct lists.
private:
SegmentBuilder* segment; // Memory segment in which the list resides.
......@@ -249,11 +269,11 @@ public:
CAPNPROTO_ALWAYS_INLINE(T getDataElement(ElementCount index) const);
// Get the element of the given type at the given index.
CAPNPROTO_ALWAYS_INLINE(StructReader getStructElement(ElementCount index) const);
StructReader getStructElement(ElementCount index, const word* defaultValue) const;
// Get the struct element at the given index.
CAPNPROTO_ALWAYS_INLINE(ListReader getListElement(
WireReferenceCount index, FieldSize expectedElementSize) const);
ListReader getListElement(WireReferenceCount index, FieldSize expectedElementSize,
const word* defaultValue) const;
// Get the list element at the given index.
private:
......@@ -273,7 +293,9 @@ private:
FieldNumber structFieldCount;
WordCount structDataSize;
WireReferenceCount structReferenceCount;
// If the elements are structs, the properties of the struct.
// If the elements are structs, the properties of the struct. The field and reference counts are
// only used to check for field presence; the data size is also used to compute the reference
// pointer.
int recursionLimit;
// Limits the depth of message structures to guard against stack-overflow-based DoS attacks.
......@@ -302,26 +324,26 @@ private:
template <typename T>
inline T StructBuilder::getDataField(ElementCount offset) const {
return reinterpret_cast<WireValue<T>*>(ptr)[offset / ELEMENTS].get();
return reinterpret_cast<WireValue<T>*>(data)[offset / ELEMENTS].get();
}
template <>
inline bool StructBuilder::getDataField<bool>(ElementCount offset) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS);
byte* b = reinterpret_cast<byte*>(ptr) + boffset / BITS_PER_BYTE;
byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE;
return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0;
}
template <typename T>
inline void StructBuilder::setDataField(
ElementCount offset, typename NoInfer<T>::Type value) const {
reinterpret_cast<WireValue<T>*>(ptr)[offset / ELEMENTS].set(value);
reinterpret_cast<WireValue<T>*>(data)[offset / ELEMENTS].set(value);
}
template <>
inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) const {
BitCount boffset = offset * (1 * BITS / ELEMENTS);
byte* b = reinterpret_cast<byte*>(ptr) + boffset / BITS_PER_BYTE;
byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE;
uint bitnum = boffset % BITS_PER_BYTE / BITS;
*reinterpret_cast<uint8_t*>(b) = (*reinterpret_cast<uint8_t*>(b) & ~(1 << bitnum))
| (static_cast<uint8_t>(value) << bitnum);
......@@ -332,7 +354,7 @@ inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) c
template <typename T>
T StructReader::getDataField(ElementCount offset, typename NoInfer<T>::Type defaultValue) const {
if (offset * bytesPerElement<T>() < dataSize * BYTES_PER_WORD) {
return reinterpret_cast<WireValue<T>*>(ptr)[offset / ELEMENTS].get();
return reinterpret_cast<const WireValue<T>*>(ptr)[offset / ELEMENTS].get();
} else {
return defaultValue;
}
......@@ -359,7 +381,7 @@ T StructReader::getDataFieldCheckingNumber(
// Intentionally use & rather than && to reduce branches.
if ((fieldNumber < fieldCount) &
(offset * bytesPerElement<T>() < dataSize * BYTES_PER_WORD)) {
return reinterpret_cast<WireValue<T>*>(ptr)[offset / ELEMENTS].get();
return reinterpret_cast<const WireValue<T>*>(ptr)[offset / ELEMENTS].get();
} else {
return defaultValue;
}
......
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