summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h')
-rw-r--r--chromium/third_party/skia/experimental/PdfViewer/src/SkTDStackNester.h214
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