capnproto

FORK: Cap'n Proto serialization/RPC system - core tools and C++ library
git clone https://git.neptards.moe/neptards/capnproto.git
Log | Files | Refs | README | LICENSE

message.c++ (10892B)


      1 // Copyright (c) 2013-2016 Sandstorm Development Group, Inc. and contributors
      2 // Licensed under the MIT License:
      3 //
      4 // Permission is hereby granted, free of charge, to any person obtaining a copy
      5 // of this software and associated documentation files (the "Software"), to deal
      6 // in the Software without restriction, including without limitation the rights
      7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      8 // copies of the Software, and to permit persons to whom the Software is
      9 // furnished to do so, subject to the following conditions:
     10 //
     11 // The above copyright notice and this permission notice shall be included in
     12 // all copies or substantial portions of the Software.
     13 //
     14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     20 // THE SOFTWARE.
     21 
     22 #define CAPNP_PRIVATE
     23 #include "message.h"
     24 #include <kj/debug.h>
     25 #include "arena.h"
     26 #include "orphan.h"
     27 #include <stdlib.h>
     28 #include <errno.h>
     29 
     30 namespace capnp {
     31 
     32 namespace {
     33 
     34 class DummyCapTableReader: public _::CapTableReader {
     35 public:
     36   kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override {
     37 #if CAPNP_LITE
     38     KJ_UNIMPLEMENTED("no cap tables in lite mode");
     39 #else
     40     return nullptr;
     41 #endif
     42   }
     43 };
     44 static KJ_CONSTEXPR(const) DummyCapTableReader dummyCapTableReader = DummyCapTableReader();
     45 
     46 }  // namespace
     47 
     48 MessageReader::MessageReader(ReaderOptions options): options(options), allocatedArena(false) {}
     49 MessageReader::~MessageReader() noexcept(false) {
     50   if (allocatedArena) {
     51     arena()->~ReaderArena();
     52   }
     53 }
     54 
     55 bool MessageReader::isCanonical() {
     56   if (!allocatedArena) {
     57     static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
     58         "arenaSpace is too small to hold a ReaderArena.  Please increase it.  This will break "
     59         "ABI compatibility.");
     60     kj::ctor(*arena(), this);
     61     allocatedArena = true;
     62   }
     63 
     64   _::SegmentReader *segment = arena()->tryGetSegment(_::SegmentId(0));
     65 
     66   if (segment == NULL) {
     67     // The message has no segments
     68     return false;
     69   }
     70 
     71   if (arena()->tryGetSegment(_::SegmentId(1))) {
     72     // The message has more than one segment
     73     return false;
     74   }
     75 
     76   const word* readHead = segment->getStartPtr() + 1;
     77   bool rootIsCanonical = _::PointerReader::getRoot(segment, nullptr,
     78                                                    segment->getStartPtr(),
     79                                                    this->getOptions().nestingLimit)
     80                                                   .isCanonical(&readHead);
     81   bool allWordsConsumed = segment->getOffsetTo(readHead) == segment->getSize();
     82   return rootIsCanonical && allWordsConsumed;
     83 }
     84 
     85 size_t MessageReader::sizeInWords() {
     86   return arena()->sizeInWords();
     87 }
     88 
     89 AnyPointer::Reader MessageReader::getRootInternal() {
     90   if (!allocatedArena) {
     91     static_assert(sizeof(_::ReaderArena) <= sizeof(arenaSpace),
     92         "arenaSpace is too small to hold a ReaderArena.  Please increase it.  This will break "
     93         "ABI compatibility.");
     94     kj::ctor(*arena(), this);
     95     allocatedArena = true;
     96   }
     97 
     98   _::SegmentReader* segment = arena()->tryGetSegment(_::SegmentId(0));
     99   KJ_REQUIRE(segment != nullptr &&
    100              segment->checkObject(segment->getStartPtr(), ONE * WORDS),
    101              "Message did not contain a root pointer.") {
    102     return AnyPointer::Reader();
    103   }
    104 
    105   // const_cast here is safe because dummyCapTableReader has no state.
    106   return AnyPointer::Reader(_::PointerReader::getRoot(
    107       segment, const_cast<DummyCapTableReader*>(&dummyCapTableReader),
    108       segment->getStartPtr(), options.nestingLimit));
    109 }
    110 
    111 // -------------------------------------------------------------------
    112 
    113 MessageBuilder::MessageBuilder(): allocatedArena(false) {}
    114 
    115 MessageBuilder::~MessageBuilder() noexcept(false) {
    116   if (allocatedArena) {
    117     kj::dtor(*arena());
    118   }
    119 }
    120 
    121 MessageBuilder::MessageBuilder(kj::ArrayPtr<SegmentInit> segments)
    122     : allocatedArena(false) {
    123   kj::ctor(*arena(), this, segments);
    124   allocatedArena = true;
    125 }
    126 
    127 _::SegmentBuilder* MessageBuilder::getRootSegment() {
    128   if (allocatedArena) {
    129     return arena()->getSegment(_::SegmentId(0));
    130   } else {
    131     static_assert(sizeof(_::BuilderArena) <= sizeof(arenaSpace),
    132         "arenaSpace is too small to hold a BuilderArena.  Please increase it.");
    133     kj::ctor(*arena(), this);
    134     allocatedArena = true;
    135 
    136     auto allocation = arena()->allocate(POINTER_SIZE_IN_WORDS);
    137 
    138     KJ_ASSERT(allocation.segment->getSegmentId() == _::SegmentId(0),
    139         "First allocated word of new arena was not in segment ID 0.");
    140     KJ_ASSERT(allocation.words == allocation.segment->getPtrUnchecked(ZERO * WORDS),
    141         "First allocated word of new arena was not the first word in its segment.");
    142     return allocation.segment;
    143   }
    144 }
    145 
    146 AnyPointer::Builder MessageBuilder::getRootInternal() {
    147   _::SegmentBuilder* rootSegment = getRootSegment();
    148   return AnyPointer::Builder(_::PointerBuilder::getRoot(
    149       rootSegment, arena()->getLocalCapTable(), rootSegment->getPtrUnchecked(ZERO * WORDS)));
    150 }
    151 
    152 kj::ArrayPtr<const kj::ArrayPtr<const word>> MessageBuilder::getSegmentsForOutput() {
    153   if (allocatedArena) {
    154     return arena()->getSegmentsForOutput();
    155   } else {
    156     return nullptr;
    157   }
    158 }
    159 
    160 Orphanage MessageBuilder::getOrphanage() {
    161   // We must ensure that the arena and root pointer have been allocated before the Orphanage
    162   // can be used.
    163   if (!allocatedArena) getRootSegment();
    164 
    165   return Orphanage(arena(), arena()->getLocalCapTable());
    166 }
    167 
    168 bool MessageBuilder::isCanonical() {
    169   _::SegmentReader *segment = getRootSegment();
    170 
    171   if (segment == NULL) {
    172     // The message has no segments
    173     return false;
    174   }
    175 
    176   if (arena()->tryGetSegment(_::SegmentId(1))) {
    177     // The message has more than one segment
    178     return false;
    179   }
    180 
    181   const word* readHead = segment->getStartPtr() + 1;
    182   return _::PointerReader::getRoot(segment, nullptr, segment->getStartPtr(), kj::maxValue)
    183                                   .isCanonical(&readHead);
    184 }
    185 
    186 size_t MessageBuilder::sizeInWords() {
    187   return arena()->sizeInWords();
    188 }
    189 
    190 kj::Own<_::CapTableBuilder> MessageBuilder::releaseBuiltinCapTable() {
    191   return arena()->releaseLocalCapTable();
    192 }
    193 
    194 // =======================================================================================
    195 
    196 SegmentArrayMessageReader::SegmentArrayMessageReader(
    197     kj::ArrayPtr<const kj::ArrayPtr<const word>> segments, ReaderOptions options)
    198     : MessageReader(options), segments(segments) {}
    199 
    200 SegmentArrayMessageReader::~SegmentArrayMessageReader() noexcept(false) {}
    201 
    202 kj::ArrayPtr<const word> SegmentArrayMessageReader::getSegment(uint id) {
    203   if (id < segments.size()) {
    204     return segments[id];
    205   } else {
    206     return nullptr;
    207   }
    208 }
    209 
    210 // -------------------------------------------------------------------
    211 
    212 MallocMessageBuilder::MallocMessageBuilder(
    213     uint firstSegmentWords, AllocationStrategy allocationStrategy)
    214     : nextSize(firstSegmentWords), allocationStrategy(allocationStrategy),
    215       ownFirstSegment(true), returnedFirstSegment(false), firstSegment(nullptr) {}
    216 
    217 MallocMessageBuilder::MallocMessageBuilder(
    218     kj::ArrayPtr<word> firstSegment, AllocationStrategy allocationStrategy)
    219     : nextSize(firstSegment.size()), allocationStrategy(allocationStrategy),
    220       ownFirstSegment(false), returnedFirstSegment(false), firstSegment(firstSegment.begin()) {
    221   KJ_REQUIRE(firstSegment.size() > 0, "First segment size must be non-zero.");
    222 
    223   // Checking just the first word should catch most cases of failing to zero the segment.
    224   KJ_REQUIRE(*reinterpret_cast<uint64_t*>(firstSegment.begin()) == 0,
    225           "First segment must be zeroed.");
    226 }
    227 
    228 MallocMessageBuilder::~MallocMessageBuilder() noexcept(false) {
    229   if (returnedFirstSegment) {
    230     if (ownFirstSegment) {
    231       free(firstSegment);
    232     } else {
    233       // Must zero first segment.
    234       kj::ArrayPtr<const kj::ArrayPtr<const word>> segments = getSegmentsForOutput();
    235       if (segments.size() > 0) {
    236         KJ_ASSERT(segments[0].begin() == firstSegment,
    237             "First segment in getSegmentsForOutput() is not the first segment allocated?");
    238         memset(firstSegment, 0, segments[0].size() * sizeof(word));
    239       }
    240     }
    241 
    242     for (void* ptr: moreSegments) {
    243       free(ptr);
    244     }
    245   }
    246 }
    247 
    248 kj::ArrayPtr<word> MallocMessageBuilder::allocateSegment(uint minimumSize) {
    249   KJ_REQUIRE(bounded(minimumSize) * WORDS <= MAX_SEGMENT_WORDS,
    250       "MallocMessageBuilder asked to allocate segment above maximum serializable size.");
    251   KJ_ASSERT(bounded(nextSize) * WORDS <= MAX_SEGMENT_WORDS,
    252       "MallocMessageBuilder nextSize out of bounds.");
    253 
    254   if (!returnedFirstSegment && !ownFirstSegment) {
    255     kj::ArrayPtr<word> result = kj::arrayPtr(reinterpret_cast<word*>(firstSegment), nextSize);
    256     if (result.size() >= minimumSize) {
    257       returnedFirstSegment = true;
    258       return result;
    259     }
    260     // If the provided first segment wasn't big enough, we discard it and proceed to allocate
    261     // our own.  This never happens in practice since minimumSize is always 1 for the first
    262     // segment.
    263     ownFirstSegment = true;
    264   }
    265 
    266   uint size = kj::max(minimumSize, nextSize);
    267 
    268   void* result = calloc(size, sizeof(word));
    269   if (result == nullptr) {
    270     KJ_FAIL_SYSCALL("calloc(size, sizeof(word))", ENOMEM, size);
    271   }
    272 
    273   if (!returnedFirstSegment) {
    274     firstSegment = result;
    275     returnedFirstSegment = true;
    276 
    277     // After the first segment, we want nextSize to equal the total size allocated so far.
    278     if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) nextSize = size;
    279   } else {
    280     moreSegments.add(result);
    281     if (allocationStrategy == AllocationStrategy::GROW_HEURISTICALLY) {
    282       // set nextSize = min(nextSize+size, MAX_SEGMENT_WORDS)
    283       // while protecting against possible overflow of (nextSize+size)
    284       nextSize = (size <= unbound(MAX_SEGMENT_WORDS / WORDS) - nextSize)
    285           ? nextSize + size : unbound(MAX_SEGMENT_WORDS / WORDS);
    286     }
    287   }
    288 
    289   return kj::arrayPtr(reinterpret_cast<word*>(result), size);
    290 }
    291 
    292 // -------------------------------------------------------------------
    293 
    294 FlatMessageBuilder::FlatMessageBuilder(kj::ArrayPtr<word> array): array(array), allocated(false) {}
    295 FlatMessageBuilder::~FlatMessageBuilder() noexcept(false) {}
    296 
    297 void FlatMessageBuilder::requireFilled() {
    298   KJ_REQUIRE(getSegmentsForOutput()[0].end() == array.end(),
    299           "FlatMessageBuilder's buffer was too large.");
    300 }
    301 
    302 kj::ArrayPtr<word> FlatMessageBuilder::allocateSegment(uint minimumSize) {
    303   KJ_REQUIRE(!allocated, "FlatMessageBuilder's buffer was not large enough.");
    304   allocated = true;
    305   return array;
    306 }
    307 
    308 }  // namespace capnp