blob: dd936baaeddaacd59df94852bf6987705a9d6516 [file] [log] [blame]
/*
* 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