diff options
Diffstat (limited to 'chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h')
-rw-r--r-- | chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h b/chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h new file mode 100644 index 00000000000..dd936baaedd --- /dev/null +++ b/chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h @@ -0,0 +1,214 @@ +/* + * 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 <typename T> 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 |