comparison win64-msvc/include/capnp/arena.h @ 148:b4bfdf10c4b3

Update Win64 capnp builds to v0.6
author Chris Cannam <cannam@all-day-breakfast.com>
date Mon, 22 May 2017 18:56:49 +0100
parents 42a73082be24
children
comparison
equal deleted inserted replaced
147:45360b968bf4 148:b4bfdf10c4b3
32 32
33 #include <kj/common.h> 33 #include <kj/common.h>
34 #include <kj/mutex.h> 34 #include <kj/mutex.h>
35 #include <kj/exception.h> 35 #include <kj/exception.h>
36 #include <kj/vector.h> 36 #include <kj/vector.h>
37 #include <kj/units.h>
37 #include "common.h" 38 #include "common.h"
38 #include "message.h" 39 #include "message.h"
39 #include "layout.h" 40 #include "layout.h"
40 #include <unordered_map> 41 #include <unordered_map>
41 42
83 inline explicit ReadLimiter(); // No limit. 84 inline explicit ReadLimiter(); // No limit.
84 inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words. 85 inline explicit ReadLimiter(WordCount64 limit); // Limit to the given number of words.
85 86
86 inline void reset(WordCount64 limit); 87 inline void reset(WordCount64 limit);
87 88
88 KJ_ALWAYS_INLINE(bool canRead(WordCount amount, Arena* arena)); 89 KJ_ALWAYS_INLINE(bool canRead(WordCount64 amount, Arena* arena));
89 90
90 void unread(WordCount64 amount); 91 void unread(WordCount64 amount);
91 // Adds back some words to the limit. Useful when the caller knows they are double-reading 92 // Adds back some words to the limit. Useful when the caller knows they are double-reading
92 // some data. 93 // some data.
93 94
111 }; 112 };
112 #endif // !CAPNP_LITE 113 #endif // !CAPNP_LITE
113 114
114 class SegmentReader { 115 class SegmentReader {
115 public: 116 public:
116 inline SegmentReader(Arena* arena, SegmentId id, kj::ArrayPtr<const word> ptr, 117 inline SegmentReader(Arena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
117 ReadLimiter* readLimiter); 118 ReadLimiter* readLimiter);
118 119
119 KJ_ALWAYS_INLINE(bool containsInterval(const void* from, const void* to)); 120 KJ_ALWAYS_INLINE(const word* checkOffset(const word* from, ptrdiff_t offset));
121 // Adds the given offset to the given pointer, checks that it is still within the bounds of the
122 // segment, then returns it. Note that the "end" pointer of the segment (which technically points
123 // to the word after the last in the segment) is considered in-bounds for this purpose, so you
124 // can't necessarily dereference it. You must call checkObject() next to check that the object
125 // you want to read is entirely in-bounds.
126 //
127 // If `from + offset` is out-of-range, this returns a pointer to the end of the segment. Thus,
128 // any non-zero-sized object will fail `checkObject()`. We do this instead of throwing to save
129 // some code footprint.
130
131 KJ_ALWAYS_INLINE(bool checkObject(const word* start, WordCountN<31> size));
132 // Assuming that `start` is in-bounds for this segment (probably checked using `checkOffset()`),
133 // check that `start + size` is also in-bounds, and hence the whole area in-between is valid.
120 134
121 KJ_ALWAYS_INLINE(bool amplifiedRead(WordCount virtualAmount)); 135 KJ_ALWAYS_INLINE(bool amplifiedRead(WordCount virtualAmount));
122 // Indicates that the reader should pretend that `virtualAmount` additional data was read even 136 // Indicates that the reader should pretend that `virtualAmount` additional data was read even
123 // though no actual pointer was traversed. This is used e.g. when reading a struct list pointer 137 // though no actual pointer was traversed. This is used e.g. when reading a struct list pointer
124 // where the element sizes are zero -- the sender could set the list size arbitrarily high and 138 // where the element sizes are zero -- the sender could set the list size arbitrarily high and
127 141
128 inline Arena* getArena(); 142 inline Arena* getArena();
129 inline SegmentId getSegmentId(); 143 inline SegmentId getSegmentId();
130 144
131 inline const word* getStartPtr(); 145 inline const word* getStartPtr();
132 inline WordCount getOffsetTo(const word* ptr); 146 inline SegmentWordCount getOffsetTo(const word* ptr);
133 inline WordCount getSize(); 147 inline SegmentWordCount getSize();
134 148
135 inline kj::ArrayPtr<const word> getArray(); 149 inline kj::ArrayPtr<const word> getArray();
136 150
137 inline void unread(WordCount64 amount); 151 inline void unread(WordCount64 amount);
138 // Add back some words to the ReadLimiter. 152 // Add back some words to the ReadLimiter.
139 153
140 private: 154 private:
141 Arena* arena; 155 Arena* arena;
142 SegmentId id; 156 SegmentId id;
143 kj::ArrayPtr<const word> ptr; 157 kj::ArrayPtr<const word> ptr; // size guaranteed to fit in SEGMENT_WORD_COUNT_BITS bits
144 ReadLimiter* readLimiter; 158 ReadLimiter* readLimiter;
145 159
146 KJ_DISALLOW_COPY(SegmentReader); 160 KJ_DISALLOW_COPY(SegmentReader);
147 161
148 friend class SegmentBuilder; 162 friend class SegmentBuilder;
163
164 static void abortCheckObjectFault();
165 // Called in debug mode in cases that would segfault in opt mode. (Should be impossible!)
149 }; 166 };
150 167
151 class SegmentBuilder: public SegmentReader { 168 class SegmentBuilder: public SegmentReader {
152 public: 169 public:
153 inline SegmentBuilder(BuilderArena* arena, SegmentId id, kj::ArrayPtr<word> ptr, 170 inline SegmentBuilder(BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
154 ReadLimiter* readLimiter, size_t wordsUsed = 0); 171 ReadLimiter* readLimiter, SegmentWordCount wordsUsed = ZERO * WORDS);
155 inline SegmentBuilder(BuilderArena* arena, SegmentId id, kj::ArrayPtr<const word> ptr, 172 inline SegmentBuilder(BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
156 ReadLimiter* readLimiter); 173 ReadLimiter* readLimiter);
157 inline SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr), 174 inline SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
158 ReadLimiter* readLimiter); 175 ReadLimiter* readLimiter);
159 176
160 KJ_ALWAYS_INLINE(word* allocate(WordCount amount)); 177 KJ_ALWAYS_INLINE(word* allocate(SegmentWordCount amount));
161 178
162 KJ_ALWAYS_INLINE(void checkWritable()); 179 KJ_ALWAYS_INLINE(void checkWritable());
163 // Throw an exception if the segment is read-only (meaning it is a reference to external data). 180 // Throw an exception if the segment is read-only (meaning it is a reference to external data).
164 181
165 KJ_ALWAYS_INLINE(word* getPtrUnchecked(WordCount offset)); 182 KJ_ALWAYS_INLINE(word* getPtrUnchecked(SegmentWordCount offset));
166 // Get a writable pointer into the segment. Throws an exception if the segment is read-only (i.e. 183 // Get a writable pointer into the segment. Throws an exception if the segment is read-only (i.e.
167 // a reference to external immutable data). 184 // a reference to external immutable data).
168 185
169 inline BuilderArena* getArena(); 186 inline BuilderArena* getArena();
170 187
208 // will need to continue with default values. 225 // will need to continue with default values.
209 }; 226 };
210 227
211 class ReaderArena final: public Arena { 228 class ReaderArena final: public Arena {
212 public: 229 public:
213 ReaderArena(MessageReader* message); 230 explicit ReaderArena(MessageReader* message);
214 ~ReaderArena() noexcept(false); 231 ~ReaderArena() noexcept(false);
215 KJ_DISALLOW_COPY(ReaderArena); 232 KJ_DISALLOW_COPY(ReaderArena);
216 233
217 // implements Arena ------------------------------------------------ 234 // implements Arena ------------------------------------------------
218 SegmentReader* tryGetSegment(SegmentId id) override; 235 SegmentReader* tryGetSegment(SegmentId id) override;
232 // this only applies to large messages. 249 // this only applies to large messages.
233 // 250 //
234 // TODO(perf): Thread-local thing instead? Some kind of lockless map? Or do sharing of data 251 // TODO(perf): Thread-local thing instead? Some kind of lockless map? Or do sharing of data
235 // in a different way, where you have to construct a new MessageReader in each thread (but 252 // in a different way, where you have to construct a new MessageReader in each thread (but
236 // possibly backed by the same data)? 253 // possibly backed by the same data)?
254
255 ReaderArena(MessageReader* message, kj::ArrayPtr<const word> firstSegment);
256 ReaderArena(MessageReader* message, const word* firstSegment, SegmentWordCount firstSegmentSize);
237 }; 257 };
238 258
239 class BuilderArena final: public Arena { 259 class BuilderArena final: public Arena {
240 // A BuilderArena that does not allow the injection of capabilities. 260 // A BuilderArena that does not allow the injection of capabilities.
241 261
275 struct AllocateResult { 295 struct AllocateResult {
276 SegmentBuilder* segment; 296 SegmentBuilder* segment;
277 word* words; 297 word* words;
278 }; 298 };
279 299
280 AllocateResult allocate(WordCount amount); 300 AllocateResult allocate(SegmentWordCount amount);
281 // Find a segment with at least the given amount of space available and allocate the space. 301 // Find a segment with at least the given amount of space available and allocate the space.
282 // Note that allocating directly from a particular segment is much faster, but allocating from 302 // Note that allocating directly from a particular segment is much faster, but allocating from
283 // the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific 303 // the arena is guaranteed to succeed. Therefore callers should try to allocate from a specific
284 // segment first if there is one, then fall back to the arena. 304 // segment first if there is one, then fall back to the arena.
285 305
337 // ======================================================================================= 357 // =======================================================================================
338 358
339 inline ReadLimiter::ReadLimiter() 359 inline ReadLimiter::ReadLimiter()
340 : limit(kj::maxValue) {} 360 : limit(kj::maxValue) {}
341 361
342 inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(limit / WORDS) {} 362 inline ReadLimiter::ReadLimiter(WordCount64 limit): limit(unbound(limit / WORDS)) {}
343 363
344 inline void ReadLimiter::reset(WordCount64 limit) { this->limit = limit / WORDS; } 364 inline void ReadLimiter::reset(WordCount64 limit) { this->limit = unbound(limit / WORDS); }
345 365
346 inline bool ReadLimiter::canRead(WordCount amount, Arena* arena) { 366 inline bool ReadLimiter::canRead(WordCount64 amount, Arena* arena) {
347 // Be careful not to store an underflowed value into `limit`, even if multiple threads are 367 // Be careful not to store an underflowed value into `limit`, even if multiple threads are
348 // decrementing it. 368 // decrementing it.
349 uint64_t current = limit; 369 uint64_t current = limit;
350 if (KJ_UNLIKELY(amount / WORDS > current)) { 370 if (KJ_UNLIKELY(unbound(amount / WORDS) > current)) {
351 arena->reportReadLimitReached(); 371 arena->reportReadLimitReached();
352 return false; 372 return false;
353 } else { 373 } else {
354 limit = current - amount / WORDS; 374 limit = current - unbound(amount / WORDS);
355 return true; 375 return true;
356 } 376 }
357 } 377 }
358 378
359 // ------------------------------------------------------------------- 379 // -------------------------------------------------------------------
360 380
361 inline SegmentReader::SegmentReader(Arena* arena, SegmentId id, kj::ArrayPtr<const word> ptr, 381 inline SegmentReader::SegmentReader(Arena* arena, SegmentId id, const word* ptr,
362 ReadLimiter* readLimiter) 382 SegmentWordCount size, ReadLimiter* readLimiter)
363 : arena(arena), id(id), ptr(ptr), readLimiter(readLimiter) {} 383 : arena(arena), id(id), ptr(kj::arrayPtr(ptr, unbound(size / WORDS))),
364 384 readLimiter(readLimiter) {}
365 inline bool SegmentReader::containsInterval(const void* from, const void* to) { 385
366 return from >= this->ptr.begin() && to <= this->ptr.end() && from <= to && 386 inline const word* SegmentReader::checkOffset(const word* from, ptrdiff_t offset) {
367 readLimiter->canRead( 387 ptrdiff_t min = ptr.begin() - from;
368 intervalLength(reinterpret_cast<const byte*>(from), 388 ptrdiff_t max = ptr.end() - from;
369 reinterpret_cast<const byte*>(to)) / BYTES_PER_WORD, 389 if (offset >= min && offset <= max) {
370 arena); 390 return from + offset;
391 } else {
392 return ptr.end();
393 }
394 }
395
396 inline bool SegmentReader::checkObject(const word* start, WordCountN<31> size) {
397 auto startOffset = intervalLength(ptr.begin(), start, MAX_SEGMENT_WORDS);
398 #ifdef KJ_DEBUG
399 if (startOffset > bounded(ptr.size()) * WORDS) {
400 abortCheckObjectFault();
401 }
402 #endif
403 return startOffset + size <= bounded(ptr.size()) * WORDS &&
404 readLimiter->canRead(size, arena);
371 } 405 }
372 406
373 inline bool SegmentReader::amplifiedRead(WordCount virtualAmount) { 407 inline bool SegmentReader::amplifiedRead(WordCount virtualAmount) {
374 return readLimiter->canRead(virtualAmount, arena); 408 return readLimiter->canRead(virtualAmount, arena);
375 } 409 }
376 410
377 inline Arena* SegmentReader::getArena() { return arena; } 411 inline Arena* SegmentReader::getArena() { return arena; }
378 inline SegmentId SegmentReader::getSegmentId() { return id; } 412 inline SegmentId SegmentReader::getSegmentId() { return id; }
379 inline const word* SegmentReader::getStartPtr() { return ptr.begin(); } 413 inline const word* SegmentReader::getStartPtr() { return ptr.begin(); }
380 inline WordCount SegmentReader::getOffsetTo(const word* ptr) { 414 inline SegmentWordCount SegmentReader::getOffsetTo(const word* ptr) {
381 return intervalLength(this->ptr.begin(), ptr); 415 KJ_IREQUIRE(this->ptr.begin() <= ptr && ptr <= this->ptr.end());
382 } 416 return intervalLength(this->ptr.begin(), ptr, MAX_SEGMENT_WORDS);
383 inline WordCount SegmentReader::getSize() { return ptr.size() * WORDS; } 417 }
418 inline SegmentWordCount SegmentReader::getSize() {
419 return assumeBits<SEGMENT_WORD_COUNT_BITS>(ptr.size()) * WORDS;
420 }
384 inline kj::ArrayPtr<const word> SegmentReader::getArray() { return ptr; } 421 inline kj::ArrayPtr<const word> SegmentReader::getArray() { return ptr; }
385 inline void SegmentReader::unread(WordCount64 amount) { readLimiter->unread(amount); } 422 inline void SegmentReader::unread(WordCount64 amount) { readLimiter->unread(amount); }
386 423
387 // ------------------------------------------------------------------- 424 // -------------------------------------------------------------------
388 425
389 inline SegmentBuilder::SegmentBuilder( 426 inline SegmentBuilder::SegmentBuilder(
390 BuilderArena* arena, SegmentId id, kj::ArrayPtr<word> ptr, ReadLimiter* readLimiter, 427 BuilderArena* arena, SegmentId id, word* ptr, SegmentWordCount size,
391 size_t wordsUsed) 428 ReadLimiter* readLimiter, SegmentWordCount wordsUsed)
392 : SegmentReader(arena, id, ptr, readLimiter), pos(ptr.begin() + wordsUsed), readOnly(false) {} 429 : SegmentReader(arena, id, ptr, size, readLimiter),
430 pos(ptr + wordsUsed), readOnly(false) {}
393 inline SegmentBuilder::SegmentBuilder( 431 inline SegmentBuilder::SegmentBuilder(
394 BuilderArena* arena, SegmentId id, kj::ArrayPtr<const word> ptr, ReadLimiter* readLimiter) 432 BuilderArena* arena, SegmentId id, const word* ptr, SegmentWordCount size,
395 : SegmentReader(arena, id, ptr, readLimiter), 433 ReadLimiter* readLimiter)
434 : SegmentReader(arena, id, ptr, size, readLimiter),
396 // const_cast is safe here because the member won't ever be dereferenced because it appears 435 // const_cast is safe here because the member won't ever be dereferenced because it appears
397 // to point to the end of the segment anyway. 436 // to point to the end of the segment anyway.
398 pos(const_cast<word*>(ptr.end())), 437 pos(const_cast<word*>(ptr + size)), readOnly(true) {}
399 readOnly(true) {}
400 inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr), 438 inline SegmentBuilder::SegmentBuilder(BuilderArena* arena, SegmentId id, decltype(nullptr),
401 ReadLimiter* readLimiter) 439 ReadLimiter* readLimiter)
402 : SegmentReader(arena, id, nullptr, readLimiter), pos(nullptr), readOnly(false) {} 440 : SegmentReader(arena, id, nullptr, ZERO * WORDS, readLimiter),
403 441 pos(nullptr), readOnly(false) {}
404 inline word* SegmentBuilder::allocate(WordCount amount) { 442
405 if (intervalLength(pos, ptr.end()) < amount) { 443 inline word* SegmentBuilder::allocate(SegmentWordCount amount) {
444 if (intervalLength(pos, ptr.end(), MAX_SEGMENT_WORDS) < amount) {
406 // Not enough space in the segment for this allocation. 445 // Not enough space in the segment for this allocation.
407 return nullptr; 446 return nullptr;
408 } else { 447 } else {
409 // Success. 448 // Success.
410 word* result = pos; 449 word* result = pos;
415 454
416 inline void SegmentBuilder::checkWritable() { 455 inline void SegmentBuilder::checkWritable() {
417 if (KJ_UNLIKELY(readOnly)) throwNotWritable(); 456 if (KJ_UNLIKELY(readOnly)) throwNotWritable();
418 } 457 }
419 458
420 inline word* SegmentBuilder::getPtrUnchecked(WordCount offset) { 459 inline word* SegmentBuilder::getPtrUnchecked(SegmentWordCount offset) {
421 return const_cast<word*>(ptr.begin() + offset); 460 return const_cast<word*>(ptr.begin() + offset);
422 } 461 }
423 462
424 inline BuilderArena* SegmentBuilder::getArena() { 463 inline BuilderArena* SegmentBuilder::getArena() {
425 // Down-cast safe because SegmentBuilder's constructor always initializes its SegmentReader base 464 // Down-cast safe because SegmentBuilder's constructor always initializes its SegmentReader base
430 inline kj::ArrayPtr<const word> SegmentBuilder::currentlyAllocated() { 469 inline kj::ArrayPtr<const word> SegmentBuilder::currentlyAllocated() {
431 return kj::arrayPtr(ptr.begin(), pos - ptr.begin()); 470 return kj::arrayPtr(ptr.begin(), pos - ptr.begin());
432 } 471 }
433 472
434 inline void SegmentBuilder::reset() { 473 inline void SegmentBuilder::reset() {
435 word* start = getPtrUnchecked(0 * WORDS); 474 word* start = getPtrUnchecked(ZERO * WORDS);
436 memset(start, 0, (pos - start) * sizeof(word)); 475 memset(start, 0, (pos - start) * sizeof(word));
437 pos = start; 476 pos = start;
438 } 477 }
439 478
440 inline void SegmentBuilder::tryTruncate(word* from, word* to) { 479 inline void SegmentBuilder::tryTruncate(word* from, word* to) {