Commit dd46a758 authored by Kenton Varda's avatar Kenton Varda

Type-safe quantities catch bugs.

parent 92a64de2
// 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 "descriptor.h"
#include <gtest/gtest.h>
namespace capnproto {
namespace internal {
namespace {
const int READONLY_SEGMENT_START = 123;
const FieldDescriptor TEST_FIELDS[2] = {
{ WordCount8(1 * WORDS), 0, 0, FieldSize::FOUR_BYTES, 1, 0, 0, 0 },
{ WordCount8(1 * WORDS), 1, 0, FieldSize::REFERENCE, 1, 0, 0, 0 }
};
extern const StructDescriptor TEST_STRUCT;
extern const Descriptor* const TEST_STRUCT_DEFAULT_REFS[1] = {
&TEST_STRUCT.base
};
const AlignedData<1> TEST_STRUCT_DEFAULT_DATA = {
{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0 }
};
const StructDescriptor TEST_STRUCT = {
{ Descriptor::Kind::STRUCT },
2,
WordCount8(1 * WORDS),
1,
TEST_FIELDS,
TEST_STRUCT_DEFAULT_DATA.bytes,
TEST_STRUCT_DEFAULT_REFS
};
const int READONLY_SEGMENT_END = 321;
TEST(Descriptors, InReadOnlySegment) {
// It's extremely important that statically-initialized descriptors end up in the read-only
// segment, proving that they will not require any dynamic initialization at startup. We hackily
// assume that variables will be placed in each segment in the order that they appear, therefore
// if our test declarations above do in fact land in the read-only segment, they should appear
// between READONLY_SEGMENT_START and READONLY_SEGMENT_END.
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);
EXPECT_GE((const void*)&READONLY_SEGMENT_END , (const void*)&TEST_STRUCT);
}
} // namespace
} // namespace internal
} // namespace capnproto
......@@ -46,6 +46,15 @@ struct ListDescriptor;
struct StructDescriptor;
struct FieldDescriptor;
template <int wordCount>
union AlignedData {
// Useful for declaring static constant data blobs as an array of bytes, but forcing those
// bytes to be word-aligned.
uint8_t bytes[wordCount * sizeof(word)];
word words[wordCount];
};
struct Descriptor {
// This is the "base type" for descriptors that describe the target of a reference.
// StructDescriptor and ListDescriptor should be treated as if they subclass this type. However,
......@@ -79,17 +88,19 @@ enum class FieldSize: uint8_t {
// fields, since a struct cannot embed another struct inline.
};
inline int sizeInBits(FieldSize s) {
static const int table[] = {1, 8, 16, 32, 64, 64, 128, -1};
inline BitCount sizeInBits(FieldSize s) {
static constexpr BitCount table[] = {1*BITS, 8*BITS, 16*BITS, 32*BITS, 64*BITS,
64*BITS, 128*BITS, 0*BITS};
return table[static_cast<int>(s)];
}
inline int byteOffsetForFieldZero(FieldSize s) {
inline ByteCount byteOffsetForFieldZero(FieldSize s) {
// For the given field size, get the offset, in bytes, between a struct pointer and the location
// of the struct's first field, if the struct's first field is of the given type. We use this
// to adjust pointers when non-struct lists are converted to struct lists or vice versa.
static const int table[] = {1, 1, 2, 4, 8, 0, 8, 0};
static constexpr ByteCount table[] = {1*BYTES, 1*BYTES, 2*BYTES, 4*BYTES, 8*BYTES,
0*BYTES, 8*BYTES, 0*BYTES};
return table[static_cast<int>(s)];
}
......@@ -131,8 +142,9 @@ struct StructDescriptor {
uint8_t fieldCount;
// Number of fields in this type -- that we were aware of at compile time, of course.
uint8_t dataSize;
WordCount8 dataSize;
// Size of the data segment, in 64-bit words.
// TODO: Can we use WordCount here and still get static init?
uint8_t referenceCount;
// Number of references in the reference segment.
......@@ -146,9 +158,10 @@ struct StructDescriptor {
const Descriptor* const* defaultReferences;
// Array of descriptors describing the references.
inline uint32_t wordSize() const {
inline WordCount wordSize() const {
// Size of the struct in words.
return static_cast<uint32_t>(fieldCount) + static_cast<uint32_t>(referenceCount);
// TODO: Somehow use WORDS_PER_REFERENCE here.
return WordCount(dataSize) + referenceCount * WORDS;
}
};
static_assert(__builtin_offsetof(StructDescriptor, base) == 0,
......@@ -158,12 +171,12 @@ static_assert(__builtin_offsetof(StructDescriptor, base) == 0,
struct FieldDescriptor {
// Describes one field of a struct.
uint8_t requiredDataSize;
WordCount8 requiredDataSize;
// The minimum size of the data segment of any object which includes this field. This is always
// offset * size / 64 bits, rounded up. This value is useful for validating object references
// received on the wire -- if dataSize is insufficient to support fieldCount, don't trust it!
uint8_t requiredReferenceSize;
uint8_t requiredReferenceCount;
// The minimum size of the reference segment of any object which includes this field. Same deal
// as with requiredDataSize.
......
......@@ -24,7 +24,174 @@
#ifndef CAPNPROTO_MACROS_H_
#define CAPNPROTO_MACROS_H_
#include <inttypes.h>
#include "unit.h"
namespace capnproto {
// TODO: Rename file "common.h" since it's no longer just macros.
#define CAPNPROTO_DISALLOW_COPY(classname) \
classname(const classname&) = delete; \
classname& operator=(const classname&) = delete
typedef unsigned int uint;
class byte { uint8_t content; CAPNPROTO_DISALLOW_COPY(byte); };
class word { uint64_t content; CAPNPROTO_DISALLOW_COPY(word); };
// byte and word are opaque types with sizes of 8 and 64 bits, respectively. These types are useful
// only to make pointer arithmetic clearer. Since the contents are private, the only way to access
// them is to first reinterpret_cast to some other pointer type.
//
// Coping is disallowed because you should always use memcpy(). Otherwise, you may run afoul of
// aliasing rules (particularly when copying words).
//
// A pointer of type word* should always be word-aligned even if won't actually be dereferenced as
// that type.
static_assert(sizeof(byte) == 1, "uint8_t is not one byte?");
static_assert(sizeof(word) == 8, "uint64_t is not 8 bytes?");
namespace internal { class bit; }
#ifdef __CDT_PARSER__
// Eclipse gets confused by decltypes, so we'll feed it these simplified-yet-compatible definitions.
//
// We could also consider using these definitions in opt builds. Trouble is, the mangled symbol
// names of any functions that take these types as inputs would be affected, so it would be
// important to compile the Cap'n Proto library and the client app with the same flags.
typedef uint BitCount;
typedef uint8_t BitCount8;
typedef uint16_t BitCount16;
typedef uint32_t BitCount32;
typedef uint64_t BitCount64;
typedef uint ByteCount;
typedef uint8_t ByteCount8;
typedef uint16_t ByteCount16;
typedef uint32_t ByteCount32;
typedef uint64_t ByteCount64;
typedef uint WordCount;
typedef uint8_t WordCount8;
typedef uint16_t WordCount16;
typedef uint32_t WordCount32;
typedef uint64_t WordCount64;
constexpr BitCount BITS = 1;
constexpr ByteCount BYTES = 1;
constexpr WordCount WORDS = 1;
constexpr uint BITS_PER_BYTE = 8;
constexpr uint BITS_PER_WORD = 64;
constexpr uint BYTES_PER_WORD = 8;
#else
typedef UnitMeasure<uint, internal::bit> BitCount;
typedef UnitMeasure<uint8_t, internal::bit> BitCount8;
typedef UnitMeasure<uint16_t, internal::bit> BitCount16;
typedef UnitMeasure<uint32_t, internal::bit> BitCount32;
typedef UnitMeasure<uint64_t, internal::bit> BitCount64;
typedef UnitMeasure<uint, byte> ByteCount;
typedef UnitMeasure<uint8_t, byte> ByteCount8;
typedef UnitMeasure<uint16_t, byte> ByteCount16;
typedef UnitMeasure<uint32_t, byte> ByteCount32;
typedef UnitMeasure<uint64_t, byte> ByteCount64;
typedef UnitMeasure<uint, word> WordCount;
typedef UnitMeasure<uint8_t, word> WordCount8;
typedef UnitMeasure<uint16_t, word> WordCount16;
typedef UnitMeasure<uint32_t, word> WordCount32;
typedef UnitMeasure<uint64_t, word> WordCount64;
constexpr BitCount BITS = BitCount::ONE;
constexpr ByteCount BYTES = ByteCount::ONE;
constexpr WordCount WORDS = WordCount::ONE;
constexpr UnitRatio<uint, internal::bit, byte> BITS_PER_BYTE(8);
constexpr UnitRatio<uint, internal::bit, word> BITS_PER_WORD(64);
constexpr UnitRatio<uint, byte, word> BYTES_PER_WORD(8);
template <typename T>
inline constexpr byte* operator+(byte* ptr, UnitMeasure<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr + offset / BYTES;
}
template <typename T>
inline constexpr byte* operator+=(byte*& ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator+=(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr + offset / BYTES;
}
template <typename T>
inline constexpr word* operator+(word* ptr, UnitMeasure<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+(const word* ptr, UnitMeasure<T, word> offset) {
return ptr + offset / WORDS;
}
template <typename T>
inline constexpr word* operator+=(word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr const word* operator+=(const word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr + offset / WORDS;
}
template <typename T>
inline constexpr byte* operator-(byte* ptr, UnitMeasure<T, byte> offset) {
return ptr - offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator-(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr - offset / BYTES;
}
template <typename T>
inline constexpr byte* operator-=(byte*& ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr - offset / BYTES;
}
template <typename T>
inline constexpr const byte* operator-=(const byte* ptr, UnitMeasure<T, byte> offset) {
return ptr = ptr - offset / BYTES;
}
template <typename T>
inline constexpr word* operator-(word* ptr, UnitMeasure<T, word> offset) {
return ptr - offset / WORDS;
}
template <typename T>
inline constexpr const word* operator-(const word* ptr, UnitMeasure<T, word> offset) {
return ptr - offset / WORDS;
}
template <typename T>
inline constexpr word* operator-=(word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr - offset / WORDS;
}
template <typename T>
inline constexpr const word* operator-=(const word*& ptr, UnitMeasure<T, word> offset) {
return ptr = ptr - offset / WORDS;
}
#endif
inline constexpr ByteCount intervalLength(const byte* a, const byte* b) {
return uint(b - a) * BYTES;
}
inline constexpr WordCount intervalLength(const word* a, const word* b) {
return uint(b - a) * WORDS;
}
namespace internal {
#define CAPNPROTO_EXPECT_TRUE(condition) __builtin_expect(condition, true)
......
......@@ -21,7 +21,6 @@
// (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 <inttypes.h>
#include <cstddef>
#include "macros.h"
......@@ -36,6 +35,22 @@ class MessageReader;
class MessageBuilder;
class ReadLimiter;
struct SegmentId {
// TODO: Generalize this. class Id<Base, Type>.
uint32_t number;
inline constexpr SegmentId(): number(0) {}
inline constexpr explicit SegmentId(int number): number(number) {}
inline constexpr bool operator==(const SegmentId& other) { return number == other.number; }
inline constexpr bool operator!=(const SegmentId& other) { return number != other.number; }
inline constexpr bool operator<=(const SegmentId& other) { return number <= other.number; }
inline constexpr bool operator>=(const SegmentId& other) { return number >= other.number; }
inline constexpr bool operator< (const SegmentId& other) { return number < other.number; }
inline constexpr bool operator> (const SegmentId& other) { return number > other.number; }
};
class MessageReader {
// Abstract interface encapsulating a readable message. By implementing this interface, you can
// control how memory is allocated for the message. Or use MallocMessage to make things easy.
......@@ -43,7 +58,7 @@ class MessageReader {
public:
virtual ~MessageReader();
virtual SegmentReader* tryGetSegment(uint32_t index) = 0;
virtual SegmentReader* tryGetSegment(SegmentId id) = 0;
// Gets the segment with the given ID, or return nullptr if no such segment exists.
virtual void reportInvalidData(const char* description) = 0;
......@@ -78,10 +93,10 @@ class MessageBuilder: public MessageReader {
public:
virtual ~MessageBuilder();
virtual SegmentBuilder* getSegment(uint32_t id) = 0;
virtual SegmentBuilder* getSegment(SegmentId id) = 0;
// Get the segment with the given id. Crashes or throws an exception if no such segment exists.
virtual SegmentBuilder* getSegmentWithAvailable(uint32_t minimumSize) = 0;
virtual SegmentBuilder* getSegmentWithAvailable(WordCount minimumAvailable) = 0;
// Get a segment which has at least the given amount of space available, allocating it if
// necessary. Crashes or throws an exception if there is not enough memory.
......@@ -95,7 +110,7 @@ class ReadLimiter {
// and overlapping are not permitted by the Cap'n Proto format because in many cases they could
// be used to craft a deceptively small message which could consume excessive server resources to
// process, perhaps even sending it into an infinite loop. Actually detecting overlaps would be
// time-consuming, so instead we just keep track of how many bytes worth of data structures the
// time-consuming, so instead we just keep track of how many words worth of data structures the
// receiver has actually dereferenced and error out if this gets too high.
//
// This counting takes place as you call getters (for non-primitive values) on the message
......@@ -104,67 +119,55 @@ class ReadLimiter {
// only trigger in unreasonable cases.
public:
inline explicit ReadLimiter(); // No limit.
inline explicit ReadLimiter(int64_t limit); // Limit to the given number of bytes.
inline explicit ReadLimiter(); // No limit.
inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words.
inline bool canRead(uint32_t amount);
inline bool canRead(WordCount amount);
private:
int64_t counter;
WordCount64 limit;
};
class SegmentReader {
public:
inline SegmentReader(MessageReader* message, uint32_t id, const void* ptr, uint32_t size,
inline SegmentReader(MessageReader* message, SegmentId id, const word ptr[], WordCount size,
ReadLimiter* readLimiter);
CAPNPROTO_ALWAYS_INLINE(const void* getPtrChecked(
uint32_t offset, uint32_t bytesBefore, uint32_t bytesAfter));
CAPNPROTO_ALWAYS_INLINE(const word* getPtrChecked(
WordCount offset, WordCount before, WordCount after));
inline MessageReader* getMessage();
inline uint32_t getSegmentId();
inline SegmentId getSegmentId();
inline const void* getStartPtr();
inline uint32_t getSize();
inline const word* getStartPtr();
inline WordCount getOffsetTo(const word* ptr);
inline WordCount getSize();
private:
MessageReader* message;
uint32_t id;
uint32_t size;
const void* start;
SegmentId id;
WordCount size;
const word* start;
ReadLimiter* readLimiter;
SegmentReader(const SegmentReader& other) = delete;
SegmentReader& operator=(const SegmentReader& other) = delete;
void readLimitReached();
friend class SegmentBuilder;
};
class SegmentBuilder: public SegmentReader {
public:
inline SegmentBuilder(MessageBuilder* message, uint32_t id, void* ptr, uint32_t available);
inline SegmentBuilder(MessageBuilder* message, SegmentId id, word ptr[], WordCount available);
struct Allocation {
void* ptr;
uint32_t offset;
inline Allocation(): ptr(nullptr), offset(0) {}
inline Allocation(std::nullptr_t): ptr(nullptr), offset(0) {}
inline Allocation(void* ptr, uint32_t offset): ptr(ptr), offset(offset) {}
inline bool operator==(std::nullptr_t) const { return ptr == nullptr; }
};
CAPNPROTO_ALWAYS_INLINE(Allocation allocate(uint32_t amount));
inline void* getPtrUnchecked(uint32_t offset);
CAPNPROTO_ALWAYS_INLINE(word* allocate(WordCount amount));
inline word* getPtrUnchecked(WordCount offset);
inline MessageBuilder* getMessage();
private:
char* pos;
char* end;
word* pos;
word* end;
ReadLimiter dummyLimiter;
SegmentBuilder(const SegmentBuilder& other) = delete;
......@@ -177,64 +180,74 @@ private:
inline ReadLimiter::ReadLimiter()
// I didn't want to #include <limits> just for this one lousy constant.
: counter(0x7fffffffffffffffll) {}
: limit(uint64_t(0x7fffffffffffffffll) * WORDS) {}
inline ReadLimiter::ReadLimiter(int64_t limit): counter(limit) {}
inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(limit) {}
inline bool ReadLimiter::canRead(uint32_t amount) {
return (counter -= amount) >= 0;
inline bool ReadLimiter::canRead(WordCount amount) {
if (amount > limit) {
return false;
} else {
limit -= amount;
return true;
}
}
// -------------------------------------------------------------------
inline SegmentReader::SegmentReader(
MessageReader* message, uint32_t id, const void* ptr, uint32_t size, ReadLimiter* readLimiter)
inline SegmentReader::SegmentReader(MessageReader* message, SegmentId id, const word ptr[],
WordCount size, ReadLimiter* readLimiter)
: message(message), id(id), size(size), start(ptr), readLimiter(readLimiter) {}
inline const void* SegmentReader::getPtrChecked(uint32_t offset, uint32_t bytesBefore,
uint32_t bytesAfter) {
inline const word* SegmentReader::getPtrChecked(
WordCount offset, WordCount before, WordCount after) {
// Check bounds. Watch out for overflow and underflow here.
if (offset > size || bytesBefore > offset || bytesAfter > size - offset) {
if (offset > size ||
before > offset ||
after > size - offset) {
return nullptr;
} else {
// Enforce the read limit. Synchronization is not necessary because readLimit is just a rough
// counter to prevent infinite loops leading to DoS attacks.
if (CAPNPROTO_EXPECT_FALSE(!readLimiter->canRead(bytesBefore + bytesAfter))) {
if (CAPNPROTO_EXPECT_FALSE(!readLimiter->canRead(before + after))) {
message->reportReadLimitReached();
}
return reinterpret_cast<const char*>(start) + offset;
return start + offset;
}
}
inline MessageReader* SegmentReader::getMessage() { return message; }
inline uint32_t SegmentReader::getSegmentId() { return id; }
inline const void* SegmentReader::getStartPtr() { return start; }
inline uint32_t SegmentReader::getSize() { return size; }
inline SegmentId SegmentReader::getSegmentId() { return id; }
inline const word* SegmentReader::getStartPtr() { return start; }
inline WordCount SegmentReader::getOffsetTo(const word* ptr) {
return intervalLength(start, ptr);
}
inline WordCount SegmentReader::getSize() { return size; }
// -------------------------------------------------------------------
inline SegmentBuilder::SegmentBuilder(
MessageBuilder* message, uint32_t id, void* ptr, uint32_t available)
: SegmentReader(message, id, ptr, 0, &dummyLimiter),
pos(reinterpret_cast<char*>(ptr)),
MessageBuilder* message, SegmentId id, word ptr[], WordCount available)
: SegmentReader(message, id, ptr, 0 * WORDS, &dummyLimiter),
pos(ptr),
end(pos + available) {}
inline SegmentBuilder::Allocation SegmentBuilder::allocate(uint32_t amount) {
if (amount > end - pos) {
inline word* SegmentBuilder::allocate(WordCount amount) {
if (amount > intervalLength(pos, end)) {
return nullptr;
} else {
char* result = pos;
word* result = pos;
pos += amount;
size += amount;
return Allocation(result, result - reinterpret_cast<const char*>(start));
return result;
}
}
inline void* SegmentBuilder::getPtrUnchecked(uint32_t offset) {
inline word* SegmentBuilder::getPtrUnchecked(WordCount offset) {
// const_cast OK because SegmentBuilder's constructor always initializes its SegmentReader base
// class with a pointer that was originally non-const.
return const_cast<char*>(reinterpret_cast<const char*>(start) + offset);
return const_cast<word*>(start + offset);
}
inline MessageBuilder* SegmentBuilder::getMessage() {
......
// 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 "unit.h"
#include <gtest/gtest.h>
#include <iostream>
namespace capnproto {
namespace {
class Bytes;
class KiB;
class MiB;
typedef UnitMeasure<int, Bytes> ByteCount;
typedef UnitMeasure<int, KiB> KiBCount;
typedef UnitMeasure<int, MiB> MiBCount;
constexpr ByteCount BYTE = ByteCount::ONE;
constexpr KiBCount KIB = KiBCount::ONE;
constexpr MiBCount MIB = MiBCount::ONE;
constexpr UnitRatio<int, Bytes, KiB> BYTES_PER_KIB(1024);
constexpr UnitRatio<int, Bytes, MiB> BYTES_PER_MIB(1024*1024);
constexpr UnitRatio<int, KiB, MiB> KIB_PER_MIB(1024);
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, UnitMeasure<T, U> value) {
return os << (value / UnitMeasure<T, U>::ONE);
}
TEST(UnitMeasure, Basics) {
KiBCount k = 15 * KIB;
EXPECT_EQ(15, k / KIB);
EXPECT_EQ(16 * KIB, k + KIB);
k += KIB;
k *= 2048;
EXPECT_EQ(32 * MIB, k / KIB_PER_MIB);
EXPECT_TRUE(2 * KIB < 4 * KIB);
EXPECT_FALSE(8 * KIB < 4 * KIB);
}
} // namespace
} // namespace capnproto
// 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 "unit.h"
namespace capnproto {
} // namespace capnproto
// 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.
#ifndef CAPNPROTO_UNIT_H_
#define CAPNPROTO_UNIT_H_
namespace capnproto {
template <typename Number, typename Unit1, typename Unit2>
class UnitRatio {
public:
constexpr UnitRatio(Number unit1PerUnit2): unit1PerUnit2(unit1PerUnit2) {}
Number unit1PerUnit2;
};
template <typename T>
T any();
template <typename Number, typename Unit>
class UnitMeasure {
public:
inline constexpr UnitMeasure() {}
template <typename OtherNumber>
inline constexpr UnitMeasure(const UnitMeasure<OtherNumber, Unit>& other)
: value(other.value) {}
static constexpr UnitMeasure ONE = UnitMeasure(1);
template <typename OtherNumber>
inline constexpr auto operator+(const UnitMeasure<OtherNumber, Unit>& other) const
-> UnitMeasure<decltype(any<Number>() + any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() + any<OtherNumber>()), Unit>(value + other.value);
}
template <typename OtherNumber>
inline constexpr auto operator-(const UnitMeasure<OtherNumber, Unit>& other) const
-> UnitMeasure<decltype(any<Number>() - any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() - any<OtherNumber>()), Unit>(value - other.value);
}
template <typename OtherNumber>
inline constexpr auto operator*(OtherNumber other) const
-> UnitMeasure<decltype(any<Number>() * other), Unit> {
return UnitMeasure<decltype(any<Number>() * other), Unit>(value * other);
}
template <typename OtherNumber>
inline constexpr auto operator/(OtherNumber other) const
-> UnitMeasure<decltype(any<Number>() / other), Unit> {
return UnitMeasure<decltype(any<Number>() / other), Unit>(value / other);
}
template <typename OtherNumber>
inline constexpr auto operator/(const UnitMeasure<OtherNumber, Unit>& other) const
-> decltype(any<Number>() / any<OtherNumber>()) {
return value / other.value;
}
template <typename OtherNumber>
inline constexpr auto operator%(const UnitMeasure<OtherNumber, Unit>& other) const
-> decltype(any<Number>() % any<OtherNumber>()) {
return value % other.value;
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator*(const UnitRatio<OtherNumber, OtherUnit, Unit>& ratio) const
-> UnitMeasure<decltype(any<Number>() * any<OtherNumber>()), OtherUnit> {
return UnitMeasure<decltype(any<Number>() * any<OtherNumber>()), OtherUnit>(
value * ratio.unit1PerUnit2);
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator/(const UnitRatio<OtherNumber, Unit, OtherUnit>& ratio) const
-> UnitMeasure<decltype(any<Number>() / any<OtherNumber>()), OtherUnit> {
return UnitMeasure<decltype(any<Number>() / any<OtherNumber>()), OtherUnit>(
value / ratio.unit1PerUnit2);
}
template <typename OtherNumber, typename OtherUnit>
inline constexpr auto operator%(const UnitRatio<OtherNumber, Unit, OtherUnit>& ratio) const
-> UnitMeasure<decltype(any<Number>() % any<OtherNumber>()), Unit> {
return UnitMeasure<decltype(any<Number>() % any<OtherNumber>()), Unit>(
value % ratio.unit1PerUnit2);
}
template <typename OtherNumber>
inline constexpr bool operator==(const UnitMeasure<OtherNumber, Unit>& other) const {
return value == other.value;
}
template <typename OtherNumber>
inline constexpr bool operator!=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value != other.value;
}
template <typename OtherNumber>
inline constexpr bool operator<=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value <= other.value;
}
template <typename OtherNumber>
inline constexpr bool operator>=(const UnitMeasure<OtherNumber, Unit>& other) const {
return value >= other.value;
}
template <typename OtherNumber>
inline constexpr bool operator<(const UnitMeasure<OtherNumber, Unit>& other) const {
return value < other.value;
}
template <typename OtherNumber>
inline constexpr bool operator>(const UnitMeasure<OtherNumber, Unit>& other) const {
return value > other.value;
}
template <typename OtherNumber>
inline UnitMeasure& operator+=(const UnitMeasure<OtherNumber, Unit>& other) {
value += other.value;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator-=(const UnitMeasure<OtherNumber, Unit>& other) {
value -= other.value;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator*=(OtherNumber other) {
value *= other;
return *this;
}
template <typename OtherNumber>
inline UnitMeasure& operator/=(OtherNumber other) {
value /= other.value;
return *this;
}
private:
inline explicit constexpr UnitMeasure(Number value): value(value) {}
Number value;
template <typename OtherNumber, typename OtherUnit>
friend class UnitMeasure;
template <typename Number1, typename Number2, typename Unit2>
friend inline constexpr auto operator*(Number1 a, UnitMeasure<Number2, Unit2> b)
-> UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit2>;
};
template <typename Number, typename Unit>
constexpr UnitMeasure<Number, Unit> UnitMeasure<Number, Unit>::ONE;
template <typename Number1, typename Number2, typename Unit>
inline constexpr auto operator*(Number1 a, UnitMeasure<Number2, Unit> b)
-> UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit> {
return UnitMeasure<decltype(any<Number1>() * any<Number2>()), Unit>(a * b.value);
}
template <typename Number1, typename Number2, typename Unit, typename Unit2>
inline constexpr auto operator*(UnitRatio<Number1, Unit2, Unit> ratio,
UnitMeasure<Number2, Unit> measure)
-> decltype(measure * ratio) {
return measure * ratio;
}
} // namespace capnproto
#endif // CAPNPROTO_UNIT_H_
......@@ -24,47 +24,49 @@
#include "wire-format.h"
#include "message.h"
#include "descriptor.h"
#include <limits>
namespace capnproto {
namespace internal {
namespace debug {
bool fieldIsStruct(const StructDescriptor* descriptor, int fieldNumber, int refIndex) {
bool fieldIsStruct(const StructDescriptor* descriptor, uint fieldNumber, uint refIndex) {
return descriptor->fieldCount > fieldNumber &&
descriptor->fields[fieldNumber].size == FieldSize::REFERENCE &&
descriptor->fields[fieldNumber].offset == refIndex &&
descriptor->defaultReferences[refIndex]->kind == Descriptor::Kind::STRUCT;
}
bool fieldIsList(const StructDescriptor* descriptor, int fieldNumber, int refIndex) {
bool fieldIsList(const StructDescriptor* descriptor, uint fieldNumber, uint refIndex) {
return descriptor->fieldCount > fieldNumber &&
descriptor->fields[fieldNumber].size == FieldSize::REFERENCE &&
descriptor->fields[fieldNumber].offset == refIndex &&
descriptor->defaultReferences[refIndex]->kind == Descriptor::Kind::LIST;
}
bool fieldIsData(const StructDescriptor* descriptor, int fieldNumber, int dataOffset, int bitSize) {
bool fieldIsData(const StructDescriptor* descriptor, uint fieldNumber, uint dataOffset,
BitCount bitSize) {
return descriptor->fieldCount > fieldNumber &&
descriptor->fields[fieldNumber].size != FieldSize::REFERENCE &&
sizeInBits(descriptor->fields[fieldNumber].size) == bitSize &&
descriptor->fields[fieldNumber].offset == dataOffset;
}
bool dataFieldInRange(const StructDescriptor* descriptor, uint32_t dataOffset, uint32_t size) {
return descriptor->dataSize * sizeof(uint64_t) >= (dataOffset * size);
bool dataFieldInRange(const StructDescriptor* descriptor, uint dataOffset, ByteCount size) {
return descriptor->dataSize * BYTES_PER_WORD >= dataOffset * size;
}
bool bitFieldInRange(const StructDescriptor* descriptor, uint32_t offset) {
return descriptor->dataSize * sizeof(uint64_t) > offset / 64;
bool bitFieldInRange(const StructDescriptor* descriptor, BitCount offset) {
return descriptor->dataSize * BITS_PER_WORD >= offset;
}
bool refFieldIsStruct(const StructDescriptor* descriptor, int refIndex) {
bool refFieldIsStruct(const StructDescriptor* descriptor, uint refIndex) {
return descriptor->referenceCount > refIndex &&
descriptor->defaultReferences[refIndex]->kind == Descriptor::Kind::STRUCT;
}
bool refFieldIsList(const StructDescriptor* descriptor, int refIndex) {
bool refFieldIsList(const StructDescriptor* descriptor, uint refIndex) {
return descriptor->referenceCount > refIndex &&
descriptor->defaultReferences[refIndex]->kind == Descriptor::Kind::LIST;
}
......@@ -72,21 +74,21 @@ bool refFieldIsList(const StructDescriptor* descriptor, int refIndex) {
bool elementsAreStructs(const ListDescriptor* descriptor) {
return descriptor->elementSize == FieldSize::STRUCT;
}
bool elementsAreStructs(const ListDescriptor* descriptor, uint32_t wordSize) {
bool elementsAreStructs(const ListDescriptor* descriptor, WordCount wordSize) {
return descriptor->elementSize == FieldSize::STRUCT &&
descriptor->elementDescriptor->asStruct()->wordSize() == wordSize;
}
bool elementsAreLists(const ListDescriptor* descriptor) {
return descriptor->elementSize == FieldSize::REFERENCE;
}
bool elementsAreData(const ListDescriptor* descriptor, int bitSize) {
bool elementsAreData(const ListDescriptor* descriptor, BitCount bitSize) {
switch (descriptor->elementSize) {
case FieldSize::REFERENCE:
case FieldSize::KEY_REFERENCE:
case FieldSize::STRUCT:
return false;
default:
return true;
return sizeInBits(descriptor->elementSize) == bitSize;
}
}
......@@ -94,6 +96,8 @@ bool elementsAreData(const ListDescriptor* descriptor, int bitSize) {
// =======================================================================================
static const WordCount WORDS_PER_REFERENCE = 1 * WORDS;
struct WireReference {
// A reference, in exactly the format in which it appears on the wire.
......@@ -107,7 +111,11 @@ struct WireReference {
STRUCT = 0,
LIST = 1,
CAPABILITY = 2,
FAR = 3
FAR = 3,
RESERVED_4 = 4,
RESERVED_5 = 5,
RESERVED_6 = 6,
RESERVED_7 = 7
};
WireValue<uint32_t> offsetAndTag;
......@@ -115,9 +123,13 @@ struct WireReference {
union {
struct {
WireValue<uint8_t> fieldCount;
WireValue<uint8_t> dataSize;
WireValue<WordCount8> dataWordCount;
WireValue<uint8_t> refCount;
WireValue<uint8_t> reserved0;
inline WordCount wordSize() const {
return dataWordCount.get() + refCount.get() * WORDS_PER_REFERENCE;
}
} structRef;
// Also covers capabilities.
......@@ -127,98 +139,101 @@ struct WireReference {
CAPNPROTO_ALWAYS_INLINE(FieldSize elementSize() const) {
return static_cast<FieldSize>(elementSizeAndCount.get() >> 29);
}
CAPNPROTO_ALWAYS_INLINE(uint32_t elementCount() const) {
CAPNPROTO_ALWAYS_INLINE(uint elementCount() const) {
return elementSizeAndCount.get() & 0x1fffffffu;
}
} listRef;
struct {
WireValue<uint32_t> segmentId;
WireValue<SegmentId> segmentId;
} farRef;
};
CAPNPROTO_ALWAYS_INLINE(bool isNull() const) { return offsetAndTag.get() == 0; }
CAPNPROTO_ALWAYS_INLINE(uint32_t offset() const) { return offsetAndTag.get() & ~7; }
CAPNPROTO_ALWAYS_INLINE(int tag() const) { return offsetAndTag.get() & 7; }
CAPNPROTO_ALWAYS_INLINE(WordCount offset() const) {
return (offsetAndTag.get() >> 3) * WORDS;
}
CAPNPROTO_ALWAYS_INLINE(Tag tag() const) {
return static_cast<Tag>(offsetAndTag.get() & 7);
}
CAPNPROTO_ALWAYS_INLINE(void setTagAndOffset(Tag tag, uint32_t offset)) {
CAPNPROTO_DEBUG_ASSERT((offset & 7) == 0, "Offsets must be word-aligned.");
offsetAndTag.set(offset | tag);
CAPNPROTO_ALWAYS_INLINE(void setTagAndOffset(Tag tag, WordCount offset)) {
offsetAndTag.set(((offset / WORDS) << 3) | tag);
}
CAPNPROTO_ALWAYS_INLINE(void setStruct(const StructDescriptor* descriptor, uint32_t offset)) {
CAPNPROTO_ALWAYS_INLINE(void setStruct(const StructDescriptor* descriptor, WordCount offset)) {
setTagAndOffset(STRUCT, offset);
structRef.fieldCount.set(descriptor->fieldCount);
structRef.dataSize.set(descriptor->dataSize);
structRef.dataWordCount.set(WordCount8(descriptor->dataSize));
structRef.refCount.set(descriptor->referenceCount);
structRef.reserved0.set(0);
}
CAPNPROTO_ALWAYS_INLINE(void setList(
const ListDescriptor* descriptor, uint32_t elementCount, uint32_t offset)) {
const ListDescriptor* descriptor, int elementCount, WordCount offset)) {
setTagAndOffset(LIST, offset);
CAPNPROTO_DEBUG_ASSERT((elementCount >> 29) == 0, "Lists are limited to 2**29 elements.");
listRef.elementSizeAndCount.set(
(static_cast<uint32_t>(descriptor->elementSize) << 29) | elementCount);
(static_cast<int>(descriptor->elementSize) << 29) | elementCount);
}
CAPNPROTO_ALWAYS_INLINE(void setFar(uint32_t segmentId, uint32_t offset)) {
CAPNPROTO_ALWAYS_INLINE(void setFar(SegmentId segmentId, WordCount offset)) {
setTagAndOffset(FAR, offset);
farRef.segmentId.set(segmentId);
}
};
static_assert(sizeof(WireReference) == 8, "Layout of capnproto::WireReference is wrong.");
static_assert(sizeof(WireReference) == sizeof(word),
"Layout of capnproto::WireReference is wrong. It must be exactly one word or all hell will "
"break loose. (At the very least WORDS_PER_REFERENCE needs updating, but that's "
"probably not all.)");
// =======================================================================================
template <typename T>
static CAPNPROTO_ALWAYS_INLINE(T divRoundingUp(T a, T b));
template <typename T>
static inline T divRoundingUp(T a, T b) {
template <typename T, typename U>
static inline decltype(T() / U()) divRoundingUp(T a, U b) {
return (a + b - 1) / b;
}
template <typename T>
static inline const void* offsetPtr(const void* ptr, int amount) {
return reinterpret_cast<const T*>(ptr) + amount;
}
template <typename T>
static inline void* offsetPtr(void* ptr, int amount) {
return reinterpret_cast<T*>(ptr) + amount;
template <typename T, typename U>
static CAPNPROTO_ALWAYS_INLINE(T divRoundingUp(UnitMeasure<T, U> a, UnitMeasure<T, U> b));
template <typename T, typename U>
static inline T divRoundingUp(UnitMeasure<T, U> a, UnitMeasure<T, U> b) {
return (a + b - UnitMeasure<T, U>::ONE) / b;
}
template <typename T>
static inline T* takeFromAllocation(SegmentBuilder::Allocation& allocation, int count = 1) {
T* result = reinterpret_cast<T*>(allocation.ptr);
allocation.ptr = result + count;
allocation.offset += sizeof(T) * count;
return result;
template <typename T, typename T2, typename U, typename U2>
static CAPNPROTO_ALWAYS_INLINE(
decltype(UnitMeasure<T, U>() / UnitRatio<T2, U, U2>(1))
divRoundingUp(UnitMeasure<T, U> a, UnitRatio<T2, U, U2> b));
template <typename T, typename T2, typename U, typename U2>
static inline decltype(UnitMeasure<T, U>() / UnitRatio<T2, U, U2>(1))
divRoundingUp(UnitMeasure<T, U> a, UnitRatio<T2, U, U2> b) {
return (a + (UnitMeasure<T2, U2>::ONE * b - UnitMeasure<T2, U>::ONE)) / b;
}
struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(SegmentBuilder::Allocation allocate(
WireReference*& ref, SegmentBuilder*& segment, uint32_t size)) {
SegmentBuilder::Allocation allocation = segment->allocate(size);
static CAPNPROTO_ALWAYS_INLINE(word* allocate(
WireReference*& ref, SegmentBuilder*& segment, WordCount amount)) {
word* ptr = segment->allocate(amount);
if (allocation == nullptr) {
if (ptr == nullptr) {
// Need to allocate in a new segment.
// Loop here just in case we ever make Segment::allocate() thread-safe -- in this case another
// thread could have grabbed the space between when we asked the message for the segment and
// when we asked the segment to allocate space.
do {
segment = segment->getMessage()->getSegmentWithAvailable(size + sizeof(WireReference));
allocation = segment->allocate(size + sizeof(WireReference));
} while (CAPNPROTO_EXPECT_FALSE(allocation == nullptr));
segment = segment->getMessage()->getSegmentWithAvailable(amount + WORDS_PER_REFERENCE);
ptr = segment->allocate(amount + WORDS_PER_REFERENCE);
} while (CAPNPROTO_EXPECT_FALSE(ptr == nullptr));
ref->setFar(segment->getSegmentId(), allocation.offset);
ref = takeFromAllocation<WireReference>(allocation);
ref->setFar(segment->getSegmentId(), segment->getOffsetTo(ptr));
ref = reinterpret_cast<WireReference*>(ptr);
// Allocated space follows new reference.
return allocation;
return ptr + WORDS_PER_REFERENCE;
} else {
return allocation;
return ptr;
}
}
......@@ -237,7 +252,7 @@ struct WireHelpers {
return false;
}
ref = reinterpret_cast<const WireReference*>(
segment->getPtrChecked(ref->offset(), 0, sizeof(WireReference)));
segment->getPtrChecked(ref->offset(), 0 * WORDS, WORDS_PER_REFERENCE));
return ref != nullptr;
} else {
return true;
......@@ -248,13 +263,13 @@ struct WireHelpers {
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 &&
return ref->structRef.dataWordCount.get() >= descriptor->dataSize &&
ref->structRef.refCount.get() >= descriptor->referenceCount;
} else if (ref->structRef.fieldCount.get() > 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() - 1];
return ref->structRef.dataSize.get() >= field->requiredDataSize &&
ref->structRef.refCount.get() >= field->requiredReferenceSize;
return ref->structRef.dataWordCount.get() >= field->requiredDataSize &&
ref->structRef.refCount.get() >= field->requiredReferenceCount;
} else {
// The incoming struct has no fields, so is necessarily compatible.
return true;
......@@ -264,20 +279,17 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(StructBuilder initStructReference(
const StructDescriptor* descriptor, WireReference* ref, SegmentBuilder* segment)) {
if (ref->isNull()) {
// Calculate the size of the struct.
uint32_t size = (descriptor->dataSize + descriptor->referenceCount) * sizeof(uint64_t);
// Allocate space for the new struct.
SegmentBuilder::Allocation allocation = allocate(ref, segment, size);
word* ptr = allocate(ref, segment, descriptor->wordSize());
// Advance the pointer to point between the data and reference segments.
takeFromAllocation<uint64_t>(allocation, descriptor->dataSize);
ptr += descriptor->dataSize;
// Initialize the reference.
ref->setStruct(descriptor, allocation.offset);
ref->setStruct(descriptor, segment->getOffsetTo(ptr));
// Build the StructBuilder.
return StructBuilder(descriptor, segment, allocation.ptr);
return StructBuilder(descriptor, segment, ptr);
} else {
followFars(ref, segment);
......@@ -296,41 +308,39 @@ struct WireHelpers {
static CAPNPROTO_ALWAYS_INLINE(ListBuilder initListReference(
const ListDescriptor* descriptor, WireReference* ref,
SegmentBuilder* segment, uint32_t elementCount)) {
SegmentBuilder* segment, uint elementCount)) {
if (descriptor->elementSize == FieldSize::STRUCT) {
const StructDescriptor* elementDescriptor =
descriptor->elementDescriptor->asStruct();
// Allocate the list, prefixed by a single WireReference.
SegmentBuilder::Allocation allocation =
allocate(ref, segment, sizeof(WireReference) +
elementDescriptor->wordSize() * elementCount * sizeof(uint64_t));
word* ptr = allocate(ref, segment,
WORDS_PER_REFERENCE + elementDescriptor->wordSize() * elementCount);
// Initialize the reference.
ref->setList(descriptor, elementCount, allocation.offset);
WireReference* structRef = takeFromAllocation<WireReference>(allocation);
// Skip past the data segment of the first struct.
takeFromAllocation<uint64_t>(allocation, elementDescriptor->dataSize);
ref->setList(descriptor, elementCount, segment->getOffsetTo(ptr));
// Initialize the struct reference.
structRef->setStruct(elementDescriptor, allocation.offset);
// The list is prefixed by a struct reference.
WireReference* structRef = reinterpret_cast<WireReference*>(ptr);
word* structPtr = ptr + WORDS_PER_REFERENCE + elementDescriptor->dataSize;
structRef->setStruct(elementDescriptor, segment->getOffsetTo(structPtr));
// Build the ListBuilder.
return ListBuilder(descriptor, segment, structRef, elementCount);
return ListBuilder(descriptor, segment, ptr, elementCount);
} else {
// Calculate size of the list.
uint32_t size = divRoundingUp<uint32_t>(
static_cast<uint32_t>(sizeInBits(descriptor->elementSize)) * elementCount, 8);
// 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(
sizeInBits(descriptor->elementSize) * elementCount, BITS_PER_WORD);
// Allocate the list.
SegmentBuilder::Allocation allocation = allocate(ref, segment, size);
word* ptr = allocate(ref, segment, wordCount);
// Initialize the reference.
ref->setList(descriptor, elementCount, allocation.offset);
ref->setList(descriptor, elementCount, segment->getOffsetTo(ptr));
// Build the ListBuilder.
return ListBuilder(descriptor, segment, allocation.ptr, elementCount);
return ListBuilder(descriptor, segment, ptr, elementCount);
}
}
......@@ -388,9 +398,9 @@ struct WireHelpers {
break;
}
const void* ptr = segment->getPtrChecked(ref->offset(),
ref->structRef.dataSize.get() * sizeof(uint8_t),
ref->structRef.refCount.get() * sizeof(WireReference));
const word* ptr = segment->getPtrChecked(ref->offset(),
ref->structRef.dataWordCount.get(),
ref->structRef.refCount.get() * WORDS_PER_REFERENCE);
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
......@@ -399,10 +409,10 @@ struct WireHelpers {
}
return StructReader(descriptor, segment, ptr, descriptor->defaultData,
ref->structRef.fieldCount.get(), 0, recursionLimit - 1);
ref->structRef.fieldCount.get(), 0 * BITS, recursionLimit - 1);
} while (false);
return StructReader(descriptor, segment, nullptr, descriptor->defaultData, 0, 0, 0);
return StructReader(descriptor, segment, nullptr, descriptor->defaultData, 0, 0 * BITS, 0);
}
static CAPNPROTO_ALWAYS_INLINE(ListReader readListReference(
......@@ -434,15 +444,15 @@ struct WireHelpers {
if (ref->listRef.elementSize() == FieldSize::STRUCT) {
// A struct list reference actually points to a struct reference which in turn points to the
// first struct in the list.
const void* ptrPtr =
segment->getPtrChecked(ref->offset(), 0, sizeof(WireReference));
const word* ptrPtr =
segment->getPtrChecked(ref->offset(), 0 * WORDS, WORDS_PER_REFERENCE);
if (CAPNPROTO_EXPECT_FALSE(ptrPtr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds list reference.");
break;
}
uint32_t size = ref->listRef.elementCount();
int size = ref->listRef.elementCount();
ref = reinterpret_cast<const WireReference*>(ptrPtr);
if (CAPNPROTO_EXPECT_FALSE(ref->tag() != WireReference::STRUCT)) {
......@@ -451,12 +461,10 @@ struct WireHelpers {
break;
}
int step = (ref->structRef.dataSize.get() + ref->structRef.refCount.get()) *
sizeof(uint8_t);
const void* ptr = segment->getPtrChecked(ref->offset(),
ref->structRef.dataSize.get() * sizeof(uint8_t),
ref->structRef.refCount.get() * sizeof(WireReference) +
step * (size - 1));
ref->structRef.dataWordCount.get(),
ref->structRef.refCount.get() * WORDS_PER_REFERENCE +
ref->structRef.wordSize() * (size - 1));
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
"Message contains out-of-bounds struct list reference.");
......@@ -467,7 +475,7 @@ struct WireHelpers {
// struct list. We need to manipulate the pointer to point at the first field of the
// struct. Together with the "stepBits", this will allow the struct list to be accessed as
// if it were a primitive list without branching.
ptr = offsetPtr<uint8_t>(ptr, -byteOffsetForFieldZero(descriptor->elementSize));
ptr = reinterpret_cast<const byte*>(ptr) - byteOffsetForFieldZero(descriptor->elementSize);
// Check whether the size is compatible.
bool compatible = true;
......@@ -477,7 +485,7 @@ struct WireHelpers {
case FieldSize::TWO_BYTES:
case FieldSize::FOUR_BYTES:
case FieldSize::EIGHT_BYTES:
compatible = ref->structRef.dataSize.get() > 0;
compatible = ref->structRef.dataWordCount.get() > 0 * WORDS;
break;
case FieldSize::REFERENCE:
......@@ -485,7 +493,7 @@ struct WireHelpers {
break;
case FieldSize::KEY_REFERENCE:
compatible = ref->structRef.dataSize.get() > 0 &&
compatible = ref->structRef.dataWordCount.get() > 0 * WORDS &&
ref->structRef.refCount.get() > 0;
break;
......@@ -500,16 +508,15 @@ struct WireHelpers {
break;
}
return ListReader(descriptor, segment, ptr, size, step * 8,
return ListReader(descriptor, segment, ptr, size, ref->structRef.wordSize() * BITS_PER_WORD,
ref->structRef.fieldCount.get(), recursionLimit - 1);
} else {
// The elements of the list are NOT structs.
int step = sizeInBits(ref->listRef.elementSize());
BitCount step = sizeInBits(ref->listRef.elementSize());
const void* ptr = segment->getPtrChecked(ref->offset(), 0,
divRoundingUp<uint64_t>(
implicit_cast<uint64_t>(ref->listRef.elementCount()) * step, 8));
const void* ptr = segment->getPtrChecked(ref->offset(), 0 * WORDS,
divRoundingUp(ref->listRef.elementCount() * BitCount64(step), BITS_PER_WORD));
if (CAPNPROTO_EXPECT_FALSE(ptr == nullptr)) {
segment->getMessage()->reportInvalidData(
......@@ -534,7 +541,8 @@ struct WireHelpers {
}
// Adjust the pointer to point where we expect it for a struct.
ptr = offsetPtr<uint8_t>(ptr, byteOffsetForFieldZero(descriptor->elementSize));
ptr = reinterpret_cast<const byte*>(ptr) +
byteOffsetForFieldZero(descriptor->elementSize);
return ListReader(descriptor, segment, ptr, ref->listRef.elementCount(),
sizeInBits(ref->listRef.elementSize()), 1, recursionLimit);
......@@ -549,7 +557,7 @@ struct WireHelpers {
case FieldSize::REFERENCE:
case FieldSize::KEY_REFERENCE:
case FieldSize::STRUCT:
return ListReader(descriptor, segment, nullptr, descriptor->defaultCount, 0, 0,
return ListReader(descriptor, segment, nullptr, descriptor->defaultCount, 0 * BITS, 0,
recursionLimit - 1);
default:
return ListReader(descriptor, segment, descriptor->defaultData, descriptor->defaultCount,
......@@ -580,7 +588,7 @@ ListBuilder StructBuilder::getListFieldInternal(int refIndex) const {
StructReader StructBuilder::asReader() const {
return StructReader(descriptor, segment, ptr, descriptor->defaultData,
descriptor->fieldCount, 0, 1 << 30);
descriptor->fieldCount, 0 * BITS, std::numeric_limits<int>::max());
}
StructReader StructReader::getStructFieldInternal(int fieldNumber, unsigned int refIndex) const {
......@@ -602,10 +610,10 @@ ListReader StructReader::getListFieldInternal(int fieldNumber, unsigned int refI
}
StructBuilder ListBuilder::getStructElementInternal(
unsigned int index, uint32_t elementWordSize) const {
unsigned int index, WordCount elementSize) const {
return StructBuilder(
descriptor->elementDescriptor->asStruct(), segment,
offsetPtr<uint64_t>(ptr, elementWordSize * index));
ptr + elementSize * index);
}
ListBuilder ListBuilder::initListElementInternal(unsigned int index, uint32_t size) const {
......@@ -641,14 +649,16 @@ StructReader ListReader::getStructElementInternal(unsigned int index) const {
segment->getMessage()->reportInvalidData(
"Message is too deeply-nested or contains cycles.");
} else {
uint64_t indexBit = static_cast<uint64_t>(index) * stepBits;
BitCount64 indexBit = static_cast<uint64_t>(index) * stepBits;
return StructReader(
elementDescriptor, segment, offsetPtr<uint8_t>(ptr, indexBit / 8),
descriptor->defaultData, structFieldCount, indexBit % 8, recursionLimit - 1);
elementDescriptor, segment,
reinterpret_cast<const byte*>(ptr) + indexBit / BITS_PER_BYTE,
descriptor->defaultData, structFieldCount, indexBit % BITS_PER_BYTE,
recursionLimit - 1);
}
}
return StructReader(elementDescriptor, segment, nullptr, descriptor->defaultData, 0, 0, 0);
return StructReader(elementDescriptor, segment, nullptr, descriptor->defaultData, 0, 0 * BITS, 0);
}
ListReader ListReader::getListElementInternal(unsigned int index, uint32_t size) const {
......@@ -659,7 +669,8 @@ ListReader ListReader::getListElementInternal(unsigned int index, uint32_t size)
} else {
return WireHelpers::readListReference(
descriptor->elementDescriptor->asList(),
reinterpret_cast<const WireReference*>(offsetPtr<uint64_t>(ptr, index * (stepBits / 64))),
reinterpret_cast<const WireReference*>(ptr) +
index * (stepBits / BITS_PER_WORD / WORDS_PER_REFERENCE),
segment, recursionLimit);
}
}
......
......@@ -30,7 +30,6 @@
#ifndef CAPNPROTO_WIRE_FORMAT_H_
#define CAPNPROTO_WIRE_FORMAT_H_
#include <inttypes.h>
#include "macros.h"
namespace capnproto {
......@@ -51,18 +50,18 @@ namespace debug {
// header is #included from generated headers, whereas descriptor.h is only #included in generated
// source files.
bool fieldIsStruct(const StructDescriptor* descriptor, int fieldNumber, int refIndex);
bool fieldIsList(const StructDescriptor* descriptor, int fieldNumber, int refIndex);
bool fieldIsData(const StructDescriptor* descriptor, int fieldNumber, int dataOffset,
int bitSize);
bool dataFieldInRange(const StructDescriptor* descriptor, uint32_t dataOffset, uint32_t size);
bool bitFieldInRange(const StructDescriptor* descriptor, uint32_t offset);
bool refFieldIsStruct(const StructDescriptor* descriptor, int refIndex);
bool refFieldIsList(const StructDescriptor* descriptor, int refIndex);
bool fieldIsStruct(const StructDescriptor* descriptor, uint fieldNumber, uint refIndex);
bool fieldIsList(const StructDescriptor* descriptor, uint fieldNumber, uint refIndex);
bool fieldIsData(const StructDescriptor* descriptor, uint fieldNumber, uint dataOffset,
BitCount bitSize);
bool dataFieldInRange(const StructDescriptor* descriptor, uint dataOffset, ByteCount size);
bool bitFieldInRange(const StructDescriptor* descriptor, BitCount offset);
bool refFieldIsStruct(const StructDescriptor* descriptor, uint refIndex);
bool refFieldIsList(const StructDescriptor* descriptor, uint refIndex);
bool elementsAreStructs(const ListDescriptor* descriptor);
bool elementsAreStructs(const ListDescriptor* descriptor, uint32_t wordSize);
bool elementsAreStructs(const ListDescriptor* descriptor, WordCount wordSize);
bool elementsAreLists(const ListDescriptor* descriptor);
bool elementsAreData(const ListDescriptor* descriptor, int bitSize);
bool elementsAreData(const ListDescriptor* descriptor, BitCount bitSize);
} // namespace debug
class StructBuilder;
......@@ -74,6 +73,14 @@ struct WireHelpers;
// -------------------------------------------------------------------
template <typename T>
struct NoInfer {
// Use NoInfer<T>::Type in place of T for a function parameter to prevent inference of the
// parameter. There's something in the standard library for this but I didn't want to #include
// type_traits or whatever.
typedef T Type;
};
template <typename T>
class WireValue {
// Wraps a primitive value as it appears on the wire. Namely, values are little-endian on the
......@@ -101,14 +108,19 @@ private:
class StructBuilder {
public:
inline StructBuilder(): descriptor(nullptr), segment(nullptr), ptr(nullptr) {}
static StructBuilder initRoot(const StructDescriptor* descriptor,
SegmentBuilder* segment, word* location);
template <typename T>
inline T getDataField(unsigned int offset) const;
inline T getDataField(uint 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.
template <typename T>
inline void setDataField(unsigned int offset, T value) const;
// Set the data field value at the given offset. Be careful to use the correct type.
inline void setDataField(uint offset, typename NoInfer<T>::Type value) const;
// Set the data field value at the given offset.
CAPNPROTO_ALWAYS_INLINE(StructBuilder getStructField(int refIndex) const);
// Get the struct field at the given index in the reference segment. Allocates space for the
......@@ -128,9 +140,9 @@ public:
private:
const StructDescriptor* descriptor; // Descriptor for the struct.
SegmentBuilder* segment; // Memory segment in which the struct resides.
void* ptr; // Pointer to the location between the struct's data and reference segments.
word* ptr; // Pointer to the location between the struct's data and reference segments.
inline StructBuilder(const StructDescriptor* descriptor, SegmentBuilder* segment, void* ptr)
inline StructBuilder(const StructDescriptor* descriptor, SegmentBuilder* segment, word* ptr)
: descriptor(descriptor), segment(segment), ptr(ptr) {}
StructBuilder getStructFieldInternal(int refIndex) const;
......@@ -146,17 +158,21 @@ private:
class StructReader {
public:
inline StructReader()
: descriptor(nullptr), segment(nullptr), ptr{nullptr, nullptr}, fieldCount(0),
bit0Offset(0 * BITS), recursionLimit(0) {}
template <typename T>
inline T getDataField(int fieldNumber, unsigned int offset) const;
inline T getDataField(int fieldNumber, uint 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.
CAPNPROTO_ALWAYS_INLINE(
StructReader getStructField(int fieldNumber, unsigned int refIndex) const);
StructReader getStructField(int fieldNumber, uint refIndex) const);
// Get the struct field at the given index in the reference segment, or the default value if not
// initialized.
CAPNPROTO_ALWAYS_INLINE(ListReader getListField(int fieldNumber, unsigned int refIndex) const);
CAPNPROTO_ALWAYS_INLINE(ListReader getListField(int fieldNumber, uint refIndex) const);
// Get the list field at the given index in the reference segment, or the default value if not
// initialized.
......@@ -168,10 +184,17 @@ private:
// ptr[0] points to the location between the struct's data and reference segments.
// ptr[1] points to the end of the *default* data segment.
// We put these in an array so we can choose between them without a branch.
// These pointers are not necessarily word-aligned -- they are aligned as well as necessary for
// the data they might point at. So if the struct has only one field that we know of, and it is
// of type Int16, then the pointers only need to be 16-bit aligned. Or if the struct has fields
// of type Int16 and Int64 (in that order), but the struct reference on the wire self-reported
// as having only one field (therefore, only the Int16), then ptr[0] need only be 16-bit aligned
// while ptr[1] must be 64-bit aligned. This relaxation of alignment is needed to handle the
// case where a list of primitives is upgraded to a list of structs.
int fieldCount; // Number of fields the struct is reported to have.
int bit0Offset;
BitCount bit0Offset;
// A special hack: When accessing a boolean with field number zero, pretend its offset is this
// instead of the usual zero. This is needed to allow a boolean list to be upgraded to a list
// of structs.
......@@ -181,13 +204,13 @@ private:
// Once this reaches zero, further pointers will be pruned.
inline StructReader(const StructDescriptor* descriptor, SegmentReader* segment,
const void* ptr, const void* defaultData, int fieldCount, int bit0Offset,
const void* ptr, const void* defaultData, int fieldCount, BitCount bit0Offset,
int recursionLimit)
: descriptor(descriptor), segment(segment), ptr{ptr, defaultData}, fieldCount(fieldCount),
bit0Offset(bit0Offset), recursionLimit(recursionLimit) {}
StructReader getStructFieldInternal(int fieldNumber, unsigned int refIndex) const;
ListReader getListFieldInternal(int fieldNumber, unsigned int refIndex) const;
StructReader getStructFieldInternal(int fieldNumber, uint refIndex) const;
ListReader getListFieldInternal(int fieldNumber, uint refIndex) const;
// The public methods are inlined and simply wrap these "Internal" methods after doing debug
// asserts. This way, debugging is enabled by the caller's compiler flags rather than
// libcapnproto's debug flags.
......@@ -201,26 +224,28 @@ private:
class ListBuilder {
public:
inline ListBuilder(): descriptor(nullptr), segment(nullptr), ptr(nullptr), elementCount(0) {}
inline uint32_t size();
// The number of elements in the list.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataElement(unsigned int index) const);
CAPNPROTO_ALWAYS_INLINE(T getDataElement(uint index) const);
// Get the element of the given type at the given index.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(void setDataElement(unsigned int index, T value) const);
// Set the element at the given index. Be careful to use the correct type.
CAPNPROTO_ALWAYS_INLINE(void setDataElement(uint index, typename NoInfer<T>::Type value) const);
// Set the element at the given index.
CAPNPROTO_ALWAYS_INLINE(
StructBuilder getStructElement(unsigned int index, uint32_t elementWordSize) const);
StructBuilder getStructElement(uint index, WordCount elementSize) const);
// Get the struct element at the given index. elementWordSize is the size, in 64-bit words, of
// each element.
CAPNPROTO_ALWAYS_INLINE(ListBuilder initListElement(unsigned int index, uint32_t size) const);
CAPNPROTO_ALWAYS_INLINE(ListBuilder initListElement(uint index, uint32_t size) const);
// Create a new list element of the given size at the given index.
CAPNPROTO_ALWAYS_INLINE(ListBuilder getListElement(unsigned int index) const);
CAPNPROTO_ALWAYS_INLINE(ListBuilder getListElement(uint index) const);
// Get the existing list element at the given index.
ListReader asReader() const;
......@@ -229,16 +254,16 @@ public:
private:
const ListDescriptor* descriptor; // Descriptor for the list.
SegmentBuilder* segment; // Memory segment in which the list resides.
void* ptr; // Pointer to the beginning of the list.
word* ptr; // Pointer to the beginning of the list.
uint32_t elementCount; // Number of elements in the list.
inline ListBuilder(const ListDescriptor* descriptor, SegmentBuilder* segment,
void* ptr, uint32_t size)
word* ptr, uint32_t size)
: descriptor(descriptor), segment(segment), ptr(ptr), elementCount(size) {}
StructBuilder getStructElementInternal(unsigned int index, uint32_t elementWordSize) const;
ListBuilder initListElementInternal(unsigned int index, uint32_t size) const;
ListBuilder getListElementInternal(unsigned int index) const;
StructBuilder getStructElementInternal(uint index, WordCount elementSize) const;
ListBuilder initListElementInternal(uint index, uint32_t size) const;
ListBuilder getListElementInternal(uint index) const;
// The public methods are inlined and simply wrap these "Internal" methods after doing debug
// asserts. This way, debugging is enabled by the caller's compiler flags rather than
// libcapnproto's debug flags.
......@@ -249,17 +274,21 @@ private:
class ListReader {
public:
inline ListReader()
: descriptor(nullptr), segment(nullptr), ptr(nullptr), elementCount(0), stepBits(0 * BITS),
structFieldCount(0), recursionLimit(0) {}
inline uint32_t size();
// The number of elements in the list.
template <typename T>
CAPNPROTO_ALWAYS_INLINE(T getDataElement(unsigned int index) const);
CAPNPROTO_ALWAYS_INLINE(T getDataElement(uint index) const);
// Get the element of the given type at the given index.
CAPNPROTO_ALWAYS_INLINE(StructReader getStructElement(unsigned int index) const);
CAPNPROTO_ALWAYS_INLINE(StructReader getStructElement(uint index) const);
// Get the struct element at the given index.
CAPNPROTO_ALWAYS_INLINE(ListReader getListElement(unsigned int index, uint32_t size) const);
CAPNPROTO_ALWAYS_INLINE(ListReader getListElement(uint index, uint32_t size) const);
// Get the list element at the given index.
private:
......@@ -267,11 +296,12 @@ private:
SegmentReader* segment; // Memory segment in which the list resides.
const void* ptr;
// Pointer to the data. If NULL, use defaultReferences. (Never NULL for data lists.)
// Pointer to the data. If null, use defaultReferences. (Never null for data lists.)
// Must be aligned appropriately for the elements.
uint32_t elementCount; // Number of elements in the list.
unsigned int stepBits;
BitCount stepBits;
// The distance between elements, in bits. This is usually the element size, but can be larger
// if the sender upgraded a data list to a struct list. It will always be aligned properly for
// the type. Unsigned so that division by a constant power of 2 is efficient.
......@@ -284,13 +314,13 @@ private:
// Once this reaches zero, further pointers will be pruned.
inline ListReader(const ListDescriptor* descriptor, SegmentReader* segment,
const void* ptr, uint32_t size, int stepBits, int structFieldCount,
const void* ptr, uint32_t size, BitCount stepBits, int structFieldCount,
int recursionLimit)
: descriptor(descriptor), segment(segment), ptr(ptr), elementCount(size), stepBits(stepBits),
structFieldCount(structFieldCount), recursionLimit(recursionLimit) {}
StructReader getStructElementInternal(unsigned int index) const;
ListReader getListElementInternal(unsigned int index, uint32_t size) const;
StructReader getStructElementInternal(uint index) const;
ListReader getListElementInternal(uint index, uint32_t size) const;
// The public methods are inlined and simply wrap these "Internal" methods after doing debug
// asserts. This way, debugging is enabled by the caller's compiler flags rather than
// libcapnproto's debug flags.
......@@ -304,31 +334,31 @@ private:
// Internal implementation details...
template <typename T>
inline T StructBuilder::getDataField(unsigned int offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::dataFieldInRange(descriptor, offset, sizeof(T)),
inline T StructBuilder::getDataField(uint offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::dataFieldInRange(descriptor, offset, sizeof(T) * BYTES),
"StructBuilder::getDataField() type mismatch.");
return reinterpret_cast<WireValue<T>*>(ptr)[-offset].get();
}
template <>
inline bool StructBuilder::getDataField<bool>(unsigned int offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::bitFieldInRange(descriptor, offset),
inline bool StructBuilder::getDataField<bool>(uint offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::bitFieldInRange(descriptor, offset * BITS),
"StructBuilder::getDataField<bool>() type mismatch.");
uint8_t byte = *(reinterpret_cast<uint8_t*>(ptr) - (offset / 8) - 1);
return (byte & (1 << (offset % 8))) != 0;
}
template <typename T>
inline void StructBuilder::setDataField(unsigned int offset, T value) const {
CAPNPROTO_DEBUG_ASSERT(debug::dataFieldInRange(descriptor, offset, sizeof(T)),
inline void StructBuilder::setDataField(uint offset, typename NoInfer<T>::Type value) const {
CAPNPROTO_DEBUG_ASSERT(debug::dataFieldInRange(descriptor, offset, sizeof(T) * BYTES),
"StructBuilder::setDataField() type mismatch.");
reinterpret_cast<WireValue<T>*>(ptr)[-offset].set(value);
}
template <>
inline void StructBuilder::setDataField<bool>(unsigned int offset, bool value) const {
CAPNPROTO_DEBUG_ASSERT(debug::bitFieldInRange(descriptor, offset),
inline void StructBuilder::setDataField<bool>(uint offset, bool value) const {
CAPNPROTO_DEBUG_ASSERT(debug::bitFieldInRange(descriptor, offset * BITS),
"StructBuilder::setDataField<bool>() type mismatch.");
uint8_t* byte = reinterpret_cast<uint8_t*>(ptr) - (offset / 8) - 1;
*byte = (*byte & ~(1 << (offset % 8)))
......@@ -356,33 +386,34 @@ inline ListBuilder StructBuilder::getListField(int refIndex) const {
// -------------------------------------------------------------------
template <typename T>
T StructReader::getDataField(int fieldNumber, unsigned int offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::fieldIsData(descriptor, fieldNumber, offset, sizeof(T) * 8),
T StructReader::getDataField(int fieldNumber, uint offset) const {
CAPNPROTO_DEBUG_ASSERT(
debug::fieldIsData(descriptor, fieldNumber, offset, sizeof(T) * BYTES * BITS_PER_BYTE),
"StructReader::getDataField() type mismatch.");
const void* dataPtr = ptr[fieldNumber >= fieldCount];
return reinterpret_cast<WireValue<T>*>(dataPtr)[-offset].get();
}
template <>
inline bool StructReader::getDataField<bool>(int fieldNumber, unsigned int offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::fieldIsData(descriptor, fieldNumber, offset, 1),
inline bool StructReader::getDataField<bool>(int fieldNumber, uint offset) const {
CAPNPROTO_DEBUG_ASSERT(debug::fieldIsData(descriptor, fieldNumber, offset, 1 * BITS),
"StructReader::getDataField<bool>() type mismatch.");
// This branch should always be optimized away when inlining.
if (offset == 0) offset = bit0Offset;
if (offset == 0) offset = bit0Offset / BITS;
const void* dataPtr = ptr[fieldNumber >= fieldCount];
uint8_t byte = *(reinterpret_cast<const uint8_t*>(dataPtr) - (offset / 8) - 1);
return (byte & (1 << (offset % 8))) != 0;
}
inline StructReader StructReader::getStructField(int fieldNumber, unsigned int refIndex) const {
inline StructReader StructReader::getStructField(int fieldNumber, uint refIndex) const {
CAPNPROTO_DEBUG_ASSERT(debug::fieldIsStruct(descriptor, fieldNumber, refIndex),
"StructReader::getStructField() type mismatch.");
return getStructFieldInternal(fieldNumber, refIndex);
}
inline ListReader StructReader::getListField(int fieldNumber, unsigned int refIndex) const {
inline ListReader StructReader::getListField(int fieldNumber, uint refIndex) const {
CAPNPROTO_DEBUG_ASSERT(debug::fieldIsList(descriptor, fieldNumber, refIndex),
"StructReader::getListField() type mismatch.");
return getListFieldInternal(fieldNumber, refIndex);
......@@ -393,30 +424,30 @@ inline ListReader StructReader::getListField(int fieldNumber, unsigned int refIn
inline uint32_t ListBuilder::size() { return elementCount; }
template <typename T>
inline T ListBuilder::getDataElement(unsigned int index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * 8),
inline T ListBuilder::getDataElement(uint index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * BYTES * BITS_PER_BYTE),
"ListBuilder::getDataElement() type mismatch.");
return reinterpret_cast<WireValue<T>*>(ptr)[index].get();
}
template <>
inline bool ListBuilder::getDataElement<bool>(unsigned int index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1),
inline bool ListBuilder::getDataElement<bool>(uint index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1 * BITS),
"ListBuilder::getDataElement<bool>() type mismatch.");
uint8_t byte = *(reinterpret_cast<uint8_t*>(ptr) + (index / 8));
return (byte & (1 << (index % 8))) != 0;
}
template <typename T>
inline void ListBuilder::setDataElement(unsigned int index, T value) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * 8),
inline void ListBuilder::setDataElement(uint index, typename NoInfer<T>::Type value) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * BYTES * BITS_PER_BYTE),
"ListBuilder::setDataElement() type mismatch.");
reinterpret_cast<WireValue<T>*>(ptr)[index].set(value);
}
template <>
inline void ListBuilder::setDataElement<bool>(unsigned int index, bool value) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1),
inline void ListBuilder::setDataElement<bool>(uint index, bool value) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1 * BITS),
"ListBuilder::setDataElement<bool>() type mismatch.");
uint8_t* byte = reinterpret_cast<uint8_t*>(ptr) + (index / 8);
*byte = (*byte & ~(1 << (index % 8)))
......@@ -424,21 +455,21 @@ inline void ListBuilder::setDataElement<bool>(unsigned int index, bool value) co
}
inline StructBuilder ListBuilder::getStructElement(
unsigned int index, uint32_t elementWordSize) const {
uint index, WordCount elementSize) const {
CAPNPROTO_DEBUG_ASSERT(index < elementCount, "List index out of range.");
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreStructs(descriptor, elementWordSize),
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreStructs(descriptor, elementSize),
"ListBuilder::getStructElement() type mismatch.");
return getStructElementInternal(index, elementWordSize);
return getStructElementInternal(index, elementSize);
}
inline ListBuilder ListBuilder::initListElement(unsigned int index, uint32_t size) const {
inline ListBuilder ListBuilder::initListElement(uint index, uint32_t size) const {
CAPNPROTO_DEBUG_ASSERT(index < elementCount, "List index out of range.");
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreLists(descriptor),
"ListBuilder::initListElement() type mismatch.");
return initListElementInternal(index, size);
}
inline ListBuilder ListBuilder::getListElement(unsigned int index) const {
inline ListBuilder ListBuilder::getListElement(uint index) const {
CAPNPROTO_DEBUG_ASSERT(index < elementCount, "List index out of range.");
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreLists(descriptor),
"ListBuilder::getListElement() type mismatch.");
......@@ -450,30 +481,30 @@ inline ListBuilder ListBuilder::getListElement(unsigned int index) const {
inline uint32_t ListReader::size() { return elementCount; }
template <typename T>
inline T ListReader::getDataElement(unsigned int index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * 8),
inline T ListReader::getDataElement(uint index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, sizeof(T) * BYTES * BITS_PER_BYTE),
"ListReader::getDataElement() type mismatch.");
return *reinterpret_cast<const T*>(
reinterpret_cast<const uint8_t*>(ptr) + index * (stepBits / 8));
reinterpret_cast<const byte*>(ptr) + index * (stepBits / BITS_PER_BYTE));
}
template <>
inline bool ListReader::getDataElement<bool>(unsigned int index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1),
inline bool ListReader::getDataElement<bool>(uint index) const {
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreData(descriptor, 1 * BITS),
"ListReader::getDataElement<bool>() type mismatch.");
unsigned int bitIndex = index * stepBits;
uint bitIndex = index * stepBits / BITS;
uint8_t byte = *(reinterpret_cast<const uint8_t*>(ptr) + (bitIndex / 8));
return (byte & (1 << (bitIndex % 8))) != 0;
}
inline StructReader ListReader::getStructElement(unsigned int index) const {
inline StructReader ListReader::getStructElement(uint index) const {
CAPNPROTO_DEBUG_ASSERT(index < elementCount, "List index out of range.");
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreStructs(descriptor),
"ListReader::getStructElement() type mismatch.");
return getStructElementInternal(index);
}
inline ListReader ListReader::getListElement(unsigned int index, uint32_t size) const {
inline ListReader ListReader::getListElement(uint index, uint32_t size) const {
CAPNPROTO_DEBUG_ASSERT(index < elementCount, "List index out of range.");
CAPNPROTO_DEBUG_ASSERT(debug::elementsAreLists(descriptor),
"ListReader::getListElement() type mismatch.");
......
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