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