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
This diff is collapsed.
......@@ -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,
ElementCount elementCount) const;
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.
......@@ -112,11 +118,12 @@ public:
// Get a StructReader pointing at the same memory.
private:
SegmentBuilder* segment; // Memory segment in which the struct resides.
word* ptr; // Pointer to the encoded struct (data followed by references).
SegmentBuilder* segment; // Memory segment in which the struct resides.
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