annotate win64-msvc/include/capnp/arena.h @ 83:ae30d91d2ffe

Replace these with versions built using an older toolset (so as to avoid ABI compatibilities when linking on Ubuntu 14.04 for packaging purposes)
author Chris Cannam
date Fri, 07 Feb 2020 11:51:13 +0000
parents 0f2d93caa50c
children
rev   line source
Chris@63 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
Chris@63 2 // Licensed under the MIT License:
Chris@63 3 //
Chris@63 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
Chris@63 5 // of this software and associated documentation files (the "Software"), to deal
Chris@63 6 // in the Software without restriction, including without limitation the rights
Chris@63 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
Chris@63 8 // copies of the Software, and to permit persons to whom the Software is
Chris@63 9 // furnished to do so, subject to the following conditions:
Chris@63 10 //
Chris@63 11 // The above copyright notice and this permission notice shall be included in
Chris@63 12 // all copies or substantial portions of the Software.
Chris@63 13 //
Chris@63 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
Chris@63 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
Chris@63 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Chris@63 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
Chris@63 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
Chris@63 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
Chris@63 20 // THE SOFTWARE.
Chris@63 21
Chris@63 22 #ifndef CAPNP_ARENA_H_
Chris@63 23 #define CAPNP_ARENA_H_
Chris@63 24
Chris@63 25 #if defined(__GNUC__) && !defined(CAPNP_HEADER_WARNINGS)
Chris@63 26 #pragma GCC system_header
Chris@63 27 #endif
Chris@63 28
Chris@63 29 #ifndef CAPNP_PRIVATE
Chris@63 30 #error "This header is only meant to be included by Cap'n Proto's own source code."
Chris@63 31 #endif
Chris@63 32
Chris@63 33 #include <kj/common.h>
Chris@63 34 #include <kj/mutex.h>
Chris@63 35 #include <kj/exception.h>
Chris@63 36 #include <kj/vector.h>
Chris@63 37 #include <kj/units.h>
Chris@63 38 #include "common.h"
Chris@63 39 #include "message.h"
Chris@63 40 #include "layout.h"
Chris@63 41 #include <unordered_map>
Chris@63 42
Chris@63 43 #if !CAPNP_LITE
Chris@63 44 #include "capability.h"
Chris@63 45 #endif // !CAPNP_LITE
Chris@63 46
Chris@63 47 namespace capnp {
Chris@63 48
Chris@63 49 #if !CAPNP_LITE
Chris@63 50 class ClientHook;
Chris@63 51 #endif // !CAPNP_LITE
Chris@63 52
Chris@63 53 namespace _ { // private
Chris@63 54
Chris@63 55 class SegmentReader;
Chris@63 56 class SegmentBuilder;
Chris@63 57 class Arena;
Chris@63 58 class BuilderArena;
Chris@63 59 class ReadLimiter;
Chris@63 60
Chris@63 61 class Segment;
Chris@63 62 typedef kj::Id<uint32_t, Segment> SegmentId;
Chris@63 63
Chris@63 64 class ReadLimiter {
Chris@63 65 // Used to keep track of how much data has been processed from a message, and cut off further
Chris@63 66 // processing if and when a particular limit is reached. This is primarily intended to guard
Chris@63 67 // against maliciously-crafted messages which contain cycles or overlapping structures. Cycles
Chris@63 68 // and overlapping are not permitted by the Cap'n Proto format because in many cases they could
Chris@63 69 // be used to craft a deceptively small message which could consume excessive server resources to
Chris@63 70 // process, perhaps even sending it into an infinite loop. Actually detecting overlaps would be
Chris@63 71 // time-consuming, so instead we just keep track of how many words worth of data structures the
Chris@63 72 // receiver has actually dereferenced and error out if this gets too high.
Chris@63 73 //
Chris@63 74 // This counting takes place as you call getters (for non-primitive values) on the message
Chris@63 75 // readers. If you call the same getter twice, the data it returns may be double-counted. This
Chris@63 76 // should not be a big deal in most cases -- just set the read limit high enough that it will
Chris@63 77 // only trigger in unreasonable cases.
Chris@63 78 //
Chris@63 79 // This class is "safe" to use from multiple threads for its intended use case. Threads may
Chris@63 80 // overwrite each others' changes to the counter, but this is OK because it only means that the
Chris@63 81 // limit is enforced a bit less strictly -- it will still kick in eventually.
Chris@63 82
Chris@63 83 public:
Chris@63 84 inline explicit ReadLimiter(); // No limit.
Chris@63 85 inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words.
Chris@63 86
Chris@63 87 inline void reset(WordCount64 limit);
Chris@63 88
Chris@63 89 KJ_ALWAYS_INLINE(bool canRead(WordCount64 amount, Arena* arena));
Chris@63 90
Chris@63 91 void unread(WordCount64 amount);
Chris@63 92 // Adds back some words to the limit. Useful when the caller knows they are double-reading
Chris@63 93 // some data.
Chris@63 94
Chris@63 95 private:
Chris@63 96 volatile uint64_t limit;
Chris@63 97 // Current limit, decremented each time catRead() is called. Volatile because multiple threads
Chris@63 98 // could be trying to modify it at once. (This is not real thread-safety, but good enough for
Chris@63 99 // the purpose of this class. See class comment.)
Chris@63 100
Chris@63 101 KJ_DISALLOW_COPY(ReadLimiter);
Chris@63 102 };
Chris@63 103
Chris@63 104 #if !CAPNP_LITE
Chris@63 105 class BrokenCapFactory {
Chris@63 106 // Callback for constructing broken caps. We use this so that we can avoid arena.c++ having a
Chris@63 107 // link-time dependency on capability code that lives in libcapnp-rpc.
Chris@63 108
Chris@63 109 public:
Chris@63 110 virtual kj::Own<ClientHook> newBrokenCap(kj::StringPtr description) = 0;
Chris@63 111 virtual kj::Own<ClientHook> newNullCap() = 0;
Chris@63 112 };
Chris@63 113 #endif // !CAPNP_LITE
Chris@63 114
Chris@63 115 class SegmentReader {
Chris@63 116 public:
Chris@63 117 inline SegmentReader(Arena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
Chris@63 118 ReadLimiter* readLimiter);
Chris@63 119
Chris@63 120 KJ_ALWAYS_INLINE(const word* checkOffset(const word* from, ptrdiff_t offset));
Chris@63 121 // Adds the given offset to the given pointer, checks that it is still within the bounds of the
Chris@63 122 // segment, then returns it. Note that the "end" pointer of the segment (which technically points
Chris@63 123 // to the word after the last in the segment) is considered in-bounds for this purpose, so you
Chris@63 124 // can't necessarily dereference it. You must call checkObject() next to check that the object
Chris@63 125 // you want to read is entirely in-bounds.
Chris@63 126 //
Chris@63 127 // If `from + offset` is out-of-range, this returns a pointer to the end of the segment. Thus,
Chris@63 128 // any non-zero-sized object will fail `checkObject()`. We do this instead of throwing to save
Chris@63 129 // some code footprint.
Chris@63 130
Chris@63 131 KJ_ALWAYS_INLINE(bool checkObject(const word* start, WordCountN<31> size));
Chris@63 132 // Assuming that `start` is in-bounds for this segment (probably checked using `checkOffset()`),
Chris@63 133 // check that `start + size` is also in-bounds, and hence the whole area in-between is valid.
Chris@63 134
Chris@63 135 KJ_ALWAYS_INLINE(bool amplifiedRead(WordCount virtualAmount));
Chris@63 136 // Indicates that the reader should pretend that `virtualAmount` additional data was read even
Chris@63 137 // though no actual pointer was traversed. This is used e.g. when reading a struct list pointer
Chris@63 138 // where the element sizes are zero -- the sender could set the list size arbitrarily high and
Chris@63 139 // cause the receiver to iterate over this list even though the message itself is small, so we
Chris@63 140 // need to defend against DoS attacks based on this.
Chris@63 141
Chris@63 142 inline Arena* getArena();
Chris@63 143 inline SegmentId getSegmentId();
Chris@63 144
Chris@63 145 inline const word* getStartPtr();
Chris@63 146 inline SegmentWordCount getOffsetTo(const word* ptr);
Chris@63 147 inline SegmentWordCount getSize();
Chris@63 148
Chris@63 149 inline kj::ArrayPtr<const word> getArray();
Chris@63 150
Chris@63 151 inline void unread(WordCount64 amount);
Chris@63 152 // Add back some words to the ReadLimiter.
Chris@63 153
Chris@63 154 private:
Chris@63 155 Arena* arena;
Chris@63 156 SegmentId id;
Chris@63 157 kj::ArrayPtr<const word> ptr; // size guaranteed to fit in SEGMENT_WORD_COUNT_BITS bits
Chris@63 158 ReadLimiter* readLimiter;
Chris@63 159
Chris@63 160 KJ_DISALLOW_COPY(SegmentReader);
Chris@63 161
Chris@63 162 friend class SegmentBuilder;
Chris@63 163
Chris@63 164 static void abortCheckObjectFault();
Chris@63 165 // Called in debug mode in cases that would segfault in opt mode. (Should be impossible!)
Chris@63 166 };
Chris@63 167
Chris@63 168 class SegmentBuilder: public SegmentReader {
Chris@63 169 public:
Chris@63 170 inline SegmentBuilder(BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
Chris@63 171 ReadLimiter* readLimiter, SegmentWordCount wordsUsed = ZERO * WORDS);
Chris@63 172 inline SegmentBuilder(BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
Chris@63 173 ReadLimiter* readLimiter);
Chris@63 174 inline SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
Chris@63 175 ReadLimiter* readLimiter);
Chris@63 176
Chris@63 177 KJ_ALWAYS_INLINE(word* allocate(SegmentWordCount amount));
Chris@63 178
Chris@63 179 KJ_ALWAYS_INLINE(void checkWritable());
Chris@63 180 // Throw an exception if the segment is read-only (meaning it is a reference to external data).
Chris@63 181
Chris@63 182 KJ_ALWAYS_INLINE(word* getPtrUnchecked(SegmentWordCount offset));
Chris@63 183 // Get a writable pointer into the segment. Throws an exception if the segment is read-only (i.e.
Chris@63 184 // a reference to external immutable data).
Chris@63 185
Chris@63 186 inline BuilderArena* getArena();
Chris@63 187
Chris@63 188 inline kj::ArrayPtr<const word> currentlyAllocated();
Chris@63 189
Chris@63 190 inline void reset();
Chris@63 191
Chris@63 192 inline bool isWritable() { return !readOnly; }
Chris@63 193
Chris@63 194 inline void tryTruncate(word* from, word* to);
Chris@63 195 // If `from` points just past the current end of the segment, then move the end back to `to`.
Chris@63 196 // Otherwise, do nothing.
Chris@63 197
Chris@63 198 inline bool tryExtend(word* from, word* to);
Chris@63 199 // If `from` points just past the current end of the segment, and `to` is within the segment
Chris@63 200 // boundaries, then move the end up to `to` and return true. Otherwise, do nothing and return
Chris@63 201 // false.
Chris@63 202
Chris@63 203 private:
Chris@63 204 word* pos;
Chris@63 205 // Pointer to a pointer to the current end point of the segment, i.e. the location where the
Chris@63 206 // next object should be allocated.
Chris@63 207
Chris@63 208 bool readOnly;
Chris@63 209
Chris@63 210 void throwNotWritable();
Chris@63 211
Chris@63 212 KJ_DISALLOW_COPY(SegmentBuilder);
Chris@63 213 };
Chris@63 214
Chris@63 215 class Arena {
Chris@63 216 public:
Chris@63 217 virtual ~Arena() noexcept(false);
Chris@63 218
Chris@63 219 virtual SegmentReader* tryGetSegment(SegmentId id) = 0;
Chris@63 220 // Gets the segment with the given ID, or return nullptr if no such segment exists.
Chris@63 221
Chris@63 222 virtual void reportReadLimitReached() = 0;
Chris@63 223 // Called to report that the read limit has been reached. See ReadLimiter, below. This invokes
Chris@63 224 // the VALIDATE_INPUT() macro which may throw an exception; if it returns normally, the caller
Chris@63 225 // will need to continue with default values.
Chris@63 226 };
Chris@63 227
Chris@63 228 class ReaderArena final: public Arena {
Chris@63 229 public:
Chris@63 230 explicit ReaderArena(MessageReader* message);
Chris@63 231 ~ReaderArena() noexcept(false);
Chris@63 232 KJ_DISALLOW_COPY(ReaderArena);
Chris@63 233
Chris@63 234 // implements Arena ------------------------------------------------
Chris@63 235 SegmentReader* tryGetSegment(SegmentId id) override;
Chris@63 236 void reportReadLimitReached() override;
Chris@63 237
Chris@63 238 private:
Chris@63 239 MessageReader* message;
Chris@63 240 ReadLimiter readLimiter;
Chris@63 241
Chris@63 242 // Optimize for single-segment messages so that small messages are handled quickly.
Chris@63 243 SegmentReader segment0;
Chris@63 244
Chris@63 245 typedef std::unordered_map<uint, kj::Own<SegmentReader>> SegmentMap;
Chris@63 246 kj::MutexGuarded<kj::Maybe<kj::Own<SegmentMap>>> moreSegments;
Chris@63 247 // We need to mutex-guard the segment map because we lazily initialize segments when they are
Chris@63 248 // first requested, but a Reader is allowed to be used concurrently in multiple threads. Luckily
Chris@63 249 // this only applies to large messages.
Chris@63 250 //
Chris@63 251 // TODO(perf): Thread-local thing instead? Some kind of lockless map? Or do sharing of data
Chris@63 252 // in a different way, where you have to construct a new MessageReader in each thread (but
Chris@63 253 // possibly backed by the same data)?
Chris@63 254
Chris@63 255 ReaderArena(MessageReader* message, kj::ArrayPtr<const word> firstSegment);
Chris@63 256 ReaderArena(MessageReader* message, const word* firstSegment, SegmentWordCount firstSegmentSize);
Chris@63 257 };
Chris@63 258
Chris@63 259 class BuilderArena final: public Arena {
Chris@63 260 // A BuilderArena that does not allow the injection of capabilities.
Chris@63 261
Chris@63 262 public:
Chris@63 263 explicit BuilderArena(MessageBuilder* message);
Chris@63 264 BuilderArena(MessageBuilder* message, kj::ArrayPtr<MessageBuilder::SegmentInit> segments);
Chris@63 265 ~BuilderArena() noexcept(false);
Chris@63 266 KJ_DISALLOW_COPY(BuilderArena);
Chris@63 267
Chris@63 268 inline SegmentBuilder* getRootSegment() { return &segment0; }
Chris@63 269
Chris@63 270 kj::ArrayPtr<const kj::ArrayPtr<const word>> getSegmentsForOutput();
Chris@63 271 // Get an array of all the segments, suitable for writing out. This only returns the allocated
Chris@63 272 // portion of each segment, whereas tryGetSegment() returns something that includes
Chris@63 273 // not-yet-allocated space.
Chris@63 274
Chris@63 275 inline CapTableBuilder* getLocalCapTable() {
Chris@63 276 // Return a CapTableBuilder that merely implements local loopback. That is, you can set
Chris@63 277 // capabilities, then read the same capabilities back, but there is no intent ever to transmit
Chris@63 278 // these capabilities. A MessageBuilder that isn't imbued with some other CapTable uses this
Chris@63 279 // by default.
Chris@63 280 //
Chris@63 281 // TODO(cleanup): It's sort of a hack that this exists. In theory, perhaps, unimbued
Chris@63 282 // MessageBuilders should throw exceptions on any attempt to access capability fields, like
Chris@63 283 // unimbued MessageReaders do. However, lots of code exists which uses MallocMessageBuilder
Chris@63 284 // as a temporary holder for data to be copied in and out (without being serialized), and it
Chris@63 285 // is expected that such data can include capabilities, which is admittedly reasonable.
Chris@63 286 // Therefore, all MessageBuilders must have a cap table by default. Arguably we should
Chris@63 287 // deprecate this usage and instead define a new helper type for this exact purpose.
Chris@63 288
Chris@63 289 return &localCapTable;
Chris@63 290 }
Chris@63 291
Chris@63 292 SegmentBuilder* getSegment(SegmentId id);
Chris@63 293 // Get the segment with the given id. Crashes or throws an exception if no such segment exists.
Chris@63 294
Chris@63 295 struct AllocateResult {
Chris@63 296 SegmentBuilder* segment;
Chris@63 297 word* words;
Chris@63 298 };
Chris@63 299
Chris@63 300 AllocateResult allocate(SegmentWordCount amount);
Chris@63 301 // Find a segment with at least the given amount of space available and allocate the space.
Chris@63 302 // Note that allocating directly from a particular segment is much faster, but allocating from
Chris@63 303 // the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
Chris@63 304 // segment first if there is one, then fall back to the arena.
Chris@63 305
Chris@63 306 SegmentBuilder* addExternalSegment(kj::ArrayPtr<const word> content);
Chris@63 307 // Add a new segment to the arena which points to some existing memory region. The segment is
Chris@63 308 // assumed to be completley full; the arena will never allocate from it. In fact, the segment
Chris@63 309 // is considered read-only. Any attempt to get a Builder pointing into this segment will throw
Chris@63 310 // an exception. Readers are allowed, however.
Chris@63 311 //
Chris@63 312 // This can be used to inject some external data into a message without a copy, e.g. embedding a
Chris@63 313 // large mmap'd file into a message as `Data` without forcing that data to actually be read in
Chris@63 314 // from disk (until the message itself is written out). `Orphanage` provides the public API for
Chris@63 315 // this feature.
Chris@63 316
Chris@63 317 // implements Arena ------------------------------------------------
Chris@63 318 SegmentReader* tryGetSegment(SegmentId id) override;
Chris@63 319 void reportReadLimitReached() override;
Chris@63 320
Chris@63 321 private:
Chris@63 322 MessageBuilder* message;
Chris@63 323 ReadLimiter dummyLimiter;
Chris@63 324
Chris@63 325 class LocalCapTable: public CapTableBuilder {
Chris@63 326 #if !CAPNP_LITE
Chris@63 327 public:
Chris@63 328 kj::Maybe<kj::Own<ClientHook>> extractCap(uint index) override;
Chris@63 329 uint injectCap(kj::Own<ClientHook>&& cap) override;
Chris@63 330 void dropCap(uint index) override;
Chris@63 331
Chris@63 332 private:
Chris@63 333 kj::Vector<kj::Maybe<kj::Own<ClientHook>>> capTable;
Chris@63 334 #endif // ! CAPNP_LITE
Chris@63 335 };
Chris@63 336
Chris@63 337 LocalCapTable localCapTable;
Chris@63 338
Chris@63 339 SegmentBuilder segment0;
Chris@63 340 kj::ArrayPtr<const word> segment0ForOutput;
Chris@63 341
Chris@63 342 struct MultiSegmentState {
Chris@63 343 kj::Vector<kj::Own<SegmentBuilder>> builders;
Chris@63 344 kj::Vector<kj::ArrayPtr<const word>> forOutput;
Chris@63 345 };
Chris@63 346 kj::Maybe<kj::Own<MultiSegmentState>> moreSegments;
Chris@63 347
Chris@63 348 SegmentBuilder* segmentWithSpace = nullptr;
Chris@63 349 // When allocating, look for space in this segment first before resorting to allocating a new
Chris@63 350 // segment. This is not necessarily the last segment because addExternalSegment() may add a
Chris@63 351 // segment that is already-full, in which case we don't update this pointer.
Chris@63 352
Chris@63 353 template <typename T> // Can be `word` or `const word`.
Chris@63 354 SegmentBuilder* addSegmentInternal(kj::ArrayPtr<T> content);
Chris@63 355 };
Chris@63 356
Chris@63 357 // =======================================================================================
Chris@63 358
Chris@63 359 inline ReadLimiter::ReadLimiter()
Chris@63 360 : limit(kj::maxValue) {}
Chris@63 361
Chris@63 362 inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(unbound(limit / WORDS)) {}
Chris@63 363
Chris@63 364 inline void ReadLimiter::reset(WordCount64 limit) { this->limit = unbound(limit / WORDS); }
Chris@63 365
Chris@63 366 inline bool ReadLimiter::canRead(WordCount64 amount, Arena* arena) {
Chris@63 367 // Be careful not to store an underflowed value into `limit`, even if multiple threads are
Chris@63 368 // decrementing it.
Chris@63 369 uint64_t current = limit;
Chris@63 370 if (KJ_UNLIKELY(unbound(amount / WORDS) > current)) {
Chris@63 371 arena->reportReadLimitReached();
Chris@63 372 return false;
Chris@63 373 } else {
Chris@63 374 limit = current - unbound(amount / WORDS);
Chris@63 375 return true;
Chris@63 376 }
Chris@63 377 }
Chris@63 378
Chris@63 379 // -------------------------------------------------------------------
Chris@63 380
Chris@63 381 inline SegmentReader::SegmentReader(Arena* arena, SegmentId id, const word* ptr,
Chris@63 382 SegmentWordCount size, ReadLimiter* readLimiter)
Chris@63 383 : arena(arena), id(id), ptr(kj::arrayPtr(ptr, unbound(size / WORDS))),
Chris@63 384 readLimiter(readLimiter) {}
Chris@63 385
Chris@63 386 inline const word* SegmentReader::checkOffset(const word* from, ptrdiff_t offset) {
Chris@63 387 ptrdiff_t min = ptr.begin() - from;
Chris@63 388 ptrdiff_t max = ptr.end() - from;
Chris@63 389 if (offset >= min && offset <= max) {
Chris@63 390 return from + offset;
Chris@63 391 } else {
Chris@63 392 return ptr.end();
Chris@63 393 }
Chris@63 394 }
Chris@63 395
Chris@63 396 inline bool SegmentReader::checkObject(const word* start, WordCountN<31> size) {
Chris@63 397 auto startOffset = intervalLength(ptr.begin(), start, MAX_SEGMENT_WORDS);
Chris@63 398 #ifdef KJ_DEBUG
Chris@63 399 if (startOffset > bounded(ptr.size()) * WORDS) {
Chris@63 400 abortCheckObjectFault();
Chris@63 401 }
Chris@63 402 #endif
Chris@63 403 return startOffset + size <= bounded(ptr.size()) * WORDS &&
Chris@63 404 readLimiter->canRead(size, arena);
Chris@63 405 }
Chris@63 406
Chris@63 407 inline bool SegmentReader::amplifiedRead(WordCount virtualAmount) {
Chris@63 408 return readLimiter->canRead(virtualAmount, arena);
Chris@63 409 }
Chris@63 410
Chris@63 411 inline Arena* SegmentReader::getArena() { return arena; }
Chris@63 412 inline SegmentId SegmentReader::getSegmentId() { return id; }
Chris@63 413 inline const word* SegmentReader::getStartPtr() { return ptr.begin(); }
Chris@63 414 inline SegmentWordCount SegmentReader::getOffsetTo(const word* ptr) {
Chris@63 415 KJ_IREQUIRE(this->ptr.begin() <= ptr && ptr <= this->ptr.end());
Chris@63 416 return intervalLength(this->ptr.begin(), ptr, MAX_SEGMENT_WORDS);
Chris@63 417 }
Chris@63 418 inline SegmentWordCount SegmentReader::getSize() {
Chris@63 419 return assumeBits<SEGMENT_WORD_COUNT_BITS>(ptr.size()) * WORDS;
Chris@63 420 }
Chris@63 421 inline kj::ArrayPtr<const word> SegmentReader::getArray() { return ptr; }
Chris@63 422 inline void SegmentReader::unread(WordCount64 amount) { readLimiter->unread(amount); }
Chris@63 423
Chris@63 424 // -------------------------------------------------------------------
Chris@63 425
Chris@63 426 inline SegmentBuilder::SegmentBuilder(
Chris@63 427 BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
Chris@63 428 ReadLimiter* readLimiter, SegmentWordCount wordsUsed)
Chris@63 429 : SegmentReader(arena, id, ptr, size, readLimiter),
Chris@63 430 pos(ptr + wordsUsed), readOnly(false) {}
Chris@63 431 inline SegmentBuilder::SegmentBuilder(
Chris@63 432 BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
Chris@63 433 ReadLimiter* readLimiter)
Chris@63 434 : SegmentReader(arena, id, ptr, size, readLimiter),
Chris@63 435 // const_cast is safe here because the member won't ever be dereferenced because it appears
Chris@63 436 // to point to the end of the segment anyway.
Chris@63 437 pos(const_cast<word*>(ptr + size)), readOnly(true) {}
Chris@63 438 inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
Chris@63 439 ReadLimiter* readLimiter)
Chris@63 440 : SegmentReader(arena, id, nullptr, ZERO * WORDS, readLimiter),
Chris@63 441 pos(nullptr), readOnly(false) {}
Chris@63 442
Chris@63 443 inline word* SegmentBuilder::allocate(SegmentWordCount amount) {
Chris@63 444 if (intervalLength(pos, ptr.end(), MAX_SEGMENT_WORDS) < amount) {
Chris@63 445 // Not enough space in the segment for this allocation.
Chris@63 446 return nullptr;
Chris@63 447 } else {
Chris@63 448 // Success.
Chris@63 449 word* result = pos;
Chris@63 450 pos = pos + amount;
Chris@63 451 return result;
Chris@63 452 }
Chris@63 453 }
Chris@63 454
Chris@63 455 inline void SegmentBuilder::checkWritable() {
Chris@63 456 if (KJ_UNLIKELY(readOnly)) throwNotWritable();
Chris@63 457 }
Chris@63 458
Chris@63 459 inline word* SegmentBuilder::getPtrUnchecked(SegmentWordCount offset) {
Chris@63 460 return const_cast<word*>(ptr.begin() + offset);
Chris@63 461 }
Chris@63 462
Chris@63 463 inline BuilderArena* SegmentBuilder::getArena() {
Chris@63 464 // Down-cast safe because SegmentBuilder's constructor always initializes its SegmentReader base
Chris@63 465 // class with an Arena pointer that actually points to a BuilderArena.
Chris@63 466 return static_cast<BuilderArena*>(arena);
Chris@63 467 }
Chris@63 468
Chris@63 469 inline kj::ArrayPtr<const word> SegmentBuilder::currentlyAllocated() {
Chris@63 470 return kj::arrayPtr(ptr.begin(), pos - ptr.begin());
Chris@63 471 }
Chris@63 472
Chris@63 473 inline void SegmentBuilder::reset() {
Chris@63 474 word* start = getPtrUnchecked(ZERO * WORDS);
Chris@63 475 memset(start, 0, (pos - start) * sizeof(word));
Chris@63 476 pos = start;
Chris@63 477 }
Chris@63 478
Chris@63 479 inline void SegmentBuilder::tryTruncate(word* from, word* to) {
Chris@63 480 if (pos == from) pos = to;
Chris@63 481 }
Chris@63 482
Chris@63 483 inline bool SegmentBuilder::tryExtend(word* from, word* to) {
Chris@63 484 // Careful about overflow.
Chris@63 485 if (pos == from && to <= ptr.end() && to >= from) {
Chris@63 486 pos = to;
Chris@63 487 return true;
Chris@63 488 } else {
Chris@63 489 return false;
Chris@63 490 }
Chris@63 491 }
Chris@63 492
Chris@63 493 } // namespace _ (private)
Chris@63 494 } // namespace capnp
Chris@63 495
Chris@63 496 #endif // CAPNP_ARENA_H_