// 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. // This file is NOT intended for use by clients, except in generated code. // // This file defines low-level, non-type-safe classes for traversing the Cap'n Proto memory layout // (which is also its wire format). Code generated by the Cap'n Proto compiler uses these classes, // as does other parts of the Cap'n proto library which provide a higher-level interface for // dynamic introspection. #ifndef CAPNP_LAYOUT_H_ #define CAPNP_LAYOUT_H_ #include <kj/common.h> #include "common.h" #include "blob.h" #include "endian.h" namespace capnp { namespace _ { // private class StructBuilder; class StructReader; class ListBuilder; class ListReader; class OrphanBuilder; struct ObjectBuilder; struct ObjectReader; struct WirePointer; struct WireHelpers; class SegmentReader; class SegmentBuilder; class BuilderArena; // ============================================================================= enum class FieldSize: uint8_t { // TODO(cleanup): Rename to FieldLayout or maybe ValueLayout. // Notice that each member of this enum, when representing a list element size, represents a // size that is greater than or equal to the previous members, since INLINE_COMPOSITE is used // only for multi-word structs. This is important because it allows us to compare FieldSize // values for the purpose of deciding when we need to upgrade a list. VOID = 0, BIT = 1, BYTE = 2, TWO_BYTES = 3, FOUR_BYTES = 4, EIGHT_BYTES = 5, POINTER = 6, // Indicates that the field lives in the pointer section, not the data section. INLINE_COMPOSITE = 7 // A composite type of fixed width. This serves two purposes: // 1) For lists of composite types where all the elements would have the exact same width, // allocating a list of pointers which in turn point at the elements would waste space. We // can avoid a layer of indirection by placing all the elements in a flat sequence, and only // indicating the element properties (e.g. field count for structs) once. // // Specifically, a list pointer indicating INLINE_COMPOSITE element size actually points to // a "tag" describing one element. This tag is formatted like a wire pointer, but the // "offset" instead stores the element count of the list. The flat list of elements appears // immediately after the tag. In the list pointer itself, the element count is replaced with // a word count for the whole list (excluding tag). This allows the tag and elements to be // precached in a single step rather than two sequential steps. // // It is NOT intended to be possible to substitute an INLINE_COMPOSITE list for a POINTER // list or vice-versa without breaking recipients. Recipients expect one or the other // depending on the message definition. // // However, it IS allowed to substitute an INLINE_COMPOSITE list -- specifically, of structs -- // when a list was expected, or vice versa, with the assumption that the first field of the // struct (field number zero) correspond to the element type. This allows a list of // primitives to be upgraded to a list of structs, avoiding the need to use parallel arrays // when you realize that you need to attach some extra information to each element of some // primitive list. // // 2) At one point there was a notion of "inline" struct fields, but it was deemed too much of // an implementation burden for too little gain, and so was deleted. }; typedef decltype(BITS / ELEMENTS) BitsPerElement; typedef decltype(POINTERS / ELEMENTS) PointersPerElement; static constexpr BitsPerElement BITS_PER_ELEMENT_TABLE[8] = { 0 * BITS / ELEMENTS, 1 * BITS / ELEMENTS, 8 * BITS / ELEMENTS, 16 * BITS / ELEMENTS, 32 * BITS / ELEMENTS, 64 * BITS / ELEMENTS, 0 * BITS / ELEMENTS, 0 * BITS / ELEMENTS }; inline constexpr BitsPerElement dataBitsPerElement(FieldSize size) { return _::BITS_PER_ELEMENT_TABLE[static_cast<int>(size)]; } inline constexpr PointersPerElement pointersPerElement(FieldSize size) { return size == FieldSize::POINTER ? 1 * POINTERS / ELEMENTS : 0 * POINTERS / ELEMENTS; } template <size_t size> struct ElementSizeForByteSize; template <> struct ElementSizeForByteSize<1> { static constexpr FieldSize value = FieldSize::BYTE; }; template <> struct ElementSizeForByteSize<2> { static constexpr FieldSize value = FieldSize::TWO_BYTES; }; template <> struct ElementSizeForByteSize<4> { static constexpr FieldSize value = FieldSize::FOUR_BYTES; }; template <> struct ElementSizeForByteSize<8> { static constexpr FieldSize value = FieldSize::EIGHT_BYTES; }; template <typename T> struct ElementSizeForType { static constexpr FieldSize value = // Primitive types that aren't special-cased below can be determined from sizeof(). kind<T>() == Kind::PRIMITIVE ? ElementSizeForByteSize<sizeof(T)>::value : kind<T>() == Kind::ENUM ? FieldSize::TWO_BYTES : kind<T>() == Kind::STRUCT ? FieldSize::INLINE_COMPOSITE : // Everything else is a pointer. FieldSize::POINTER; }; // Void and bool are special. template <> struct ElementSizeForType<Void> { static constexpr FieldSize value = FieldSize::VOID; }; template <> struct ElementSizeForType<bool> { static constexpr FieldSize value = FieldSize::BIT; }; // Lists and blobs are pointers, not structs. template <typename T, bool b> struct ElementSizeForType<List<T, b>> { static constexpr FieldSize value = FieldSize::POINTER; }; template <> struct ElementSizeForType<Text> { static constexpr FieldSize value = FieldSize::POINTER; }; template <> struct ElementSizeForType<Data> { static constexpr FieldSize value = FieldSize::POINTER; }; } // namespace _ (private) // ============================================================================= namespace _ { // private 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 StructSize { WordCount16 data; WirePointerCount16 pointers; FieldSize preferredListEncoding; // Preferred size to use when encoding a list of this struct. This is INLINE_COMPOSITE if and // only if the struct is larger than one word; otherwise the struct list can be encoded more // efficiently by encoding it as if it were some primitive type. inline constexpr WordCount total() const { return data + pointers * WORDS_PER_POINTER; } StructSize() = default; inline constexpr StructSize(WordCount data, WirePointerCount pointers, FieldSize preferredListEncoding) : data(data), pointers(pointers), preferredListEncoding(preferredListEncoding) {} }; template <typename T> struct StructSize_; // Specialized for every struct type with member: static constexpr StructSize value" template <typename T> inline constexpr StructSize structSize() { return StructSize_<T>::value; } // ------------------------------------------------------------------- // Masking of default values template <typename T, Kind kind = kind<T>()> struct Mask_; template <typename T> struct Mask_<T, Kind::PRIMITIVE> { typedef T Type; }; template <typename T> struct Mask_<T, Kind::ENUM> { typedef uint16_t Type; }; template <> struct Mask_<float, Kind::PRIMITIVE> { typedef uint32_t Type; }; template <> struct Mask_<double, Kind::PRIMITIVE> { typedef uint64_t Type; }; template <typename T> struct Mask_<T, Kind::UNKNOWN> { // Union discriminants end up here. static_assert(sizeof(T) == 2, "Don't know how to mask this type."); typedef uint16_t Type; }; template <typename T> using Mask = typename Mask_<T>::Type; template <typename T> KJ_ALWAYS_INLINE(Mask<T> mask(T value, Mask<T> mask)); template <typename T> KJ_ALWAYS_INLINE(T unmask(Mask<T> value, Mask<T> mask)); template <typename T> inline Mask<T> mask(T value, Mask<T> mask) { return static_cast<Mask<T> >(value) ^ mask; } template <> inline uint32_t mask<float>(float value, uint32_t mask) { uint32_t i; static_assert(sizeof(i) == sizeof(value), "float is not 32 bits?"); memcpy(&i, &value, sizeof(value)); return i ^ mask; } template <> inline uint64_t mask<double>(double value, uint64_t mask) { uint64_t i; static_assert(sizeof(i) == sizeof(value), "double is not 64 bits?"); memcpy(&i, &value, sizeof(value)); return i ^ mask; } template <typename T> inline T unmask(Mask<T> value, Mask<T> mask) { return static_cast<T>(value ^ mask); } template <> inline float unmask<float>(uint32_t value, uint32_t mask) { value ^= mask; float result; static_assert(sizeof(result) == sizeof(value), "float is not 32 bits?"); memcpy(&result, &value, sizeof(value)); return result; } template <> inline double unmask<double>(uint64_t value, uint64_t mask) { value ^= mask; double result; static_assert(sizeof(result) == sizeof(value), "double is not 64 bits?"); memcpy(&result, &value, sizeof(value)); return result; } // ------------------------------------------------------------------- // ------------------------------------------------------------------- class StructBuilder: public kj::DisallowConstCopy { public: inline StructBuilder(): segment(nullptr), data(nullptr), pointers(nullptr), bit0Offset(0) {} static StructBuilder initRoot(SegmentBuilder* segment, word* location, StructSize size); static void setRoot(SegmentBuilder* segment, word* location, StructReader value); static StructBuilder getRoot(SegmentBuilder* segment, word* location, StructSize size); inline BitCount getDataSectionSize() const { return dataSize; } inline WirePointerCount getPointerSectionSize() const { return pointerCount; } inline Data::Builder getDataSectionAsBlob(); template <typename T> KJ_ALWAYS_INLINE(T getDataField(ElementCount offset)); // Gets the data field value of the given type at the given offset. The offset is measured in // multiples of the field size, determined by the type. template <typename T> KJ_ALWAYS_INLINE(T getDataField(ElementCount offset, Mask<T> mask)); // Like getDataField() but applies the given XOR mask to the data on load. Used for reading // fields with non-zero default values. template <typename T> KJ_ALWAYS_INLINE(void setDataField( ElementCount offset, kj::NoInfer<T> value)); // Sets the data field value at the given offset. template <typename T> KJ_ALWAYS_INLINE(void setDataField( ElementCount offset, kj::NoInfer<T> value, Mask<T> mask)); // Like setDataField() but applies the given XOR mask before storing. Used for writing fields // with non-zero default values. StructBuilder initStructField(WirePointerCount ptrIndex, StructSize size); // Initializes the struct field at the given index in the pointer section. If it is already // initialized, the previous value is discarded or overwritten. The struct is initialized to // the type's default state (all-zero). Use getStructField() if you want the struct to be // initialized as a copy of the field's default value (which may have non-null pointers). StructBuilder getStructField(WirePointerCount ptrIndex, StructSize size, const word* defaultValue); // Gets the struct field at the given index in the pointer section. If the field is not already // initialized, it is initialized as a deep copy of the given default value (a flat message), // or to the empty state if defaultValue is nullptr. ListBuilder initListField(WirePointerCount ptrIndex, FieldSize elementSize, ElementCount elementCount); // Allocates a new list of the given size for the field at the given index in the pointer // segment, and return a pointer to it. All elements are initialized to zero. ListBuilder initStructListField(WirePointerCount ptrIndex, ElementCount elementCount, StructSize size); // Allocates a new list of the given size for the field at the given index in the pointer // segment, and return a pointer to it. Each element is initialized to its empty state. ListBuilder getListField(WirePointerCount ptrIndex, FieldSize elementSize, const word* defaultValue); // Gets the already-allocated list field for the given pointer index, ensuring that the list is // suitable for storing non-struct elements of the given size. If the list is not already // allocated, it is allocated as a deep copy of the given default value (a flat message). If // the default value is null, an empty list is used. ListBuilder getStructListField(WirePointerCount ptrIndex, StructSize elementSize, const word* defaultValue); // Gets the already-allocated list field for the given pointer index, ensuring that the list // is suitable for storing struct elements of the given size. If the list is not // already allocated, it is allocated as a deep copy of the given default value (a flat // message). If the default value is null, an empty list is used. template <typename T> typename T::Builder initBlobField(WirePointerCount ptrIndex, ByteCount size); // Initialize a Text or Data field to the given size in bytes (not including NUL terminator for // Text) and return a Text::Builder which can be used to fill in the content. template <typename T> void setBlobField(WirePointerCount ptrIndex, typename T::Reader value); // Set the blob field to a copy of the given blob. template <typename T> typename T::Builder getBlobField(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize); // Get the blob field. If it is not initialized, initialize it to a copy of the given default. ObjectBuilder getObjectField(WirePointerCount ptrIndex, const word* defaultValue); // Read a pointer of arbitrary type. void setStructField(WirePointerCount ptrIndex, StructReader value); void setListField(WirePointerCount ptrIndex, ListReader value); void setObjectField(WirePointerCount ptrIndex, ObjectReader value); // Sets a pointer field to a deep copy of the given value. void adopt(WirePointerCount ptrIndex, OrphanBuilder&& orphan); // Adopt the orphaned object as the value of the given pointer field. The orphan must reside in // the same message as the new parent. No copying occurs. OrphanBuilder disown(WirePointerCount ptrIndex); // Detach the given pointer field from this object. The pointer becomes null, and the child // object is returned as an orphan. void transferContentFrom(StructBuilder other); // Adopt all pointers from `other`, and also copy all data. If `other`'s sections are larger // than this, the extra data is not transferred, meaning there is a risk of data loss when // transferring from messages built with future versions of the protocol. void copyContentFrom(StructReader other); // Copy content from `other`. If `other`'s sections are larger than this, the extra data is not // copied, meaning there is a risk of data loss when copying from messages built with future // versions of the protocol. bool isPointerFieldNull(WirePointerCount ptrIndex); StructReader asReader() const; // Gets a StructReader pointing at the same memory. BuilderArena* getArena(); // Gets the arena in which this object is allocated. private: SegmentBuilder* segment; // Memory segment in which the struct resides. void* data; // Pointer to the encoded data. WirePointer* pointers; // Pointer to the encoded pointers. BitCount32 dataSize; // Size of data section. We use a bit count rather than a word count to more easily handle the // case of struct lists encoded with less than a word per element. WirePointerCount16 pointerCount; // Size of the pointer section. BitCount8 bit0Offset; // A special hack: If dataSize == 1 bit, then bit0Offset is the offset of that bit within the // byte pointed to by `data`. In all other cases, this is zero. This is needed to implement // struct lists where each struct is one bit. inline StructBuilder(SegmentBuilder* segment, void* data, WirePointer* pointers, BitCount dataSize, WirePointerCount pointerCount, BitCount8 bit0Offset) : segment(segment), data(data), pointers(pointers), dataSize(dataSize), pointerCount(pointerCount), bit0Offset(bit0Offset) {} friend class ListBuilder; friend struct WireHelpers; friend class OrphanBuilder; }; class StructReader { public: inline StructReader() : segment(nullptr), data(nullptr), pointers(nullptr), dataSize(0), pointerCount(0), bit0Offset(0), nestingLimit(0x7fffffff) {} static StructReader readRootUnchecked(const word* location); static StructReader readRoot(const word* location, SegmentReader* segment, int nestingLimit); inline BitCount getDataSectionSize() const { return dataSize; } inline WirePointerCount getPointerSectionSize() const { return pointerCount; } inline Data::Reader getDataSectionAsBlob(); template <typename T> KJ_ALWAYS_INLINE(T getDataField(ElementCount offset) const); // Get the data field value of the given type at the given offset. The offset is measured in // multiples of the field size, determined by the type. Returns zero if the offset is past the // end of the struct's data section. template <typename T> KJ_ALWAYS_INLINE( T getDataField(ElementCount offset, Mask<T> mask) const); // Like getDataField(offset), but applies the given XOR mask to the result. Used for reading // fields with non-zero default values. StructReader getStructField(WirePointerCount ptrIndex, const word* defaultValue) const; // Get the struct field at the given index in the pointer section, or the default value if not // initialized. defaultValue will be interpreted as a flat message -- it must point at a // struct pointer, which in turn points at the struct value. The default value is allowed to // be null, in which case an empty struct is used. ListReader getListField(WirePointerCount ptrIndex, FieldSize expectedElementSize, const word* defaultValue) const; // Get the list field at the given index in the pointer section, or the default value if not // initialized. The default value is allowed to be null, in which case an empty list is used. template <typename T> typename T::Reader getBlobField(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize) const; // Gets the text or data field, or the given default value if not initialized. ObjectReader getObjectField(WirePointerCount ptrIndex, const word* defaultValue) const; // Read a pointer of arbitrary type. const word* getUncheckedPointer(WirePointerCount ptrIndex) const; // If this is an unchecked message, get a word* pointing at the location of the pointer. This // word* can actually be passed to readUnchecked() to read the designated sub-object later. If // this isn't an unchecked message, throws an exception. bool isPointerFieldNull(WirePointerCount ptrIndex) const; WordCount64 totalSize() const; // Return the total size of the struct and everything to which it points. Does not count far // pointer overhead. This is useful for deciding how much space is needed to copy the struct // into a flat array. However, the caller is advised NOT to treat this value as secure. Instead, // use the result as a hint for allocating the first segment, do the copy, and then throw an // exception if it overruns. private: SegmentReader* segment; // Memory segment in which the struct resides. const void* data; const WirePointer* pointers; BitCount32 dataSize; // Size of data section. We use a bit count rather than a word count to more easily handle the // case of struct lists encoded with less than a word per element. WirePointerCount16 pointerCount; // Size of the pointer section. BitCount8 bit0Offset; // A special hack: If dataSize == 1 bit, then bit0Offset is the offset of that bit within the // byte pointed to by `data`. In all other cases, this is zero. This is needed to implement // struct lists where each struct is one bit. // // TODO(someday): Consider packing this together with dataSize, since we have 10 extra bits // there doing nothing -- or arguably 12 bits, if you consider that 2-bit and 4-bit sizes // aren't allowed. Consider that we could have a method like getDataSizeIn<T>() which is // specialized to perform the correct shifts for each size. int nestingLimit; // Limits the depth of message structures to guard against stack-overflow-based DoS attacks. // Once this reaches zero, further pointers will be pruned. // TODO(perf): Limit to 8 bits for better alignment? inline StructReader(SegmentReader* segment, const void* data, const WirePointer* pointers, BitCount dataSize, WirePointerCount pointerCount, BitCount8 bit0Offset, int nestingLimit) : segment(segment), data(data), pointers(pointers), dataSize(dataSize), pointerCount(pointerCount), bit0Offset(bit0Offset), nestingLimit(nestingLimit) {} friend class ListReader; friend class StructBuilder; friend struct WireHelpers; }; // ------------------------------------------------------------------- class ListBuilder: public kj::DisallowConstCopy { public: inline ListBuilder() : segment(nullptr), ptr(nullptr), elementCount(0 * ELEMENTS), step(0 * BITS / ELEMENTS) {} inline ElementCount size() const; // The number of elements in the list. Text::Builder asText(); Data::Builder asData(); // Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized. template <typename T> KJ_ALWAYS_INLINE(T getDataElement(ElementCount index)); // Get the element of the given type at the given index. template <typename T> KJ_ALWAYS_INLINE(void setDataElement( ElementCount index, kj::NoInfer<T> value)); // Set the element at the given index. StructBuilder getStructElement(ElementCount index); // Get the struct element at the given index. ListBuilder initListElement( ElementCount index, FieldSize elementSize, ElementCount elementCount); // Create a new list element of the given size at the given index. All elements are initialized // to zero. ListBuilder initStructListElement(ElementCount index, ElementCount elementCount, StructSize size); // Allocates a new list of the given size for the field at the given index in the pointer // segment, and return a pointer to it. Each element is initialized to its empty state. ListBuilder getListElement(ElementCount index, FieldSize elementSize); // Get the existing list element at the given index, making sure it is suitable for storing // non-struct elements of the given size. Returns an empty list if the element is not // initialized. ListBuilder getStructListElement(ElementCount index, StructSize elementSize); // Get the existing list element at the given index, making sure it is suitable for storing // struct elements of the given size. Returns an empty list if the element is not // initialized. template <typename T> typename T::Builder initBlobElement(ElementCount index, ByteCount size); // Initialize a Text or Data element to the given size in bytes (not including NUL terminator for // Text) and return a Text::Builder which can be used to fill in the content. template <typename T> void setBlobElement(ElementCount index, typename T::Reader value); // Set the blob element to a copy of the given blob. template <typename T> typename T::Builder getBlobElement(ElementCount index); // Get the blob element. If it is not initialized, return an empty blob builder. ObjectBuilder getObjectElement(ElementCount index); // Gets a pointer element of arbitrary type. void setListElement(ElementCount index, ListReader value); void setObjectElement(ElementCount index, ObjectReader value); // Sets a pointer element to a deep copy of the given value. void adopt(ElementCount index, OrphanBuilder&& orphan); // Adopt the orphaned object as the value of the given pointer field. The orphan must reside in // the same message as the new parent. No copying occurs. OrphanBuilder disown(ElementCount index); // Detach the given pointer field from this object. The pointer becomes null, and the child // object is returned as an orphan. ListReader asReader() const; // Get a ListReader pointing at the same memory. BuilderArena* getArena(); // Gets the arena in which this object is allocated. private: SegmentBuilder* segment; // Memory segment in which the list resides. byte* ptr; // Pointer to list content. ElementCount elementCount; // Number of elements in the list. decltype(BITS / ELEMENTS) step; // The distance between elements. BitCount32 structDataSize; WirePointerCount16 structPointerCount; // The struct properties to use when interpreting the elements as structs. All lists can be // interpreted as struct lists, so these are always filled in. inline ListBuilder(SegmentBuilder* segment, void* ptr, decltype(BITS / ELEMENTS) step, ElementCount size, BitCount structDataSize, WirePointerCount structPointerCount) : segment(segment), ptr(reinterpret_cast<byte*>(ptr)), elementCount(size), step(step), structDataSize(structDataSize), structPointerCount(structPointerCount) {} friend class StructBuilder; friend struct WireHelpers; friend class OrphanBuilder; }; class ListReader { public: inline ListReader() : segment(nullptr), ptr(nullptr), elementCount(0), step(0 * BITS / ELEMENTS), structDataSize(0), structPointerCount(0), nestingLimit(0x7fffffff) {} inline ElementCount size() const; // The number of elements in the list. Text::Reader asText(); Data::Reader asData(); // Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized. template <typename T> KJ_ALWAYS_INLINE(T getDataElement(ElementCount index) const); // Get the element of the given type at the given index. StructReader getStructElement(ElementCount index) const; // Get the struct element at the given index. ListReader getListElement(ElementCount index, FieldSize expectedElementSize) const; // Get the list element at the given index. template <typename T> typename T::Reader getBlobElement(ElementCount index) const; // Gets the text or data field. If it is not initialized, returns an empty blob reader. ObjectReader getObjectElement(ElementCount index) const; // Gets a pointer element of arbitrary type. private: SegmentReader* segment; // Memory segment in which the list resides. const byte* ptr; // Pointer to list content. ElementCount elementCount; // Number of elements in the list. decltype(BITS / ELEMENTS) step; // The distance between elements. BitCount32 structDataSize; WirePointerCount16 structPointerCount; // The struct properties to use when interpreting the elements as structs. All lists can be // interpreted as struct lists, so these are always filled in. int nestingLimit; // Limits the depth of message structures to guard against stack-overflow-based DoS attacks. // Once this reaches zero, further pointers will be pruned. inline ListReader(SegmentReader* segment, const void* ptr, ElementCount elementCount, decltype(BITS / ELEMENTS) step, BitCount structDataSize, WirePointerCount structPointerCount, int nestingLimit) : segment(segment), ptr(reinterpret_cast<const byte*>(ptr)), elementCount(elementCount), step(step), structDataSize(structDataSize), structPointerCount(structPointerCount), nestingLimit(nestingLimit) {} friend class StructReader; friend class ListBuilder; friend struct WireHelpers; friend class OrphanBuilder; }; // ------------------------------------------------------------------- enum class ObjectKind { NULL_POINTER, // Object was read from a null pointer. STRUCT, LIST }; struct ObjectBuilder { // A reader for any kind of object. ObjectKind kind; union { StructBuilder structBuilder; ListBuilder listBuilder; }; ObjectBuilder(): kind(ObjectKind::NULL_POINTER), structBuilder() {} ObjectBuilder(StructBuilder structBuilder) : kind(ObjectKind::STRUCT), structBuilder(structBuilder) {} ObjectBuilder(ListBuilder listBuilder) : kind(ObjectKind::LIST), listBuilder(listBuilder) {} }; struct ObjectReader { // A reader for any kind of object. ObjectKind kind; union { StructReader structReader; ListReader listReader; }; ObjectReader(): kind(ObjectKind::NULL_POINTER), structReader() {} ObjectReader(StructReader structReader) : kind(ObjectKind::STRUCT), structReader(structReader) {} ObjectReader(ListReader listReader) : kind(ObjectKind::LIST), listReader(listReader) {} }; // ------------------------------------------------------------------- class OrphanBuilder { public: inline OrphanBuilder(): segment(nullptr), location(nullptr) { memset(&tag, 0, sizeof(tag)); } OrphanBuilder(const OrphanBuilder& other) = delete; inline OrphanBuilder(OrphanBuilder&& other); inline ~OrphanBuilder(); static OrphanBuilder initStruct(BuilderArena* arena, StructSize size); static OrphanBuilder initList(BuilderArena* arena, ElementCount elementCount, FieldSize elementSize); static OrphanBuilder initStructList(BuilderArena* arena, ElementCount elementCount, StructSize elementSize); static OrphanBuilder initText(BuilderArena* arena, ByteCount size); static OrphanBuilder initData(BuilderArena* arena, ByteCount size); static OrphanBuilder copy(BuilderArena* arena, StructReader copyFrom); static OrphanBuilder copy(BuilderArena* arena, ListReader copyFrom); static OrphanBuilder copy(BuilderArena* arena, Text::Reader copyFrom); static OrphanBuilder copy(BuilderArena* arena, Data::Reader copyFrom); OrphanBuilder& operator=(const OrphanBuilder& other) = delete; inline OrphanBuilder& operator=(OrphanBuilder&& other); inline bool operator==(decltype(nullptr)) { return location == nullptr; } inline bool operator!=(decltype(nullptr)) { return location != nullptr; } StructBuilder asStruct(StructSize size); // Interpret as a struct, or throw an exception if not a struct. ListBuilder asList(FieldSize elementSize); // Interpret as a list, or throw an exception if not a list. elementSize cannot be // INLINE_COMPOSITE -- use asStructList() instead. ListBuilder asStructList(StructSize elementSize); // Interpret as a struct list, or throw an exception if not a list. Text::Builder asText(); Data::Builder asData(); // Interpret as a blob, or throw an exception if not a blob. ObjectBuilder asObject(); // Interpret as an arbitrary object. StructReader asStructReader(StructSize size) const; ListReader asListReader(FieldSize elementSize) const; Text::Reader asTextReader() const; Data::Reader asDataReader() const; ObjectReader asObjectReader() const; private: static_assert(1 * POINTERS * WORDS_PER_POINTER == 1 * WORDS, "This struct assumes a pointer is one word."); word tag; // Contains an encoded WirePointer representing this object. WirePointer is defined in // layout.c++, but fits in a word. // // If the pointer is a FAR pointer, then the tag is a complete pointer, `location` is null, and // `segment` is any arbitrary segment in the message. Otherwise, the tag's offset is garbage, // `location` points at the actual object, and `segment` points at the segment where `location` // resides. SegmentBuilder* segment; // Segment in which the object resides, or an arbitrary segment in the message if the tag is a // FAR pointer. word* location; // Pointer to the object, or null if the tag is a FAR pointer. inline OrphanBuilder(const void* tagPtr, SegmentBuilder* segment, word* location) : segment(segment), location(location) { memcpy(&tag, tagPtr, sizeof(tag)); } inline WirePointer* tagAsPtr() { return reinterpret_cast<WirePointer*>(&tag); } inline const WirePointer* tagAsPtr() const { return reinterpret_cast<const WirePointer*>(&tag); } void euthanize(); // Erase the target object, zeroing it out and possibly reclaiming the memory. Called when // the OrphanBuilder is being destroyed or overwritten and it is non-null. friend struct WireHelpers; }; // ======================================================================================= // Internal implementation details... inline Data::Builder StructBuilder::getDataSectionAsBlob() { return Data::Builder(reinterpret_cast<byte*>(data), dataSize / BITS_PER_BYTE / BYTES); } template <typename T> inline T StructBuilder::getDataField(ElementCount offset) { return reinterpret_cast<WireValue<T>*>(data)[offset / ELEMENTS].get(); } template <> inline bool StructBuilder::getDataField<bool>(ElementCount offset) { // This branch should be compiled out whenever this is inlined with a constant offset. BitCount boffset = (offset == 0 * ELEMENTS) ? BitCount(bit0Offset) : offset * (1 * BITS / ELEMENTS); byte* b = reinterpret_cast<byte*>(data) + boffset / BITS_PER_BYTE; return (*reinterpret_cast<uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; } template <> inline Void StructBuilder::getDataField<Void>(ElementCount offset) { return Void::VOID; } template <typename T> inline T StructBuilder::getDataField(ElementCount offset, Mask<T> mask) { return unmask<T>(getDataField<Mask<T> >(offset), mask); } template <typename T> inline void StructBuilder::setDataField(ElementCount offset, kj::NoInfer<T> value) { reinterpret_cast<WireValue<T>*>(data)[offset / ELEMENTS].set(value); } template <> inline void StructBuilder::setDataField<bool>(ElementCount offset, bool value) { // This branch should be compiled out whenever this is inlined with a constant offset. BitCount boffset = (offset == 0 * ELEMENTS) ? BitCount(bit0Offset) : offset * (1 * BITS / ELEMENTS); 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); } template <> inline void StructBuilder::setDataField<Void>(ElementCount offset, Void value) {} template <typename T> inline void StructBuilder::setDataField(ElementCount offset, kj::NoInfer<T> value, Mask<T> m) { setDataField<Mask<T> >(offset, mask<T>(value, m)); } // ------------------------------------------------------------------- inline Data::Reader StructReader::getDataSectionAsBlob() { return Data::Reader(reinterpret_cast<const byte*>(data), dataSize / BITS_PER_BYTE / BYTES); } template <typename T> T StructReader::getDataField(ElementCount offset) const { if ((offset + 1 * ELEMENTS) * capnp::bitsPerElement<T>() <= dataSize) { return reinterpret_cast<const WireValue<T>*>(data)[offset / ELEMENTS].get(); } else { return static_cast<T>(0); } } template <> inline bool StructReader::getDataField<bool>(ElementCount offset) const { BitCount boffset = offset * (1 * BITS / ELEMENTS); if (boffset < dataSize) { // This branch should be compiled out whenever this is inlined with a constant offset. if (offset == 0 * ELEMENTS) { boffset = bit0Offset; } const byte* b = reinterpret_cast<const byte*>(data) + boffset / BITS_PER_BYTE; return (*reinterpret_cast<const uint8_t*>(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; } else { return false; } } template <> inline Void StructReader::getDataField<Void>(ElementCount offset) const { return Void::VOID; } template <typename T> T StructReader::getDataField(ElementCount offset, Mask<T> mask) const { return unmask<T>(getDataField<Mask<T> >(offset), mask); } // ------------------------------------------------------------------- inline ElementCount ListBuilder::size() const { return elementCount; } template <typename T> inline T ListBuilder::getDataElement(ElementCount index) { return reinterpret_cast<WireValue<T>*>(ptr + index * step / BITS_PER_BYTE)->get(); // TODO(soon): Benchmark this alternate implementation, which I suspect may make better use of // the x86 SIB byte. Also use it for all the other getData/setData implementations below, and // the various non-inline methods that look up pointers. // Also if using this, consider changing ptr back to void* instead of byte*. // return reinterpret_cast<WireValue<T>*>(ptr)[ // index / ELEMENTS * (step / capnp::bitsPerElement<T>())].get(); } template <> inline bool ListBuilder::getDataElement<bool>(ElementCount index) { // Ignore stepBytes for bit lists because bit lists cannot be upgraded to struct lists. BitCount bindex = index * step; byte* b = ptr + bindex / BITS_PER_BYTE; return (*reinterpret_cast<uint8_t*>(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0; } template <> inline Void ListBuilder::getDataElement<Void>(ElementCount index) { return Void::VOID; } template <typename T> inline void ListBuilder::setDataElement(ElementCount index, kj::NoInfer<T> value) { reinterpret_cast<WireValue<T>*>(ptr + index * step / BITS_PER_BYTE)->set(value); } template <> inline void ListBuilder::setDataElement<bool>(ElementCount index, bool value) { // Ignore stepBytes for bit lists because bit lists cannot be upgraded to struct lists. BitCount bindex = index * (1 * BITS / ELEMENTS); byte* b = ptr + bindex / BITS_PER_BYTE; uint bitnum = bindex % BITS_PER_BYTE / BITS; *reinterpret_cast<uint8_t*>(b) = (*reinterpret_cast<uint8_t*>(b) & ~(1 << bitnum)) | (static_cast<uint8_t>(value) << bitnum); } template <> inline void ListBuilder::setDataElement<Void>(ElementCount index, Void value) {} // ------------------------------------------------------------------- inline ElementCount ListReader::size() const { return elementCount; } template <typename T> inline T ListReader::getDataElement(ElementCount index) const { return reinterpret_cast<const WireValue<T>*>(ptr + index * step / BITS_PER_BYTE)->get(); } template <> inline bool ListReader::getDataElement<bool>(ElementCount index) const { // Ignore stepBytes for bit lists because bit lists cannot be upgraded to struct lists. BitCount bindex = index * step; const byte* b = ptr + bindex / BITS_PER_BYTE; return (*reinterpret_cast<const uint8_t*>(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0; } template <> inline Void ListReader::getDataElement<Void>(ElementCount index) const { return Void::VOID; } // These are defined in the source file. template <> typename Text::Builder StructBuilder::initBlobField<Text>(WirePointerCount ptrIndex, ByteCount size); template <> void StructBuilder::setBlobField<Text>(WirePointerCount ptrIndex, typename Text::Reader value); template <> typename Text::Builder StructBuilder::getBlobField<Text>(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize); template <> typename Text::Reader StructReader::getBlobField<Text>(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize) const; template <> typename Text::Builder ListBuilder::initBlobElement<Text>(ElementCount index, ByteCount size); template <> void ListBuilder::setBlobElement<Text>(ElementCount index, typename Text::Reader value); template <> typename Text::Builder ListBuilder::getBlobElement<Text>(ElementCount index); template <> typename Text::Reader ListReader::getBlobElement<Text>(ElementCount index) const; template <> typename Data::Builder StructBuilder::initBlobField<Data>(WirePointerCount ptrIndex, ByteCount size); template <> void StructBuilder::setBlobField<Data>(WirePointerCount ptrIndex, typename Data::Reader value); template <> typename Data::Builder StructBuilder::getBlobField<Data>(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize); template <> typename Data::Reader StructReader::getBlobField<Data>(WirePointerCount ptrIndex, const void* defaultValue, ByteCount defaultSize) const; template <> typename Data::Builder ListBuilder::initBlobElement<Data>(ElementCount index, ByteCount size); template <> void ListBuilder::setBlobElement<Data>(ElementCount index, typename Data::Reader value); template <> typename Data::Builder ListBuilder::getBlobElement<Data>(ElementCount index); template <> typename Data::Reader ListReader::getBlobElement<Data>(ElementCount index) const; // ------------------------------------------------------------------- inline OrphanBuilder::OrphanBuilder(OrphanBuilder&& other) : segment(other.segment), location(other.location) { memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules. other.segment = nullptr; other.location = nullptr; } inline OrphanBuilder::~OrphanBuilder() { if (segment != nullptr) euthanize(); } inline OrphanBuilder& OrphanBuilder::operator=(OrphanBuilder&& other) { // With normal smart pointers, it's important to handle the case where the incoming pointer // is actually transitively owned by this one. In this case, euthanize() would destroy `other` // before we copied it. This isn't possible in the case of `OrphanBuilder` because it only // owns message objects, and `other` is not itself a message object, therefore cannot possibly // be transitively owned by `this`. if (segment != nullptr) euthanize(); segment = other.segment; location = other.location; memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules. other.segment = nullptr; other.location = nullptr; return *this; } } // namespace _ (private) } // namespace capnp #endif // CAPNP_LAYOUT_H_