/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTDStackNester_DEFINED #define SkTDStackNester_DEFINED #include "SkTypes.h" #include "SkPdfReporter.h" // Adobe limits it to 28. Allow deeper nesting in case a file does not quite meet the // spec. 256 should be more than enough. #define MAX_NESTING 256 /** \class SkTDStackNester * * Specialized version of SkTDStack which allows a stack of stacks. * FIXME (scroggo): Could this be a subclass of SkTDStack? Could it have-a SkTDStack? * The difference between SkTDStackNester and SkTDStack is that: * - SkTDStackNester uses new/delete to manage initializations * FIXME (scroggo): Why use new rather than malloc? * - Supports nest/unnest which simulates a stack of stack. unnest will pop all the * objects pushed since the last nest * - kSlotCount is 64, instead of 8. * FIXME (scroggo): How did we arrive at this number? */ template class SkTDStackNester : SkNoncopyable { public: SkTDStackNester() : fCount(0) , fLocalCount(0) , fNestingLevel(0) { fInitialRec.fNext = NULL; fRec = &fInitialRec; SkDEBUGCODE(fTotalCount = 0;) } ~SkTDStackNester() { Rec* rec = fRec; while (rec != &fInitialRec) { Rec* next = rec->fNext; delete rec; rec = next; } } /** * Return the number of objects in the current nesting level. */ int count() const { return fLocalCount; } /** * Whether the current nesting level is empty. */ bool empty() const { return fLocalCount == 0; } /** * The current nesting level. */ int nestingLevel() const { return fNestingLevel; } /** * Analogous to an SkCanvas::save(). When unnest() is called, the state of this SkTDStackNester * will return to its state when nest() was called. * * After a call to nest(), fLocalCount is reset to 0, since the stack is on a new nesting * level. */ void nest() { SkASSERT(fNestingLevel >= 0); if (fNestingLevel < MAX_NESTING) { fNestings[fNestingLevel] = fLocalCount; fLocalCount = 0; } else { // We are are past max nesting levels. We will still continue to work, but we might fail // to properly ignore errors. Ideally it should only mean poor rendering in exceptional // cases. SkPdfReport(kWarning_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue, "Past maximum nesting level", NULL, NULL); } fNestingLevel++; } /** * Analagous to an SkCanvas::restore(). Will revert this stack to the state it was in the last * time nest() was called. It is an error to call unnest() more times than nest() has been * called. */ void unnest() { SkASSERT(fNestingLevel >= 0); if (0 == fNestingLevel) { SkPdfReport(kWarning_SkPdfIssueSeverity, kStackNestingOverflow_SkPdfIssue, "Nesting underflow", NULL, NULL); return; } fNestingLevel--; if (fNestingLevel < MAX_NESTING) { while (fLocalCount > 0) { // FIXME (scroggo): Pass the object? SkPdfReport(kInfo_SkPdfIssueSeverity, kUnusedObject_SkPdfIssue, "Unused object when calling unnest!", NULL, NULL); this->pop(); } fLocalCount = fNestings[fNestingLevel]; } } /** * Add an object to the stack, and return a pointer to it for modification. */ T* push() { SkASSERT(fCount <= kSlotCount); if (fCount == kSlotCount) { Rec* rec = new Rec(); rec->fNext = fRec; fRec = rec; fCount = 0; } SkDEBUGCODE(++fTotalCount;) ++fLocalCount; return &fRec->fSlots[fCount++]; } /** * Add an object to the stack, copied from elem. */ void push(const T& elem) { *this->push() = elem; } /** * Return the top element. */ const T& top() const { SkASSERT(fRec && fCount > 0); return fRec->fSlots[fCount - 1]; } /** * Return the top element. */ T& top() { SkASSERT(fRec && fCount > 0); return fRec->fSlots[fCount - 1]; } /** * Pop an object off the stack (via pop()), and copy its members into elem. */ void pop(T* elem) { if (elem) { *elem = fRec->fSlots[fCount - 1]; } this->pop(); } /** * Pop an object off the stack. It is an error to call pop() more times * than push() has been called in total or since the last call to nest(). */ void pop() { SkASSERT(fCount > 0 && fRec); SkASSERT(fLocalCount > 0); --fLocalCount; SkDEBUGCODE(--fTotalCount;) if (--fCount == 0) { if (fRec != &fInitialRec) { Rec* rec = fRec->fNext; delete fRec; fCount = kSlotCount; fRec = rec; } else { SkASSERT(fTotalCount == 0); } } } private: enum { // Number of objects held per Rec. Storing multiple objects in one Rec // means that we call new less often. kSlotCount = 64 }; struct Rec { Rec* fNext; T fSlots[kSlotCount]; }; // First Rec, requiring no allocation. Rec fInitialRec; // The Rec on top of the stack. Rec* fRec; // Number of objects in fRec. int fCount; // Number of objects in the current nesting level. int fLocalCount; // Array of counts of objects per nesting level. // Only valid for fNestings[0] through fNestings[fNestingLevel-1]. int fNestings[MAX_NESTING]; // Current nesting level. int fNestingLevel; // Total number of objects in this SkTDStackNester. SkDEBUGCODE(int fTotalCount;) // For testing. friend class SkTDStackNesterTester; }; #endif // SkTDStackNester_DEFINED