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