OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2006 The Android Open Source Project | 2 * Copyright 2006 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkGlyphCache.h" | 8 #include "SkGlyphCache.h" |
9 #include "SkGlyphCache_Globals.h" | 9 #include "SkGlyphCache_Globals.h" |
10 #include "SkGraphics.h" | 10 #include "SkGraphics.h" |
(...skipping 25 matching lines...) Expand all Loading... |
36 #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphC
ount) | 36 #define kMinAllocAmount ((sizeof(SkGlyph) + kMinGlyphImageSize) * kMinGlyphC
ount) |
37 | 37 |
38 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca
lerContext* ctx) | 38 SkGlyphCache::SkGlyphCache(SkTypeface* typeface, const SkDescriptor* desc, SkSca
lerContext* ctx) |
39 : fDesc(desc->copy()) | 39 : fDesc(desc->copy()) |
40 , fScalerContext(ctx) | 40 , fScalerContext(ctx) |
41 , fGlyphAlloc(kMinAllocAmount) { | 41 , fGlyphAlloc(kMinAllocAmount) { |
42 SkASSERT(typeface); | 42 SkASSERT(typeface); |
43 SkASSERT(desc); | 43 SkASSERT(desc); |
44 SkASSERT(ctx); | 44 SkASSERT(ctx); |
45 | 45 |
46 fPrev = fNext = NULL; | 46 fPrev = fNext = nullptr; |
47 | 47 |
48 fScalerContext->getFontMetrics(&fFontMetrics); | 48 fScalerContext->getFontMetrics(&fFontMetrics); |
49 | 49 |
50 fMemoryUsed = sizeof(*this); | 50 fMemoryUsed = sizeof(*this); |
51 | 51 |
52 fAuxProcList = NULL; | 52 fAuxProcList = nullptr; |
53 } | 53 } |
54 | 54 |
55 SkGlyphCache::~SkGlyphCache() { | 55 SkGlyphCache::~SkGlyphCache() { |
56 fGlyphMap.foreach ([](SkGlyph* g) { delete g->fPath; }); | 56 fGlyphMap.foreach ([](SkGlyph* g) { delete g->fPath; }); |
57 SkDescriptor::Free(fDesc); | 57 SkDescriptor::Free(fDesc); |
58 delete fScalerContext; | 58 delete fScalerContext; |
59 this->invokeAndRemoveAuxProcs(); | 59 this->invokeAndRemoveAuxProcs(); |
60 } | 60 } |
61 | 61 |
62 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(PackedUnicharID packed
UnicharID) { | 62 SkGlyphCache::CharGlyphRec* SkGlyphCache::getCharGlyphRec(PackedUnicharID packed
UnicharID) { |
63 if (NULL == fPackedUnicharIDToPackedGlyphID.get()) { | 63 if (nullptr == fPackedUnicharIDToPackedGlyphID.get()) { |
64 // Allocate the array. | 64 // Allocate the array. |
65 fPackedUnicharIDToPackedGlyphID.reset(kHashCount); | 65 fPackedUnicharIDToPackedGlyphID.reset(kHashCount); |
66 // Initialize array to map character and position with the impossible gl
yph ID. This | 66 // Initialize array to map character and position with the impossible gl
yph ID. This |
67 // represents no mapping. | 67 // represents no mapping. |
68 for (int i = 0; i <kHashCount; ++i) { | 68 for (int i = 0; i <kHashCount; ++i) { |
69 fPackedUnicharIDToPackedGlyphID[i].fPackedUnicharID = SkGlyph::kImpo
ssibleID; | 69 fPackedUnicharIDToPackedGlyphID[i].fPackedUnicharID = SkGlyph::kImpo
ssibleID; |
70 fPackedUnicharIDToPackedGlyphID[i].fPackedGlyphID = 0; | 70 fPackedUnicharIDToPackedGlyphID[i].fPackedGlyphID = 0; |
71 } | 71 } |
72 } | 72 } |
73 | 73 |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
154 rec->fPackedGlyphID = combinedID; | 154 rec->fPackedGlyphID = combinedID; |
155 return this->lookupByPackedGlyphID(combinedID, type); | 155 return this->lookupByPackedGlyphID(combinedID, type); |
156 } else { | 156 } else { |
157 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type); | 157 return this->lookupByPackedGlyphID(rec->fPackedGlyphID, type); |
158 } | 158 } |
159 } | 159 } |
160 | 160 |
161 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(PackedGlyphID packedGlyphID, Metric
sType type) { | 161 SkGlyph* SkGlyphCache::lookupByPackedGlyphID(PackedGlyphID packedGlyphID, Metric
sType type) { |
162 SkGlyph* glyph = fGlyphMap.find(packedGlyphID); | 162 SkGlyph* glyph = fGlyphMap.find(packedGlyphID); |
163 | 163 |
164 if (NULL == glyph) { | 164 if (nullptr == glyph) { |
165 glyph = this->allocateNewGlyph(packedGlyphID, type); | 165 glyph = this->allocateNewGlyph(packedGlyphID, type); |
166 } else { | 166 } else { |
167 if (type == kFull_MetricsType && glyph->isJustAdvance()) { | 167 if (type == kFull_MetricsType && glyph->isJustAdvance()) { |
168 fScalerContext->getMetrics(glyph); | 168 fScalerContext->getMetrics(glyph); |
169 } | 169 } |
170 } | 170 } |
171 return glyph; | 171 return glyph; |
172 } | 172 } |
173 | 173 |
174 SkGlyph* SkGlyphCache::allocateNewGlyph(PackedGlyphID packedGlyphID, MetricsType
mtype) { | 174 SkGlyph* SkGlyphCache::allocateNewGlyph(PackedGlyphID packedGlyphID, MetricsType
mtype) { |
(...skipping 12 matching lines...) Expand all Loading... |
187 SkASSERT(kFull_MetricsType == mtype); | 187 SkASSERT(kFull_MetricsType == mtype); |
188 fScalerContext->getMetrics(glyphPtr); | 188 fScalerContext->getMetrics(glyphPtr); |
189 } | 189 } |
190 | 190 |
191 SkASSERT(glyphPtr->fID != SkGlyph::kImpossibleID); | 191 SkASSERT(glyphPtr->fID != SkGlyph::kImpossibleID); |
192 return glyphPtr; | 192 return glyphPtr; |
193 } | 193 } |
194 | 194 |
195 const void* SkGlyphCache::findImage(const SkGlyph& glyph) { | 195 const void* SkGlyphCache::findImage(const SkGlyph& glyph) { |
196 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { | 196 if (glyph.fWidth > 0 && glyph.fWidth < kMaxGlyphWidth) { |
197 if (NULL == glyph.fImage) { | 197 if (nullptr == glyph.fImage) { |
198 size_t size = glyph.computeImageSize(); | 198 size_t size = glyph.computeImageSize(); |
199 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size, | 199 const_cast<SkGlyph&>(glyph).fImage = fGlyphAlloc.alloc(size, |
200 SkChunkAlloc::kReturnNil_AllocFailType); | 200 SkChunkAlloc::kReturnNil_AllocFailType); |
201 // check that alloc() actually succeeded | 201 // check that alloc() actually succeeded |
202 if (glyph.fImage) { | 202 if (glyph.fImage) { |
203 fScalerContext->getImage(glyph); | 203 fScalerContext->getImage(glyph); |
204 // TODO: the scaler may have changed the maskformat during | 204 // TODO: the scaler may have changed the maskformat during |
205 // getImage (e.g. from AA or LCD to BW) which means we may have | 205 // getImage (e.g. from AA or LCD to BW) which means we may have |
206 // overallocated the buffer. Check if the new computedImageSize | 206 // overallocated the buffer. Check if the new computedImageSize |
207 // is smaller, and if so, strink the alloc size in fImageAlloc. | 207 // is smaller, and if so, strink the alloc size in fImageAlloc. |
208 fMemoryUsed += size; | 208 fMemoryUsed += size; |
209 } | 209 } |
210 } | 210 } |
211 } | 211 } |
212 return glyph.fImage; | 212 return glyph.fImage; |
213 } | 213 } |
214 | 214 |
215 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { | 215 const SkPath* SkGlyphCache::findPath(const SkGlyph& glyph) { |
216 if (glyph.fWidth) { | 216 if (glyph.fWidth) { |
217 if (glyph.fPath == NULL) { | 217 if (glyph.fPath == nullptr) { |
218 const_cast<SkGlyph&>(glyph).fPath = new SkPath; | 218 const_cast<SkGlyph&>(glyph).fPath = new SkPath; |
219 fScalerContext->getPath(glyph, glyph.fPath); | 219 fScalerContext->getPath(glyph, glyph.fPath); |
220 fMemoryUsed += sizeof(SkPath) + | 220 fMemoryUsed += sizeof(SkPath) + |
221 glyph.fPath->countPoints() * sizeof(SkPoint); | 221 glyph.fPath->countPoints() * sizeof(SkPoint); |
222 } | 222 } |
223 } | 223 } |
224 return glyph.fPath; | 224 return glyph.fPath; |
225 } | 225 } |
226 | 226 |
227 void SkGlyphCache::dump() const { | 227 void SkGlyphCache::dump() const { |
(...skipping 25 matching lines...) Expand all Loading... |
253 *dataPtr = rec->fData; | 253 *dataPtr = rec->fData; |
254 } | 254 } |
255 return true; | 255 return true; |
256 } | 256 } |
257 rec = rec->fNext; | 257 rec = rec->fNext; |
258 } | 258 } |
259 return false; | 259 return false; |
260 } | 260 } |
261 | 261 |
262 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { | 262 void SkGlyphCache::setAuxProc(void (*proc)(void*), void* data) { |
263 if (proc == NULL) { | 263 if (proc == nullptr) { |
264 return; | 264 return; |
265 } | 265 } |
266 | 266 |
267 AuxProcRec* rec = fAuxProcList; | 267 AuxProcRec* rec = fAuxProcList; |
268 while (rec) { | 268 while (rec) { |
269 if (rec->fProc == proc) { | 269 if (rec->fProc == proc) { |
270 rec->fData = data; | 270 rec->fData = data; |
271 return; | 271 return; |
272 } | 272 } |
273 rec = rec->fNext; | 273 rec = rec->fNext; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
350 SkASSERT(desc); | 350 SkASSERT(desc); |
351 | 351 |
352 SkGlyphCache_Globals& globals = get_globals(); | 352 SkGlyphCache_Globals& globals = get_globals(); |
353 SkGlyphCache* cache; | 353 SkGlyphCache* cache; |
354 | 354 |
355 { | 355 { |
356 AutoAcquire ac(globals.fLock); | 356 AutoAcquire ac(globals.fLock); |
357 | 357 |
358 globals.validate(); | 358 globals.validate(); |
359 | 359 |
360 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fN
ext) { | 360 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache-
>fNext) { |
361 if (cache->fDesc->equals(*desc)) { | 361 if (cache->fDesc->equals(*desc)) { |
362 globals.internalDetachCache(cache); | 362 globals.internalDetachCache(cache); |
363 if (!proc(cache, context)) { | 363 if (!proc(cache, context)) { |
364 globals.internalAttachCacheToHead(cache); | 364 globals.internalAttachCacheToHead(cache); |
365 cache = NULL; | 365 cache = nullptr; |
366 } | 366 } |
367 return cache; | 367 return cache; |
368 } | 368 } |
369 } | 369 } |
370 } | 370 } |
371 | 371 |
372 // Check if we can create a scaler-context before creating the glyphcache. | 372 // Check if we can create a scaler-context before creating the glyphcache. |
373 // If not, we may have exhausted OS/font resources, so try purging the | 373 // If not, we may have exhausted OS/font resources, so try purging the |
374 // cache once and try again. | 374 // cache once and try again. |
375 { | 375 { |
376 // pass true the first time, to notice if the scalercontext failed, | 376 // pass true the first time, to notice if the scalercontext failed, |
377 // so we can try the purge. | 377 // so we can try the purge. |
378 SkScalerContext* ctx = typeface->createScalerContext(desc, true); | 378 SkScalerContext* ctx = typeface->createScalerContext(desc, true); |
379 if (!ctx) { | 379 if (!ctx) { |
380 get_globals().purgeAll(); | 380 get_globals().purgeAll(); |
381 ctx = typeface->createScalerContext(desc, false); | 381 ctx = typeface->createScalerContext(desc, false); |
382 SkASSERT(ctx); | 382 SkASSERT(ctx); |
383 } | 383 } |
384 cache = new SkGlyphCache(typeface, desc, ctx); | 384 cache = new SkGlyphCache(typeface, desc, ctx); |
385 } | 385 } |
386 | 386 |
387 AutoValidate av(cache); | 387 AutoValidate av(cache); |
388 | 388 |
389 if (!proc(cache, context)) { // need to reattach | 389 if (!proc(cache, context)) { // need to reattach |
390 globals.attachCacheToHead(cache); | 390 globals.attachCacheToHead(cache); |
391 cache = NULL; | 391 cache = nullptr; |
392 } | 392 } |
393 return cache; | 393 return cache; |
394 } | 394 } |
395 | 395 |
396 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { | 396 void SkGlyphCache::AttachCache(SkGlyphCache* cache) { |
397 SkASSERT(cache); | 397 SkASSERT(cache); |
398 SkASSERT(cache->fNext == NULL); | 398 SkASSERT(cache->fNext == nullptr); |
399 | 399 |
400 get_globals().attachCacheToHead(cache); | 400 get_globals().attachCacheToHead(cache); |
401 } | 401 } |
402 | 402 |
403 static void dump_visitor(const SkGlyphCache& cache, void* context) { | 403 static void dump_visitor(const SkGlyphCache& cache, void* context) { |
404 int* counter = (int*)context; | 404 int* counter = (int*)context; |
405 int index = *counter; | 405 int index = *counter; |
406 *counter += 1; | 406 *counter += 1; |
407 | 407 |
408 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); | 408 const SkScalerContextRec& rec = cache.getScalerContext()->getRec(); |
(...skipping 15 matching lines...) Expand all Loading... |
424 SkGlyphCache::VisitAll(dump_visitor, &counter); | 424 SkGlyphCache::VisitAll(dump_visitor, &counter); |
425 } | 425 } |
426 | 426 |
427 void SkGlyphCache::VisitAll(Visitor visitor, void* context) { | 427 void SkGlyphCache::VisitAll(Visitor visitor, void* context) { |
428 SkGlyphCache_Globals& globals = get_globals(); | 428 SkGlyphCache_Globals& globals = get_globals(); |
429 AutoAcquire ac(globals.fLock); | 429 AutoAcquire ac(globals.fLock); |
430 SkGlyphCache* cache; | 430 SkGlyphCache* cache; |
431 | 431 |
432 globals.validate(); | 432 globals.validate(); |
433 | 433 |
434 for (cache = globals.internalGetHead(); cache != NULL; cache = cache->fNext)
{ | 434 for (cache = globals.internalGetHead(); cache != nullptr; cache = cache->fNe
xt) { |
435 visitor(*cache, context); | 435 visitor(*cache, context); |
436 } | 436 } |
437 } | 437 } |
438 | 438 |
439 /////////////////////////////////////////////////////////////////////////////// | 439 /////////////////////////////////////////////////////////////////////////////// |
440 | 440 |
441 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { | 441 void SkGlyphCache_Globals::attachCacheToHead(SkGlyphCache* cache) { |
442 AutoAcquire ac(fLock); | 442 AutoAcquire ac(fLock); |
443 | 443 |
444 this->validate(); | 444 this->validate(); |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
482 if (!countNeeded && !bytesNeeded) { | 482 if (!countNeeded && !bytesNeeded) { |
483 return 0; | 483 return 0; |
484 } | 484 } |
485 | 485 |
486 size_t bytesFreed = 0; | 486 size_t bytesFreed = 0; |
487 int countFreed = 0; | 487 int countFreed = 0; |
488 | 488 |
489 // we start at the tail and proceed backwards, as the linklist is in LRU | 489 // we start at the tail and proceed backwards, as the linklist is in LRU |
490 // order, with unimportant entries at the tail. | 490 // order, with unimportant entries at the tail. |
491 SkGlyphCache* cache = this->internalGetTail(); | 491 SkGlyphCache* cache = this->internalGetTail(); |
492 while (cache != NULL && | 492 while (cache != nullptr && |
493 (bytesFreed < bytesNeeded || countFreed < countNeeded)) { | 493 (bytesFreed < bytesNeeded || countFreed < countNeeded)) { |
494 SkGlyphCache* prev = cache->fPrev; | 494 SkGlyphCache* prev = cache->fPrev; |
495 bytesFreed += cache->fMemoryUsed; | 495 bytesFreed += cache->fMemoryUsed; |
496 countFreed += 1; | 496 countFreed += 1; |
497 | 497 |
498 this->internalDetachCache(cache); | 498 this->internalDetachCache(cache); |
499 delete cache; | 499 delete cache; |
500 cache = prev; | 500 cache = prev; |
501 } | 501 } |
502 | 502 |
503 this->validate(); | 503 this->validate(); |
504 | 504 |
505 #ifdef SPEW_PURGE_STATUS | 505 #ifdef SPEW_PURGE_STATUS |
506 if (countFreed) { | 506 if (countFreed) { |
507 SkDebugf("purging %dK from font cache [%d entries]\n", | 507 SkDebugf("purging %dK from font cache [%d entries]\n", |
508 (int)(bytesFreed >> 10), countFreed); | 508 (int)(bytesFreed >> 10), countFreed); |
509 } | 509 } |
510 #endif | 510 #endif |
511 | 511 |
512 return bytesFreed; | 512 return bytesFreed; |
513 } | 513 } |
514 | 514 |
515 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { | 515 void SkGlyphCache_Globals::internalAttachCacheToHead(SkGlyphCache* cache) { |
516 SkASSERT(NULL == cache->fPrev && NULL == cache->fNext); | 516 SkASSERT(nullptr == cache->fPrev && nullptr == cache->fNext); |
517 if (fHead) { | 517 if (fHead) { |
518 fHead->fPrev = cache; | 518 fHead->fPrev = cache; |
519 cache->fNext = fHead; | 519 cache->fNext = fHead; |
520 } | 520 } |
521 fHead = cache; | 521 fHead = cache; |
522 | 522 |
523 fCacheCount += 1; | 523 fCacheCount += 1; |
524 fTotalMemoryUsed += cache->fMemoryUsed; | 524 fTotalMemoryUsed += cache->fMemoryUsed; |
525 } | 525 } |
526 | 526 |
527 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { | 527 void SkGlyphCache_Globals::internalDetachCache(SkGlyphCache* cache) { |
528 SkASSERT(fCacheCount > 0); | 528 SkASSERT(fCacheCount > 0); |
529 fCacheCount -= 1; | 529 fCacheCount -= 1; |
530 fTotalMemoryUsed -= cache->fMemoryUsed; | 530 fTotalMemoryUsed -= cache->fMemoryUsed; |
531 | 531 |
532 if (cache->fPrev) { | 532 if (cache->fPrev) { |
533 cache->fPrev->fNext = cache->fNext; | 533 cache->fPrev->fNext = cache->fNext; |
534 } else { | 534 } else { |
535 fHead = cache->fNext; | 535 fHead = cache->fNext; |
536 } | 536 } |
537 if (cache->fNext) { | 537 if (cache->fNext) { |
538 cache->fNext->fPrev = cache->fPrev; | 538 cache->fNext->fPrev = cache->fPrev; |
539 } | 539 } |
540 cache->fPrev = cache->fNext = NULL; | 540 cache->fPrev = cache->fNext = nullptr; |
541 } | 541 } |
542 | 542 |
543 /////////////////////////////////////////////////////////////////////////////// | 543 /////////////////////////////////////////////////////////////////////////////// |
544 | 544 |
545 #ifdef SK_DEBUG | 545 #ifdef SK_DEBUG |
546 | 546 |
547 void SkGlyphCache::validate() const { | 547 void SkGlyphCache::validate() const { |
548 #ifdef SK_DEBUG_GLYPH_CACHE | 548 #ifdef SK_DEBUG_GLYPH_CACHE |
549 int count = fGlyphArray.count(); | 549 int count = fGlyphArray.count(); |
550 for (int i = 0; i < count; i++) { | 550 for (int i = 0; i < count; i++) { |
551 const SkGlyph* glyph = &fGlyphArray[i]; | 551 const SkGlyph* glyph = &fGlyphArray[i]; |
552 SkASSERT(glyph); | 552 SkASSERT(glyph); |
553 if (glyph->fImage) { | 553 if (glyph->fImage) { |
554 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); | 554 SkASSERT(fGlyphAlloc.contains(glyph->fImage)); |
555 } | 555 } |
556 } | 556 } |
557 #endif | 557 #endif |
558 } | 558 } |
559 | 559 |
560 void SkGlyphCache_Globals::validate() const { | 560 void SkGlyphCache_Globals::validate() const { |
561 size_t computedBytes = 0; | 561 size_t computedBytes = 0; |
562 int computedCount = 0; | 562 int computedCount = 0; |
563 | 563 |
564 const SkGlyphCache* head = fHead; | 564 const SkGlyphCache* head = fHead; |
565 while (head != NULL) { | 565 while (head != nullptr) { |
566 computedBytes += head->fMemoryUsed; | 566 computedBytes += head->fMemoryUsed; |
567 computedCount += 1; | 567 computedCount += 1; |
568 head = head->fNext; | 568 head = head->fNext; |
569 } | 569 } |
570 | 570 |
571 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d"
, fCacheCount, | 571 SkASSERTF(fCacheCount == computedCount, "fCacheCount: %d, computedCount: %d"
, fCacheCount, |
572 computedCount); | 572 computedCount); |
573 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computed
Bytes: %d", | 573 SkASSERTF(fTotalMemoryUsed == computedBytes, "fTotalMemoryUsed: %d, computed
Bytes: %d", |
574 fTotalMemoryUsed, computedBytes); | 574 fTotalMemoryUsed, computedBytes); |
575 } | 575 } |
(...skipping 30 matching lines...) Expand all Loading... |
606 } | 606 } |
607 | 607 |
608 void SkGraphics::PurgeFontCache() { | 608 void SkGraphics::PurgeFontCache() { |
609 get_globals().purgeAll(); | 609 get_globals().purgeAll(); |
610 SkTypefaceCache::PurgeAll(); | 610 SkTypefaceCache::PurgeAll(); |
611 } | 611 } |
612 | 612 |
613 // TODO(herb): clean up TLS apis. | 613 // TODO(herb): clean up TLS apis. |
614 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; } | 614 size_t SkGraphics::GetTLSFontCacheLimit() { return 0; } |
615 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { } | 615 void SkGraphics::SetTLSFontCacheLimit(size_t bytes) { } |
OLD | NEW |