cannam@134: // Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors cannam@134: // Licensed under the MIT License: cannam@134: // cannam@134: // Permission is hereby granted, free of charge, to any person obtaining a copy cannam@134: // of this software and associated documentation files (the "Software"), to deal cannam@134: // in the Software without restriction, including without limitation the rights cannam@134: // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell cannam@134: // copies of the Software, and to permit persons to whom the Software is cannam@134: // furnished to do so, subject to the following conditions: cannam@134: // cannam@134: // The above copyright notice and this permission notice shall be included in cannam@134: // all copies or substantial portions of the Software. cannam@134: // cannam@134: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR cannam@134: // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, cannam@134: // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE cannam@134: // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER cannam@134: // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, cannam@134: // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN cannam@134: // THE SOFTWARE. cannam@134: cannam@134: // This file is NOT intended for use by clients, except in generated code. cannam@134: // cannam@134: // This file defines low-level, non-type-safe classes for traversing the Cap'n Proto memory layout cannam@134: // (which is also its wire format). Code generated by the Cap'n Proto compiler uses these classes, cannam@134: // as does other parts of the Cap'n proto library which provide a higher-level interface for cannam@134: // dynamic introspection. cannam@134: cannam@134: #ifndef CAPNP_LAYOUT_H_ cannam@134: #define CAPNP_LAYOUT_H_ cannam@134: cannam@134: #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS) cannam@134: #pragma GCC system_header cannam@134: #endif cannam@134: cannam@134: #include cannam@134: #include cannam@134: #include "common.h" cannam@134: #include "blob.h" cannam@134: #include "endian.h" cannam@134: cannam@134: #if (defined(__mips__) || defined(__hppa__)) && !defined(CAPNP_CANONICALIZE_NAN) cannam@134: #define CAPNP_CANONICALIZE_NAN 1 cannam@134: // Explicitly detect NaNs and canonicalize them to the quiet NaN value as would be returned by cannam@134: // __builtin_nan("") on systems implementing the IEEE-754 recommended (but not required) NaN cannam@134: // signalling/quiet differentiation (such as x86). Unfortunately, some architectures -- in cannam@134: // particular, MIPS -- represent quiet vs. signalling nans differently than the rest of the world. cannam@134: // Canonicalizing them makes output consistent (which is important!), but hurts performance cannam@134: // slightly. cannam@134: // cannam@134: // Note that trying to convert MIPS NaNs to standard NaNs without losing data doesn't work. cannam@134: // Signaling vs. quiet is indicated by a bit, with the meaning being the opposite on MIPS vs. cannam@134: // everyone else. It would be great if we could just flip that bit, but we can't, because if the cannam@134: // significand is all-zero, then the value is infinity rather than NaN. This means that on most cannam@134: // machines, where the bit indicates quietness, there is one more quiet NaN value than signalling cannam@134: // NaN value, whereas on MIPS there is one more sNaN than qNaN, and thus there is no isomorphic cannam@134: // mapping that properly preserves quietness. Instead of doing something hacky, we just give up cannam@134: // and blow away NaN payloads, because no one uses them anyway. cannam@134: #endif cannam@134: cannam@134: namespace capnp { cannam@134: cannam@134: #if !CAPNP_LITE cannam@134: class ClientHook; cannam@134: #endif // !CAPNP_LITE cannam@134: cannam@134: namespace _ { // private cannam@134: cannam@134: class PointerBuilder; cannam@134: class PointerReader; cannam@134: class StructBuilder; cannam@134: class StructReader; cannam@134: class ListBuilder; cannam@134: class ListReader; cannam@134: class OrphanBuilder; cannam@134: struct WirePointer; cannam@134: struct WireHelpers; cannam@134: class SegmentReader; cannam@134: class SegmentBuilder; cannam@134: class Arena; cannam@134: class BuilderArena; cannam@134: cannam@134: // ============================================================================= cannam@134: cannam@134: typedef decltype(BITS / ELEMENTS) BitsPerElement; cannam@134: typedef decltype(POINTERS / ELEMENTS) PointersPerElement; cannam@134: cannam@134: static constexpr BitsPerElement BITS_PER_ELEMENT_TABLE[8] = { cannam@134: 0 * BITS / ELEMENTS, cannam@134: 1 * BITS / ELEMENTS, cannam@134: 8 * BITS / ELEMENTS, cannam@134: 16 * BITS / ELEMENTS, cannam@134: 32 * BITS / ELEMENTS, cannam@134: 64 * BITS / ELEMENTS, cannam@134: 0 * BITS / ELEMENTS, cannam@134: 0 * BITS / ELEMENTS cannam@134: }; cannam@134: cannam@134: inline KJ_CONSTEXPR() BitsPerElement dataBitsPerElement(ElementSize size) { cannam@134: return _::BITS_PER_ELEMENT_TABLE[static_cast(size)]; cannam@134: } cannam@134: cannam@134: inline constexpr PointersPerElement pointersPerElement(ElementSize size) { cannam@134: return size == ElementSize::POINTER ? 1 * POINTERS / ELEMENTS : 0 * POINTERS / ELEMENTS; cannam@134: } cannam@134: cannam@134: template struct ElementSizeForByteSize; cannam@134: template <> struct ElementSizeForByteSize<1> { static constexpr ElementSize value = ElementSize::BYTE; }; cannam@134: template <> struct ElementSizeForByteSize<2> { static constexpr ElementSize value = ElementSize::TWO_BYTES; }; cannam@134: template <> struct ElementSizeForByteSize<4> { static constexpr ElementSize value = ElementSize::FOUR_BYTES; }; cannam@134: template <> struct ElementSizeForByteSize<8> { static constexpr ElementSize value = ElementSize::EIGHT_BYTES; }; cannam@134: cannam@134: template struct ElementSizeForType { cannam@134: static constexpr ElementSize value = cannam@134: // Primitive types that aren't special-cased below can be determined from sizeof(). cannam@134: CAPNP_KIND(T) == Kind::PRIMITIVE ? ElementSizeForByteSize::value : cannam@134: CAPNP_KIND(T) == Kind::ENUM ? ElementSize::TWO_BYTES : cannam@134: CAPNP_KIND(T) == Kind::STRUCT ? ElementSize::INLINE_COMPOSITE : cannam@134: cannam@134: // Everything else is a pointer. cannam@134: ElementSize::POINTER; cannam@134: }; cannam@134: cannam@134: // Void and bool are special. cannam@134: template <> struct ElementSizeForType { static constexpr ElementSize value = ElementSize::VOID; }; cannam@134: template <> struct ElementSizeForType { static constexpr ElementSize value = ElementSize::BIT; }; cannam@134: cannam@134: // Lists and blobs are pointers, not structs. cannam@134: template struct ElementSizeForType> { cannam@134: static constexpr ElementSize value = ElementSize::POINTER; cannam@134: }; cannam@134: template <> struct ElementSizeForType { cannam@134: static constexpr ElementSize value = ElementSize::POINTER; cannam@134: }; cannam@134: template <> struct ElementSizeForType { cannam@134: static constexpr ElementSize value = ElementSize::POINTER; cannam@134: }; cannam@134: cannam@134: template cannam@134: inline constexpr ElementSize elementSizeForType() { cannam@134: return ElementSizeForType::value; cannam@134: } cannam@134: cannam@134: struct MessageSizeCounts { cannam@134: WordCount64 wordCount; cannam@134: uint capCount; cannam@134: cannam@134: MessageSizeCounts& operator+=(const MessageSizeCounts& other) { cannam@134: wordCount += other.wordCount; cannam@134: capCount += other.capCount; cannam@134: return *this; cannam@134: } cannam@134: cannam@134: MessageSize asPublic() { cannam@134: return MessageSize { wordCount / WORDS, capCount }; cannam@134: } cannam@134: }; cannam@134: cannam@134: // ============================================================================= cannam@134: cannam@134: template cannam@134: union AlignedData { cannam@134: // Useful for declaring static constant data blobs as an array of bytes, but forcing those cannam@134: // bytes to be word-aligned. cannam@134: cannam@134: uint8_t bytes[wordCount * sizeof(word)]; cannam@134: word words[wordCount]; cannam@134: }; cannam@134: cannam@134: struct StructSize { cannam@134: WordCount16 data; cannam@134: WirePointerCount16 pointers; cannam@134: cannam@134: inline constexpr WordCount total() const { return data + pointers * WORDS_PER_POINTER; } cannam@134: cannam@134: StructSize() = default; cannam@134: inline constexpr StructSize(WordCount data, WirePointerCount pointers) cannam@134: : data(data), pointers(pointers) {} cannam@134: }; cannam@134: cannam@134: template cannam@134: inline constexpr StructSize structSize() { cannam@134: return StructSize(CapnpPrivate::dataWordSize * WORDS, CapnpPrivate::pointerCount * POINTERS); cannam@134: } cannam@134: cannam@134: template > cannam@134: inline constexpr StructSize minStructSizeForElement() { cannam@134: // If T is a struct, return its struct size. Otherwise return the minimum struct size big enough cannam@134: // to hold a T. cannam@134: cannam@134: return StructSize(CapnpPrivate::dataWordSize * WORDS, CapnpPrivate::pointerCount * POINTERS); cannam@134: } cannam@134: cannam@134: template > cannam@134: inline constexpr StructSize minStructSizeForElement() { cannam@134: // If T is a struct, return its struct size. Otherwise return the minimum struct size big enough cannam@134: // to hold a T. cannam@134: cannam@134: return StructSize( cannam@134: dataBitsPerElement(elementSizeForType()) * ELEMENTS > 0 * BITS ? 1 * WORDS : 0 * WORDS, cannam@134: pointersPerElement(elementSizeForType()) * ELEMENTS); cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: // Masking of default values cannam@134: cannam@134: template struct Mask_; cannam@134: template struct Mask_ { typedef T Type; }; cannam@134: template struct Mask_ { typedef uint16_t Type; }; cannam@134: template <> struct Mask_ { typedef uint32_t Type; }; cannam@134: template <> struct Mask_ { typedef uint64_t Type; }; cannam@134: cannam@134: template struct Mask_ { cannam@134: // Union discriminants end up here. cannam@134: static_assert(sizeof(T) == 2, "Don't know how to mask this type."); cannam@134: typedef uint16_t Type; cannam@134: }; cannam@134: cannam@134: template cannam@134: using Mask = typename Mask_::Type; cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(Mask mask(T value, Mask mask)); cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T unmask(Mask value, Mask mask)); cannam@134: cannam@134: template cannam@134: inline Mask mask(T value, Mask mask) { cannam@134: return static_cast >(value) ^ mask; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline uint32_t mask(float value, uint32_t mask) { cannam@134: #if CAPNP_CANONICALIZE_NAN cannam@134: if (value != value) { cannam@134: return 0x7fc00000u ^ mask; cannam@134: } cannam@134: #endif cannam@134: cannam@134: uint32_t i; cannam@134: static_assert(sizeof(i) == sizeof(value), "float is not 32 bits?"); cannam@134: memcpy(&i, &value, sizeof(value)); cannam@134: return i ^ mask; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline uint64_t mask(double value, uint64_t mask) { cannam@134: #if CAPNP_CANONICALIZE_NAN cannam@134: if (value != value) { cannam@134: return 0x7ff8000000000000ull ^ mask; cannam@134: } cannam@134: #endif cannam@134: cannam@134: uint64_t i; cannam@134: static_assert(sizeof(i) == sizeof(value), "double is not 64 bits?"); cannam@134: memcpy(&i, &value, sizeof(value)); cannam@134: return i ^ mask; cannam@134: } cannam@134: cannam@134: template cannam@134: inline T unmask(Mask value, Mask mask) { cannam@134: return static_cast(value ^ mask); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline float unmask(uint32_t value, uint32_t mask) { cannam@134: value ^= mask; cannam@134: float result; cannam@134: static_assert(sizeof(result) == sizeof(value), "float is not 32 bits?"); cannam@134: memcpy(&result, &value, sizeof(value)); cannam@134: return result; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline double unmask(uint64_t value, uint64_t mask) { cannam@134: value ^= mask; cannam@134: double result; cannam@134: static_assert(sizeof(result) == sizeof(value), "double is not 64 bits?"); cannam@134: memcpy(&result, &value, sizeof(value)); cannam@134: return result; cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: class CapTableReader { cannam@134: public: cannam@134: #if !CAPNP_LITE cannam@134: virtual kj::Maybe> extractCap(uint index) = 0; cannam@134: // Extract the capability at the given index. If the index is invalid, returns null. cannam@134: #endif // !CAPNP_LITE cannam@134: }; cannam@134: cannam@134: class CapTableBuilder: public CapTableReader { cannam@134: public: cannam@134: #if !CAPNP_LITE cannam@134: virtual uint injectCap(kj::Own&& cap) = 0; cannam@134: // Add the capability to the message and return its index. If the same ClientHook is injected cannam@134: // twice, this may return the same index both times, but in this case dropCap() needs to be cannam@134: // called an equal number of times to actually remove the cap. cannam@134: cannam@134: virtual void dropCap(uint index) = 0; cannam@134: // Remove a capability injected earlier. Called when the pointer is overwritten or zero'd out. cannam@134: #endif // !CAPNP_LITE cannam@134: }; cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: class PointerBuilder: public kj::DisallowConstCopy { cannam@134: // Represents a single pointer, usually embedded in a struct or a list. cannam@134: cannam@134: public: cannam@134: inline PointerBuilder(): segment(nullptr), capTable(nullptr), pointer(nullptr) {} cannam@134: cannam@134: static inline PointerBuilder getRoot( cannam@134: SegmentBuilder* segment, CapTableBuilder* capTable, word* location); cannam@134: // Get a PointerBuilder representing a message root located in the given segment at the given cannam@134: // location. cannam@134: cannam@134: inline bool isNull() { return getPointerType() == PointerType::NULL_; } cannam@134: PointerType getPointerType(); cannam@134: cannam@134: StructBuilder getStruct(StructSize size, const word* defaultValue); cannam@134: ListBuilder getList(ElementSize elementSize, const word* defaultValue); cannam@134: ListBuilder getStructList(StructSize elementSize, const word* defaultValue); cannam@134: ListBuilder getListAnySize(const word* defaultValue); cannam@134: template typename T::Builder getBlob(const void* defaultValue,ByteCount defaultSize); cannam@134: #if !CAPNP_LITE cannam@134: kj::Own getCapability(); cannam@134: #endif // !CAPNP_LITE cannam@134: // Get methods: Get the value. If it is null, initialize it to a copy of the default value. cannam@134: // The default value is encoded as an "unchecked message" for structs, lists, and objects, or a cannam@134: // simple byte array for blobs. cannam@134: cannam@134: StructBuilder initStruct(StructSize size); cannam@134: ListBuilder initList(ElementSize elementSize, ElementCount elementCount); cannam@134: ListBuilder initStructList(ElementCount elementCount, StructSize size); cannam@134: template typename T::Builder initBlob(ByteCount size); cannam@134: // Init methods: Initialize the pointer to a newly-allocated object, discarding the existing cannam@134: // object. cannam@134: cannam@134: void setStruct(const StructReader& value, bool canonical = false); cannam@134: void setList(const ListReader& value, bool canonical = false); cannam@134: template void setBlob(typename T::Reader value); cannam@134: #if !CAPNP_LITE cannam@134: void setCapability(kj::Own&& cap); cannam@134: #endif // !CAPNP_LITE cannam@134: // Set methods: Initialize the pointer to a newly-allocated copy of the given value, discarding cannam@134: // the existing object. cannam@134: cannam@134: void adopt(OrphanBuilder&& orphan); cannam@134: // Set the pointer to point at the given orphaned value. cannam@134: cannam@134: OrphanBuilder disown(); cannam@134: // Set the pointer to null and return its previous value as an orphan. cannam@134: cannam@134: void clear(); cannam@134: // Clear the pointer to null, discarding its previous value. cannam@134: cannam@134: void transferFrom(PointerBuilder other); cannam@134: // Equivalent to `adopt(other.disown())`. cannam@134: cannam@134: void copyFrom(PointerReader other, bool canonical = false); cannam@134: // Equivalent to `set(other.get())`. cannam@134: // If you set the canonical flag, it will attempt to lay the target out cannam@134: // canonically, provided enough space is available. cannam@134: cannam@134: PointerReader asReader() const; cannam@134: cannam@134: BuilderArena* getArena() const; cannam@134: // Get the arena containing this pointer. cannam@134: cannam@134: CapTableBuilder* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: PointerBuilder imbue(CapTableBuilder* capTable); cannam@134: // Return a copy of this builder except using the given capability context. cannam@134: cannam@134: private: cannam@134: SegmentBuilder* segment; // Memory segment in which the pointer resides. cannam@134: CapTableBuilder* capTable; // Table of capability indexes. cannam@134: WirePointer* pointer; // Pointer to the pointer. cannam@134: cannam@134: inline PointerBuilder(SegmentBuilder* segment, CapTableBuilder* capTable, WirePointer* pointer) cannam@134: : segment(segment), capTable(capTable), pointer(pointer) {} cannam@134: cannam@134: friend class StructBuilder; cannam@134: friend class ListBuilder; cannam@134: friend class OrphanBuilder; cannam@134: }; cannam@134: cannam@134: class PointerReader { cannam@134: public: cannam@134: inline PointerReader() cannam@134: : segment(nullptr), capTable(nullptr), pointer(nullptr), nestingLimit(0x7fffffff) {} cannam@134: cannam@134: static PointerReader getRoot(SegmentReader* segment, CapTableReader* capTable, cannam@134: const word* location, int nestingLimit); cannam@134: // Get a PointerReader representing a message root located in the given segment at the given cannam@134: // location. cannam@134: cannam@134: static inline PointerReader getRootUnchecked(const word* location); cannam@134: // Get a PointerReader for an unchecked message. cannam@134: cannam@134: MessageSizeCounts targetSize() const; cannam@134: // Return the total size of the target object and everything to which it points. Does not count cannam@134: // far pointer overhead. This is useful for deciding how much space is needed to copy the object cannam@134: // into a flat array. However, the caller is advised NOT to treat this value as secure. Instead, cannam@134: // use the result as a hint for allocating the first segment, do the copy, and then throw an cannam@134: // exception if it overruns. cannam@134: cannam@134: inline bool isNull() const { return getPointerType() == PointerType::NULL_; } cannam@134: PointerType getPointerType() const; cannam@134: cannam@134: StructReader getStruct(const word* defaultValue) const; cannam@134: ListReader getList(ElementSize expectedElementSize, const word* defaultValue) const; cannam@134: ListReader getListAnySize(const word* defaultValue) const; cannam@134: template cannam@134: typename T::Reader getBlob(const void* defaultValue, ByteCount defaultSize) const; cannam@134: #if !CAPNP_LITE cannam@134: kj::Own getCapability() const; cannam@134: #endif // !CAPNP_LITE cannam@134: // Get methods: Get the value. If it is null, return the default value instead. cannam@134: // The default value is encoded as an "unchecked message" for structs, lists, and objects, or a cannam@134: // simple byte array for blobs. cannam@134: cannam@134: const word* getUnchecked() const; cannam@134: // If this is an unchecked message, get a word* pointing at the location of the pointer. This cannam@134: // word* can actually be passed to readUnchecked() to read the designated sub-object later. If cannam@134: // this isn't an unchecked message, throws an exception. cannam@134: cannam@134: kj::Maybe getArena() const; cannam@134: // Get the arena containing this pointer. cannam@134: cannam@134: CapTableReader* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: PointerReader imbue(CapTableReader* capTable) const; cannam@134: // Return a copy of this reader except using the given capability context. cannam@134: cannam@134: bool isCanonical(const word **readHead); cannam@134: // Validate this pointer's canonicity, subject to the conditions: cannam@134: // * All data to the left of readHead has been read thus far (for pointer cannam@134: // ordering) cannam@134: // * All pointers in preorder have already been checked cannam@134: // * This pointer is in the first and only segment of the message cannam@134: cannam@134: private: cannam@134: SegmentReader* segment; // Memory segment in which the pointer resides. cannam@134: CapTableReader* capTable; // Table of capability indexes. cannam@134: const WirePointer* pointer; // Pointer to the pointer. null = treat as null pointer. cannam@134: cannam@134: int nestingLimit; cannam@134: // Limits the depth of message structures to guard against stack-overflow-based DoS attacks. cannam@134: // Once this reaches zero, further pointers will be pruned. cannam@134: cannam@134: inline PointerReader(SegmentReader* segment, CapTableReader* capTable, cannam@134: const WirePointer* pointer, int nestingLimit) cannam@134: : segment(segment), capTable(capTable), pointer(pointer), nestingLimit(nestingLimit) {} cannam@134: cannam@134: friend class StructReader; cannam@134: friend class ListReader; cannam@134: friend class PointerBuilder; cannam@134: friend class OrphanBuilder; cannam@134: }; cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: class StructBuilder: public kj::DisallowConstCopy { cannam@134: public: cannam@134: inline StructBuilder(): segment(nullptr), capTable(nullptr), data(nullptr), pointers(nullptr) {} cannam@134: cannam@134: inline word* getLocation() { return reinterpret_cast(data); } cannam@134: // Get the object's location. Only valid for independently-allocated objects (i.e. not list cannam@134: // elements). cannam@134: cannam@134: inline BitCount getDataSectionSize() const { return dataSize; } cannam@134: inline WirePointerCount getPointerSectionSize() const { return pointerCount; } cannam@134: inline kj::ArrayPtr getDataSectionAsBlob(); cannam@134: inline _::ListBuilder getPointerSectionAsList(); cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(bool hasDataField(ElementCount offset)); cannam@134: // Return true if the field is set to something other than its default value. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T getDataField(ElementCount offset)); cannam@134: // Gets the data field value of the given type at the given offset. The offset is measured in cannam@134: // multiples of the field size, determined by the type. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T getDataField(ElementCount offset, Mask mask)); cannam@134: // Like getDataField() but applies the given XOR mask to the data on load. Used for reading cannam@134: // fields with non-zero default values. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(void setDataField( cannam@134: ElementCount offset, kj::NoInfer value)); cannam@134: // Sets the data field value at the given offset. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(void setDataField( cannam@134: ElementCount offset, kj::NoInfer value, Mask mask)); cannam@134: // Like setDataField() but applies the given XOR mask before storing. Used for writing fields cannam@134: // with non-zero default values. cannam@134: cannam@134: KJ_ALWAYS_INLINE(PointerBuilder getPointerField(WirePointerCount ptrIndex)); cannam@134: // Get a builder for a pointer field given the index within the pointer section. cannam@134: cannam@134: void clearAll(); cannam@134: // Clear all pointers and data. cannam@134: cannam@134: void transferContentFrom(StructBuilder other); cannam@134: // Adopt all pointers from `other`, and also copy all data. If `other`'s sections are larger cannam@134: // than this, the extra data is not transferred, meaning there is a risk of data loss when cannam@134: // transferring from messages built with future versions of the protocol. cannam@134: cannam@134: void copyContentFrom(StructReader other); cannam@134: // Copy content from `other`. If `other`'s sections are larger than this, the extra data is not cannam@134: // copied, meaning there is a risk of data loss when copying from messages built with future cannam@134: // versions of the protocol. cannam@134: cannam@134: StructReader asReader() const; cannam@134: // Gets a StructReader pointing at the same memory. cannam@134: cannam@134: BuilderArena* getArena(); cannam@134: // Gets the arena in which this object is allocated. cannam@134: cannam@134: CapTableBuilder* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: StructBuilder imbue(CapTableBuilder* capTable); cannam@134: // Return a copy of this builder except using the given capability context. cannam@134: cannam@134: private: cannam@134: SegmentBuilder* segment; // Memory segment in which the struct resides. cannam@134: CapTableBuilder* capTable; // Table of capability indexes. cannam@134: void* data; // Pointer to the encoded data. cannam@134: WirePointer* pointers; // Pointer to the encoded pointers. cannam@134: cannam@134: BitCount32 dataSize; cannam@134: // Size of data section. We use a bit count rather than a word count to more easily handle the cannam@134: // case of struct lists encoded with less than a word per element. cannam@134: cannam@134: WirePointerCount16 pointerCount; // Size of the pointer section. cannam@134: cannam@134: inline StructBuilder(SegmentBuilder* segment, CapTableBuilder* capTable, cannam@134: void* data, WirePointer* pointers, cannam@134: BitCount dataSize, WirePointerCount pointerCount) cannam@134: : segment(segment), capTable(capTable), data(data), pointers(pointers), cannam@134: dataSize(dataSize), pointerCount(pointerCount) {} cannam@134: cannam@134: friend class ListBuilder; cannam@134: friend struct WireHelpers; cannam@134: friend class OrphanBuilder; cannam@134: }; cannam@134: cannam@134: class StructReader { cannam@134: public: cannam@134: inline StructReader() cannam@134: : segment(nullptr), capTable(nullptr), data(nullptr), pointers(nullptr), dataSize(0), cannam@134: pointerCount(0), nestingLimit(0x7fffffff) {} cannam@134: inline StructReader(kj::ArrayPtr data) cannam@134: : segment(nullptr), capTable(nullptr), data(data.begin()), pointers(nullptr), cannam@134: dataSize(data.size() * WORDS * BITS_PER_WORD), pointerCount(0), nestingLimit(0x7fffffff) {} cannam@134: cannam@134: const void* getLocation() const { return data; } cannam@134: cannam@134: inline BitCount getDataSectionSize() const { return dataSize; } cannam@134: inline WirePointerCount getPointerSectionSize() const { return pointerCount; } cannam@134: inline kj::ArrayPtr getDataSectionAsBlob(); cannam@134: inline _::ListReader getPointerSectionAsList(); cannam@134: cannam@134: kj::Array canonicalize(); cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(bool hasDataField(ElementCount offset) const); cannam@134: // Return true if the field is set to something other than its default value. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T getDataField(ElementCount offset) const); cannam@134: // Get the data field value of the given type at the given offset. The offset is measured in cannam@134: // multiples of the field size, determined by the type. Returns zero if the offset is past the cannam@134: // end of the struct's data section. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE( cannam@134: T getDataField(ElementCount offset, Mask mask) const); cannam@134: // Like getDataField(offset), but applies the given XOR mask to the result. Used for reading cannam@134: // fields with non-zero default values. cannam@134: cannam@134: KJ_ALWAYS_INLINE(PointerReader getPointerField(WirePointerCount ptrIndex) const); cannam@134: // Get a reader for a pointer field given the index within the pointer section. If the index cannam@134: // is out-of-bounds, returns a null pointer. cannam@134: cannam@134: MessageSizeCounts totalSize() const; cannam@134: // Return the total size of the struct and everything to which it points. Does not count far cannam@134: // pointer overhead. This is useful for deciding how much space is needed to copy the struct cannam@134: // into a flat array. However, the caller is advised NOT to treat this value as secure. Instead, cannam@134: // use the result as a hint for allocating the first segment, do the copy, and then throw an cannam@134: // exception if it overruns. cannam@134: cannam@134: CapTableReader* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: StructReader imbue(CapTableReader* capTable) const; cannam@134: // Return a copy of this reader except using the given capability context. cannam@134: cannam@134: bool isCanonical(const word **readHead, const word **ptrHead, cannam@134: bool *dataTrunc, bool *ptrTrunc); cannam@134: // Validate this pointer's canonicity, subject to the conditions: cannam@134: // * All data to the left of readHead has been read thus far (for pointer cannam@134: // ordering) cannam@134: // * All pointers in preorder have already been checked cannam@134: // * This pointer is in the first and only segment of the message cannam@134: // cannam@134: // If this function returns false, the struct is non-canonical. If it cannam@134: // returns true, then: cannam@134: // * If it is a composite in a list, it is canonical if at least one struct cannam@134: // in the list outputs dataTrunc = 1, and at least one outputs ptrTrunc = 1 cannam@134: // * If it is derived from a struct pointer, it is canonical if cannam@134: // dataTrunc = 1 AND ptrTrunc = 1 cannam@134: cannam@134: private: cannam@134: SegmentReader* segment; // Memory segment in which the struct resides. cannam@134: CapTableReader* capTable; // Table of capability indexes. cannam@134: cannam@134: const void* data; cannam@134: const WirePointer* pointers; cannam@134: cannam@134: BitCount32 dataSize; cannam@134: // Size of data section. We use a bit count rather than a word count to more easily handle the cannam@134: // case of struct lists encoded with less than a word per element. cannam@134: cannam@134: WirePointerCount16 pointerCount; // Size of the pointer section. cannam@134: cannam@134: int nestingLimit; cannam@134: // Limits the depth of message structures to guard against stack-overflow-based DoS attacks. cannam@134: // Once this reaches zero, further pointers will be pruned. cannam@134: // TODO(perf): Limit to 16 bits for better packing? cannam@134: cannam@134: inline StructReader(SegmentReader* segment, CapTableReader* capTable, cannam@134: const void* data, const WirePointer* pointers, cannam@134: BitCount dataSize, WirePointerCount pointerCount, int nestingLimit) cannam@134: : segment(segment), capTable(capTable), data(data), pointers(pointers), cannam@134: dataSize(dataSize), pointerCount(pointerCount), cannam@134: nestingLimit(nestingLimit) {} cannam@134: cannam@134: friend class ListReader; cannam@134: friend class StructBuilder; cannam@134: friend struct WireHelpers; cannam@134: }; cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: class ListBuilder: public kj::DisallowConstCopy { cannam@134: public: cannam@134: inline explicit ListBuilder(ElementSize elementSize) cannam@134: : segment(nullptr), capTable(nullptr), ptr(nullptr), elementCount(0 * ELEMENTS), cannam@134: step(0 * BITS / ELEMENTS), structDataSize(0 * BITS), structPointerCount(0 * POINTERS), cannam@134: elementSize(elementSize) {} cannam@134: cannam@134: inline word* getLocation() { cannam@134: // Get the object's location. cannam@134: cannam@134: if (elementSize == ElementSize::INLINE_COMPOSITE && ptr != nullptr) { cannam@134: return reinterpret_cast(ptr) - POINTER_SIZE_IN_WORDS; cannam@134: } else { cannam@134: return reinterpret_cast(ptr); cannam@134: } cannam@134: } cannam@134: cannam@134: inline ElementSize getElementSize() const { return elementSize; } cannam@134: cannam@134: inline ElementCount size() const; cannam@134: // The number of elements in the list. cannam@134: cannam@134: Text::Builder asText(); cannam@134: Data::Builder asData(); cannam@134: // Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T getDataElement(ElementCount index)); cannam@134: // Get the element of the given type at the given index. cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(void setDataElement( cannam@134: ElementCount index, kj::NoInfer value)); cannam@134: // Set the element at the given index. cannam@134: cannam@134: KJ_ALWAYS_INLINE(PointerBuilder getPointerElement(ElementCount index)); cannam@134: cannam@134: StructBuilder getStructElement(ElementCount index); cannam@134: cannam@134: ListReader asReader() const; cannam@134: // Get a ListReader pointing at the same memory. cannam@134: cannam@134: BuilderArena* getArena(); cannam@134: // Gets the arena in which this object is allocated. cannam@134: cannam@134: CapTableBuilder* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: ListBuilder imbue(CapTableBuilder* capTable); cannam@134: // Return a copy of this builder except using the given capability context. cannam@134: cannam@134: private: cannam@134: SegmentBuilder* segment; // Memory segment in which the list resides. cannam@134: CapTableBuilder* capTable; // Table of capability indexes. cannam@134: cannam@134: byte* ptr; // Pointer to list content. cannam@134: cannam@134: ElementCount elementCount; // Number of elements in the list. cannam@134: cannam@134: decltype(BITS / ELEMENTS) step; cannam@134: // The distance between elements. cannam@134: cannam@134: BitCount32 structDataSize; cannam@134: WirePointerCount16 structPointerCount; cannam@134: // The struct properties to use when interpreting the elements as structs. All lists can be cannam@134: // interpreted as struct lists, so these are always filled in. cannam@134: cannam@134: ElementSize elementSize; cannam@134: // The element size as a ElementSize. This is only really needed to disambiguate INLINE_COMPOSITE cannam@134: // from other types when the overall size is exactly zero or one words. cannam@134: cannam@134: inline ListBuilder(SegmentBuilder* segment, CapTableBuilder* capTable, void* ptr, cannam@134: decltype(BITS / ELEMENTS) step, ElementCount size, cannam@134: BitCount structDataSize, WirePointerCount structPointerCount, cannam@134: ElementSize elementSize) cannam@134: : segment(segment), capTable(capTable), ptr(reinterpret_cast(ptr)), cannam@134: elementCount(size), step(step), structDataSize(structDataSize), cannam@134: structPointerCount(structPointerCount), elementSize(elementSize) {} cannam@134: cannam@134: friend class StructBuilder; cannam@134: friend struct WireHelpers; cannam@134: friend class OrphanBuilder; cannam@134: }; cannam@134: cannam@134: class ListReader { cannam@134: public: cannam@134: inline explicit ListReader(ElementSize elementSize) cannam@134: : segment(nullptr), capTable(nullptr), ptr(nullptr), elementCount(0), cannam@134: step(0 * BITS / ELEMENTS), structDataSize(0), structPointerCount(0), cannam@134: elementSize(elementSize), nestingLimit(0x7fffffff) {} cannam@134: cannam@134: inline ElementCount size() const; cannam@134: // The number of elements in the list. cannam@134: cannam@134: inline ElementSize getElementSize() const { return elementSize; } cannam@134: cannam@134: Text::Reader asText(); cannam@134: Data::Reader asData(); cannam@134: // Reinterpret the list as a blob. Throws an exception if the elements are not byte-sized. cannam@134: cannam@134: kj::ArrayPtr asRawBytes(); cannam@134: cannam@134: template cannam@134: KJ_ALWAYS_INLINE(T getDataElement(ElementCount index) const); cannam@134: // Get the element of the given type at the given index. cannam@134: cannam@134: KJ_ALWAYS_INLINE(PointerReader getPointerElement(ElementCount index) const); cannam@134: cannam@134: StructReader getStructElement(ElementCount index) const; cannam@134: cannam@134: CapTableReader* getCapTable(); cannam@134: // Gets the capability context in which this object is operating. cannam@134: cannam@134: ListReader imbue(CapTableReader* capTable) const; cannam@134: // Return a copy of this reader except using the given capability context. cannam@134: cannam@134: bool isCanonical(const word **readHead); cannam@134: // Validate this pointer's canonicity, subject to the conditions: cannam@134: // * All data to the left of readHead has been read thus far (for pointer cannam@134: // ordering) cannam@134: // * All pointers in preorder have already been checked cannam@134: // * This pointer is in the first and only segment of the message cannam@134: cannam@134: private: cannam@134: SegmentReader* segment; // Memory segment in which the list resides. cannam@134: CapTableReader* capTable; // Table of capability indexes. cannam@134: cannam@134: const byte* ptr; // Pointer to list content. cannam@134: cannam@134: ElementCount elementCount; // Number of elements in the list. cannam@134: cannam@134: decltype(BITS / ELEMENTS) step; cannam@134: // The distance between elements. cannam@134: cannam@134: BitCount32 structDataSize; cannam@134: WirePointerCount16 structPointerCount; cannam@134: // The struct properties to use when interpreting the elements as structs. All lists can be cannam@134: // interpreted as struct lists, so these are always filled in. cannam@134: cannam@134: ElementSize elementSize; cannam@134: // The element size as a ElementSize. This is only really needed to disambiguate INLINE_COMPOSITE cannam@134: // from other types when the overall size is exactly zero or one words. cannam@134: cannam@134: int nestingLimit; cannam@134: // Limits the depth of message structures to guard against stack-overflow-based DoS attacks. cannam@134: // Once this reaches zero, further pointers will be pruned. cannam@134: cannam@134: inline ListReader(SegmentReader* segment, CapTableReader* capTable, const void* ptr, cannam@134: ElementCount elementCount, decltype(BITS / ELEMENTS) step, cannam@134: BitCount structDataSize, WirePointerCount structPointerCount, cannam@134: ElementSize elementSize, int nestingLimit) cannam@134: : segment(segment), capTable(capTable), ptr(reinterpret_cast(ptr)), cannam@134: elementCount(elementCount), step(step), structDataSize(structDataSize), cannam@134: structPointerCount(structPointerCount), elementSize(elementSize), cannam@134: nestingLimit(nestingLimit) {} cannam@134: cannam@134: friend class StructReader; cannam@134: friend class ListBuilder; cannam@134: friend struct WireHelpers; cannam@134: friend class OrphanBuilder; cannam@134: }; cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: class OrphanBuilder { cannam@134: public: cannam@134: inline OrphanBuilder(): segment(nullptr), capTable(nullptr), location(nullptr) { cannam@134: memset(&tag, 0, sizeof(tag)); cannam@134: } cannam@134: OrphanBuilder(const OrphanBuilder& other) = delete; cannam@134: inline OrphanBuilder(OrphanBuilder&& other) noexcept; cannam@134: inline ~OrphanBuilder() noexcept(false); cannam@134: cannam@134: static OrphanBuilder initStruct(BuilderArena* arena, CapTableBuilder* capTable, StructSize size); cannam@134: static OrphanBuilder initList(BuilderArena* arena, CapTableBuilder* capTable, cannam@134: ElementCount elementCount, ElementSize elementSize); cannam@134: static OrphanBuilder initStructList(BuilderArena* arena, CapTableBuilder* capTable, cannam@134: ElementCount elementCount, StructSize elementSize); cannam@134: static OrphanBuilder initText(BuilderArena* arena, CapTableBuilder* capTable, ByteCount size); cannam@134: static OrphanBuilder initData(BuilderArena* arena, CapTableBuilder* capTable, ByteCount size); cannam@134: cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, StructReader copyFrom); cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, ListReader copyFrom); cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, PointerReader copyFrom); cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, Text::Reader copyFrom); cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, Data::Reader copyFrom); cannam@134: #if !CAPNP_LITE cannam@134: static OrphanBuilder copy(BuilderArena* arena, CapTableBuilder* capTable, cannam@134: kj::Own copyFrom); cannam@134: #endif // !CAPNP_LITE cannam@134: cannam@134: static OrphanBuilder concat(BuilderArena* arena, CapTableBuilder* capTable, cannam@134: ElementSize expectedElementSize, StructSize expectedStructSize, cannam@134: kj::ArrayPtr lists); cannam@134: cannam@134: static OrphanBuilder referenceExternalData(BuilderArena* arena, Data::Reader data); cannam@134: cannam@134: OrphanBuilder& operator=(const OrphanBuilder& other) = delete; cannam@134: inline OrphanBuilder& operator=(OrphanBuilder&& other); cannam@134: cannam@134: inline bool operator==(decltype(nullptr)) const { return location == nullptr; } cannam@134: inline bool operator!=(decltype(nullptr)) const { return location != nullptr; } cannam@134: cannam@134: StructBuilder asStruct(StructSize size); cannam@134: // Interpret as a struct, or throw an exception if not a struct. cannam@134: cannam@134: ListBuilder asList(ElementSize elementSize); cannam@134: // Interpret as a list, or throw an exception if not a list. elementSize cannot be cannam@134: // INLINE_COMPOSITE -- use asStructList() instead. cannam@134: cannam@134: ListBuilder asStructList(StructSize elementSize); cannam@134: // Interpret as a struct list, or throw an exception if not a list. cannam@134: cannam@134: Text::Builder asText(); cannam@134: Data::Builder asData(); cannam@134: // Interpret as a blob, or throw an exception if not a blob. cannam@134: cannam@134: StructReader asStructReader(StructSize size) const; cannam@134: ListReader asListReader(ElementSize elementSize) const; cannam@134: #if !CAPNP_LITE cannam@134: kj::Own asCapability() const; cannam@134: #endif // !CAPNP_LITE cannam@134: Text::Reader asTextReader() const; cannam@134: Data::Reader asDataReader() const; cannam@134: cannam@134: bool truncate(ElementCount size, bool isText) KJ_WARN_UNUSED_RESULT; cannam@134: // Resize the orphan list to the given size. Returns false if the list is currently empty but cannam@134: // the requested size is non-zero, in which case the caller will need to allocate a new list. cannam@134: cannam@134: void truncate(ElementCount size, ElementSize elementSize); cannam@134: void truncate(ElementCount size, StructSize elementSize); cannam@134: void truncateText(ElementCount size); cannam@134: // Versions of truncate() that know how to allocate a new list if needed. cannam@134: cannam@134: private: cannam@134: static_assert(1 * POINTERS * WORDS_PER_POINTER == 1 * WORDS, cannam@134: "This struct assumes a pointer is one word."); cannam@134: word tag; cannam@134: // Contains an encoded WirePointer representing this object. WirePointer is defined in cannam@134: // layout.c++, but fits in a word. cannam@134: // cannam@134: // This may be a FAR pointer. Even in that case, `location` points to the eventual destination cannam@134: // of that far pointer. The reason we keep the far pointer around rather than just making `tag` cannam@134: // represent the final destination is because if the eventual adopter of the pointer is not in cannam@134: // the target's segment then it may be useful to reuse the far pointer landing pad. cannam@134: // cannam@134: // If `tag` is not a far pointer, its offset is garbage; only `location` points to the actual cannam@134: // target. cannam@134: cannam@134: SegmentBuilder* segment; cannam@134: // Segment in which the object resides. cannam@134: cannam@134: CapTableBuilder* capTable; cannam@134: // Table of capability indexes. cannam@134: cannam@134: word* location; cannam@134: // Pointer to the object, or nullptr if the pointer is null. For capabilities, we make this cannam@134: // 0x1 just so that it is non-null for operator==, but it is never used. cannam@134: cannam@134: inline OrphanBuilder(const void* tagPtr, SegmentBuilder* segment, cannam@134: CapTableBuilder* capTable, word* location) cannam@134: : segment(segment), capTable(capTable), location(location) { cannam@134: memcpy(&tag, tagPtr, sizeof(tag)); cannam@134: } cannam@134: cannam@134: inline WirePointer* tagAsPtr() { return reinterpret_cast(&tag); } cannam@134: inline const WirePointer* tagAsPtr() const { return reinterpret_cast(&tag); } cannam@134: cannam@134: void euthanize(); cannam@134: // Erase the target object, zeroing it out and possibly reclaiming the memory. Called when cannam@134: // the OrphanBuilder is being destroyed or overwritten and it is non-null. cannam@134: cannam@134: friend struct WireHelpers; cannam@134: }; cannam@134: cannam@134: // ======================================================================================= cannam@134: // Internal implementation details... cannam@134: cannam@134: // These are defined in the source file. cannam@134: template <> typename Text::Builder PointerBuilder::initBlob(ByteCount size); cannam@134: template <> void PointerBuilder::setBlob(typename Text::Reader value); cannam@134: template <> typename Text::Builder PointerBuilder::getBlob(const void* defaultValue, ByteCount defaultSize); cannam@134: template <> typename Text::Reader PointerReader::getBlob(const void* defaultValue, ByteCount defaultSize) const; cannam@134: cannam@134: template <> typename Data::Builder PointerBuilder::initBlob(ByteCount size); cannam@134: template <> void PointerBuilder::setBlob(typename Data::Reader value); cannam@134: template <> typename Data::Builder PointerBuilder::getBlob(const void* defaultValue, ByteCount defaultSize); cannam@134: template <> typename Data::Reader PointerReader::getBlob(const void* defaultValue, ByteCount defaultSize) const; cannam@134: cannam@134: inline PointerBuilder PointerBuilder::getRoot( cannam@134: SegmentBuilder* segment, CapTableBuilder* capTable, word* location) { cannam@134: return PointerBuilder(segment, capTable, reinterpret_cast(location)); cannam@134: } cannam@134: cannam@134: inline PointerReader PointerReader::getRootUnchecked(const word* location) { cannam@134: return PointerReader(nullptr, nullptr, cannam@134: reinterpret_cast(location), 0x7fffffff); cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: inline kj::ArrayPtr StructBuilder::getDataSectionAsBlob() { cannam@134: return kj::ArrayPtr(reinterpret_cast(data), dataSize / BITS_PER_BYTE / BYTES); cannam@134: } cannam@134: cannam@134: inline _::ListBuilder StructBuilder::getPointerSectionAsList() { cannam@134: return _::ListBuilder(segment, capTable, pointers, 1 * POINTERS * BITS_PER_POINTER / ELEMENTS, cannam@134: pointerCount * (1 * ELEMENTS / POINTERS), cannam@134: 0 * BITS, 1 * POINTERS, ElementSize::POINTER); cannam@134: } cannam@134: cannam@134: template cannam@134: inline bool StructBuilder::hasDataField(ElementCount offset) { cannam@134: return getDataField>(offset) != 0; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool StructBuilder::hasDataField(ElementCount offset) { cannam@134: return false; cannam@134: } cannam@134: cannam@134: template cannam@134: inline T StructBuilder::getDataField(ElementCount offset) { cannam@134: return reinterpret_cast*>(data)[offset / ELEMENTS].get(); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool StructBuilder::getDataField(ElementCount offset) { cannam@134: BitCount boffset = offset * (1 * BITS / ELEMENTS); cannam@134: byte* b = reinterpret_cast(data) + boffset / BITS_PER_BYTE; cannam@134: return (*reinterpret_cast(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline Void StructBuilder::getDataField(ElementCount offset) { cannam@134: return VOID; cannam@134: } cannam@134: cannam@134: template cannam@134: inline T StructBuilder::getDataField(ElementCount offset, Mask mask) { cannam@134: return unmask(getDataField >(offset), mask); cannam@134: } cannam@134: cannam@134: template cannam@134: inline void StructBuilder::setDataField(ElementCount offset, kj::NoInfer value) { cannam@134: reinterpret_cast*>(data)[offset / ELEMENTS].set(value); cannam@134: } cannam@134: cannam@134: #if CAPNP_CANONICALIZE_NAN cannam@134: // Use mask() on floats and doubles to make sure we canonicalize NaNs. cannam@134: template <> cannam@134: inline void StructBuilder::setDataField(ElementCount offset, float value) { cannam@134: setDataField(offset, mask(value, 0)); cannam@134: } cannam@134: template <> cannam@134: inline void StructBuilder::setDataField(ElementCount offset, double value) { cannam@134: setDataField(offset, mask(value, 0)); cannam@134: } cannam@134: #endif cannam@134: cannam@134: template <> cannam@134: inline void StructBuilder::setDataField(ElementCount offset, bool value) { cannam@134: BitCount boffset = offset * (1 * BITS / ELEMENTS); cannam@134: byte* b = reinterpret_cast(data) + boffset / BITS_PER_BYTE; cannam@134: uint bitnum = boffset % BITS_PER_BYTE / BITS; cannam@134: *reinterpret_cast(b) = (*reinterpret_cast(b) & ~(1 << bitnum)) cannam@134: | (static_cast(value) << bitnum); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline void StructBuilder::setDataField(ElementCount offset, Void value) {} cannam@134: cannam@134: template cannam@134: inline void StructBuilder::setDataField(ElementCount offset, kj::NoInfer value, Mask m) { cannam@134: setDataField >(offset, mask(value, m)); cannam@134: } cannam@134: cannam@134: inline PointerBuilder StructBuilder::getPointerField(WirePointerCount ptrIndex) { cannam@134: // Hacky because WirePointer is defined in the .c++ file (so is incomplete here). cannam@134: return PointerBuilder(segment, capTable, reinterpret_cast( cannam@134: reinterpret_cast(pointers) + ptrIndex * WORDS_PER_POINTER)); cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: inline kj::ArrayPtr StructReader::getDataSectionAsBlob() { cannam@134: return kj::ArrayPtr(reinterpret_cast(data), dataSize / BITS_PER_BYTE / BYTES); cannam@134: } cannam@134: cannam@134: inline _::ListReader StructReader::getPointerSectionAsList() { cannam@134: return _::ListReader(segment, capTable, pointers, pointerCount * (1 * ELEMENTS / POINTERS), cannam@134: 1 * POINTERS * BITS_PER_POINTER / ELEMENTS, 0 * BITS, 1 * POINTERS, cannam@134: ElementSize::POINTER, nestingLimit); cannam@134: } cannam@134: cannam@134: template cannam@134: inline bool StructReader::hasDataField(ElementCount offset) const { cannam@134: return getDataField>(offset) != 0; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool StructReader::hasDataField(ElementCount offset) const { cannam@134: return false; cannam@134: } cannam@134: cannam@134: template cannam@134: inline T StructReader::getDataField(ElementCount offset) const { cannam@134: if ((offset + 1 * ELEMENTS) * capnp::bitsPerElement() <= dataSize) { cannam@134: return reinterpret_cast*>(data)[offset / ELEMENTS].get(); cannam@134: } else { cannam@134: return static_cast(0); cannam@134: } cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool StructReader::getDataField(ElementCount offset) const { cannam@134: BitCount boffset = offset * (1 * BITS / ELEMENTS); cannam@134: if (boffset < dataSize) { cannam@134: const byte* b = reinterpret_cast(data) + boffset / BITS_PER_BYTE; cannam@134: return (*reinterpret_cast(b) & (1 << (boffset % BITS_PER_BYTE / BITS))) != 0; cannam@134: } else { cannam@134: return false; cannam@134: } cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline Void StructReader::getDataField(ElementCount offset) const { cannam@134: return VOID; cannam@134: } cannam@134: cannam@134: template cannam@134: T StructReader::getDataField(ElementCount offset, Mask mask) const { cannam@134: return unmask(getDataField >(offset), mask); cannam@134: } cannam@134: cannam@134: inline PointerReader StructReader::getPointerField(WirePointerCount ptrIndex) const { cannam@134: if (ptrIndex < pointerCount) { cannam@134: // Hacky because WirePointer is defined in the .c++ file (so is incomplete here). cannam@134: return PointerReader(segment, capTable, reinterpret_cast( cannam@134: reinterpret_cast(pointers) + ptrIndex * WORDS_PER_POINTER), nestingLimit); cannam@134: } else{ cannam@134: return PointerReader(); cannam@134: } cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: inline ElementCount ListBuilder::size() const { return elementCount; } cannam@134: cannam@134: template cannam@134: inline T ListBuilder::getDataElement(ElementCount index) { cannam@134: return reinterpret_cast*>(ptr + index * step / BITS_PER_BYTE)->get(); cannam@134: cannam@134: // TODO(perf): Benchmark this alternate implementation, which I suspect may make better use of cannam@134: // the x86 SIB byte. Also use it for all the other getData/setData implementations below, and cannam@134: // the various non-inline methods that look up pointers. cannam@134: // Also if using this, consider changing ptr back to void* instead of byte*. cannam@134: // return reinterpret_cast*>(ptr)[ cannam@134: // index / ELEMENTS * (step / capnp::bitsPerElement())].get(); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool ListBuilder::getDataElement(ElementCount index) { cannam@134: // Ignore step for bit lists because bit lists cannot be upgraded to struct lists. cannam@134: BitCount bindex = index * (1 * BITS / ELEMENTS); cannam@134: byte* b = ptr + bindex / BITS_PER_BYTE; cannam@134: return (*reinterpret_cast(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline Void ListBuilder::getDataElement(ElementCount index) { cannam@134: return VOID; cannam@134: } cannam@134: cannam@134: template cannam@134: inline void ListBuilder::setDataElement(ElementCount index, kj::NoInfer value) { cannam@134: reinterpret_cast*>(ptr + index * step / BITS_PER_BYTE)->set(value); cannam@134: } cannam@134: cannam@134: #if CAPNP_CANONICALIZE_NAN cannam@134: // Use mask() on floats and doubles to make sure we canonicalize NaNs. cannam@134: template <> cannam@134: inline void ListBuilder::setDataElement(ElementCount index, float value) { cannam@134: setDataElement(index, mask(value, 0)); cannam@134: } cannam@134: template <> cannam@134: inline void ListBuilder::setDataElement(ElementCount index, double value) { cannam@134: setDataElement(index, mask(value, 0)); cannam@134: } cannam@134: #endif cannam@134: cannam@134: template <> cannam@134: inline void ListBuilder::setDataElement(ElementCount index, bool value) { cannam@134: // Ignore stepBytes for bit lists because bit lists cannot be upgraded to struct lists. cannam@134: BitCount bindex = index * (1 * BITS / ELEMENTS); cannam@134: byte* b = ptr + bindex / BITS_PER_BYTE; cannam@134: uint bitnum = bindex % BITS_PER_BYTE / BITS; cannam@134: *reinterpret_cast(b) = (*reinterpret_cast(b) & ~(1 << bitnum)) cannam@134: | (static_cast(value) << bitnum); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline void ListBuilder::setDataElement(ElementCount index, Void value) {} cannam@134: cannam@134: inline PointerBuilder ListBuilder::getPointerElement(ElementCount index) { cannam@134: return PointerBuilder(segment, capTable, cannam@134: reinterpret_cast(ptr + index * step / BITS_PER_BYTE)); cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: inline ElementCount ListReader::size() const { return elementCount; } cannam@134: cannam@134: template cannam@134: inline T ListReader::getDataElement(ElementCount index) const { cannam@134: return reinterpret_cast*>(ptr + index * step / BITS_PER_BYTE)->get(); cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline bool ListReader::getDataElement(ElementCount index) const { cannam@134: // Ignore step for bit lists because bit lists cannot be upgraded to struct lists. cannam@134: BitCount bindex = index * (1 * BITS / ELEMENTS); cannam@134: const byte* b = ptr + bindex / BITS_PER_BYTE; cannam@134: return (*reinterpret_cast(b) & (1 << (bindex % BITS_PER_BYTE / BITS))) != 0; cannam@134: } cannam@134: cannam@134: template <> cannam@134: inline Void ListReader::getDataElement(ElementCount index) const { cannam@134: return VOID; cannam@134: } cannam@134: cannam@134: inline PointerReader ListReader::getPointerElement(ElementCount index) const { cannam@134: return PointerReader(segment, capTable, cannam@134: reinterpret_cast(ptr + index * step / BITS_PER_BYTE), nestingLimit); cannam@134: } cannam@134: cannam@134: // ------------------------------------------------------------------- cannam@134: cannam@134: inline OrphanBuilder::OrphanBuilder(OrphanBuilder&& other) noexcept cannam@134: : segment(other.segment), capTable(other.capTable), location(other.location) { cannam@134: memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules. cannam@134: other.segment = nullptr; cannam@134: other.location = nullptr; cannam@134: } cannam@134: cannam@134: inline OrphanBuilder::~OrphanBuilder() noexcept(false) { cannam@134: if (segment != nullptr) euthanize(); cannam@134: } cannam@134: cannam@134: inline OrphanBuilder& OrphanBuilder::operator=(OrphanBuilder&& other) { cannam@134: // With normal smart pointers, it's important to handle the case where the incoming pointer cannam@134: // is actually transitively owned by this one. In this case, euthanize() would destroy `other` cannam@134: // before we copied it. This isn't possible in the case of `OrphanBuilder` because it only cannam@134: // owns message objects, and `other` is not itself a message object, therefore cannot possibly cannam@134: // be transitively owned by `this`. cannam@134: cannam@134: if (segment != nullptr) euthanize(); cannam@134: segment = other.segment; cannam@134: capTable = other.capTable; cannam@134: location = other.location; cannam@134: memcpy(&tag, &other.tag, sizeof(tag)); // Needs memcpy to comply with aliasing rules. cannam@134: other.segment = nullptr; cannam@134: other.location = nullptr; cannam@134: return *this; cannam@134: } cannam@134: cannam@134: } // namespace _ (private) cannam@134: } // namespace capnp cannam@134: cannam@134: #endif // CAPNP_LAYOUT_H_