Apply clang-format to Redwood source.
This commit is contained in:
parent
93e9360d2f
commit
dda0993d16
|
@ -32,10 +32,10 @@ static inline int commonPrefixLength(uint8_t const* ap, uint8_t const* bp, int c
|
|||
int i = 0;
|
||||
const int wordEnd = cl - sizeof(Word) + 1;
|
||||
|
||||
for(; i < wordEnd; i += sizeof(Word)) {
|
||||
Word a = *(Word *)ap;
|
||||
Word b = *(Word *)bp;
|
||||
if(a != b) {
|
||||
for (; i < wordEnd; i += sizeof(Word)) {
|
||||
Word a = *(Word*)ap;
|
||||
Word b = *(Word*)bp;
|
||||
if (a != b) {
|
||||
return i + ctzll(a ^ b) / 8;
|
||||
}
|
||||
ap += sizeof(Word);
|
||||
|
@ -59,7 +59,8 @@ static int commonPrefixLength(StringRef a, StringRef b) {
|
|||
// This appears to be the fastest version
|
||||
static int lessOrEqualPowerOfTwo(int n) {
|
||||
int p;
|
||||
for (p = 1; p+p <= n; p+=p);
|
||||
for (p = 1; p + p <= n; p += p)
|
||||
;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
@ -91,16 +92,14 @@ static int perfectSubtreeSplitPoint(int subtree_size) {
|
|||
}
|
||||
|
||||
static int perfectSubtreeSplitPointCached(int subtree_size) {
|
||||
static uint16_t *points = nullptr;
|
||||
static uint16_t* points = nullptr;
|
||||
static const int max = 500;
|
||||
if(points == nullptr) {
|
||||
if (points == nullptr) {
|
||||
points = new uint16_t[max];
|
||||
for(int i = 0; i < max; ++i)
|
||||
points[i] = perfectSubtreeSplitPoint(i);
|
||||
for (int i = 0; i < max; ++i) points[i] = perfectSubtreeSplitPoint(i);
|
||||
}
|
||||
|
||||
if(subtree_size < max)
|
||||
return points[subtree_size];
|
||||
if (subtree_size < max) return points[subtree_size];
|
||||
return perfectSubtreeSplitPoint(subtree_size);
|
||||
}
|
||||
|
||||
|
@ -147,7 +146,7 @@ static int perfectSubtreeSplitPointCached(int subtree_size) {
|
|||
// // Retrieves the previously stored boolean
|
||||
// bool getPrefixSource() const;
|
||||
//
|
||||
#pragma pack(push,1)
|
||||
#pragma pack(push, 1)
|
||||
template <typename T, typename DeltaT = typename T::Delta>
|
||||
struct DeltaTree {
|
||||
struct Node {
|
||||
|
@ -162,44 +161,34 @@ struct DeltaTree {
|
|||
} smallOffsets;
|
||||
};
|
||||
|
||||
static int headerSize(bool large) {
|
||||
return large ? sizeof(largeOffsets) : sizeof(smallOffsets);
|
||||
}
|
||||
static int headerSize(bool large) { return large ? sizeof(largeOffsets) : sizeof(smallOffsets); }
|
||||
|
||||
inline DeltaT & delta(bool large) {
|
||||
return large ? *(DeltaT *)(&largeOffsets + 1) : *(DeltaT *)(&smallOffsets + 1);
|
||||
inline DeltaT& delta(bool large) {
|
||||
return large ? *(DeltaT*)(&largeOffsets + 1) : *(DeltaT*)(&smallOffsets + 1);
|
||||
};
|
||||
|
||||
inline const DeltaT & delta(bool large) const {
|
||||
return large ? *(const DeltaT *)(&largeOffsets + 1) : *(const DeltaT *)(&smallOffsets + 1);
|
||||
inline const DeltaT& delta(bool large) const {
|
||||
return large ? *(const DeltaT*)(&largeOffsets + 1) : *(const DeltaT*)(&smallOffsets + 1);
|
||||
};
|
||||
|
||||
Node * resolvePointer(int offset) const {
|
||||
return offset == 0 ? nullptr : (Node *)((uint8_t *)this + offset);
|
||||
}
|
||||
Node* resolvePointer(int offset) const { return offset == 0 ? nullptr : (Node*)((uint8_t*)this + offset); }
|
||||
|
||||
Node * rightChild(bool large) const {
|
||||
return resolvePointer(large ? largeOffsets.right : smallOffsets.right);
|
||||
}
|
||||
Node* rightChild(bool large) const { return resolvePointer(large ? largeOffsets.right : smallOffsets.right); }
|
||||
|
||||
Node * leftChild(bool large) const {
|
||||
return resolvePointer(large ? largeOffsets.left : smallOffsets.left);
|
||||
}
|
||||
Node* leftChild(bool large) const { return resolvePointer(large ? largeOffsets.left : smallOffsets.left); }
|
||||
|
||||
void setRightChildOffset(bool large, int offset) {
|
||||
if(large) {
|
||||
if (large) {
|
||||
largeOffsets.right = offset;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
smallOffsets.right = offset;
|
||||
}
|
||||
}
|
||||
|
||||
void setLeftChildOffset(bool large, int offset) {
|
||||
if(large) {
|
||||
if (large) {
|
||||
largeOffsets.left = offset;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
smallOffsets.left = offset;
|
||||
}
|
||||
}
|
||||
|
@ -216,87 +205,66 @@ struct DeltaTree {
|
|||
uint16_t numItems; // Number of items in the tree.
|
||||
uint32_t nodeBytesUsed; // Bytes used by nodes (everything after the tree header)
|
||||
uint32_t nodeBytesFree; // Bytes left at end of tree to expand into
|
||||
uint32_t nodeBytesDeleted; // Delta bytes deleted from tree. Note that some of these bytes could be borrowed by descendents.
|
||||
uint32_t nodeBytesDeleted; // Delta bytes deleted from tree. Note that some of these bytes could be borrowed by
|
||||
// descendents.
|
||||
uint8_t initialHeight; // Height of tree as originally built
|
||||
uint8_t maxHeight; // Maximum height of tree after any insertion. Value of 0 means no insertions done.
|
||||
bool largeNodes; // Node size, can be calculated as capacity > SmallSizeLimit but it will be used a lot
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
inline Node & root() {
|
||||
return *(Node *)(this + 1);
|
||||
}
|
||||
inline Node& root() { return *(Node*)(this + 1); }
|
||||
|
||||
inline const Node & root() const {
|
||||
return *(const Node *)(this + 1);
|
||||
}
|
||||
inline const Node& root() const { return *(const Node*)(this + 1); }
|
||||
|
||||
int size() const {
|
||||
return sizeof(DeltaTree) + nodeBytesUsed;
|
||||
}
|
||||
int size() const { return sizeof(DeltaTree) + nodeBytesUsed; }
|
||||
|
||||
int capacity() const {
|
||||
return size() + nodeBytesFree;
|
||||
}
|
||||
int capacity() const { return size() + nodeBytesFree; }
|
||||
|
||||
inline Node & newNode() {
|
||||
return *(Node *)((uint8_t *)this + size());
|
||||
}
|
||||
inline Node& newNode() { return *(Node*)((uint8_t*)this + size()); }
|
||||
|
||||
public:
|
||||
// Get count of total overhead bytes (everything but the user-formatted Delta) for a tree given size n
|
||||
static int emptyTreeSize() {
|
||||
return sizeof(DeltaTree);
|
||||
}
|
||||
static int emptyTreeSize() { return sizeof(DeltaTree); }
|
||||
|
||||
struct DecodedNode {
|
||||
DecodedNode() {}
|
||||
|
||||
// construct root node
|
||||
DecodedNode(Node *raw, const T *prev, const T *next, Arena &arena, bool large)
|
||||
: raw(raw), parent(nullptr), otherAncestor(nullptr), leftChild(nullptr), rightChild(nullptr), prev(prev), next(next),
|
||||
item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)),
|
||||
large(large)
|
||||
{
|
||||
//printf("DecodedNode1 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str());
|
||||
DecodedNode(Node* raw, const T* prev, const T* next, Arena& arena, bool large)
|
||||
: raw(raw), parent(nullptr), otherAncestor(nullptr), leftChild(nullptr), rightChild(nullptr), prev(prev),
|
||||
next(next), item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)),
|
||||
large(large) {
|
||||
// printf("DecodedNode1 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str());
|
||||
}
|
||||
|
||||
// Construct non-root node
|
||||
// wentLeft indicates that we've gone left to get to the raw node.
|
||||
DecodedNode(Node *raw, DecodedNode *parent, bool wentLeft, Arena &arena)
|
||||
: parent(parent), large(parent->large), otherAncestor(wentLeft ? parent->getPrevAncestor() : parent->getNextAncestor()),
|
||||
prev(wentLeft ? parent->prev : &parent->item),
|
||||
next(wentLeft ? &parent->item : parent->next),
|
||||
leftChild(nullptr), rightChild(nullptr),
|
||||
raw(raw), item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena))
|
||||
{
|
||||
//printf("DecodedNode2 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str());
|
||||
DecodedNode(Node* raw, DecodedNode* parent, bool wentLeft, Arena& arena)
|
||||
: parent(parent), large(parent->large),
|
||||
otherAncestor(wentLeft ? parent->getPrevAncestor() : parent->getNextAncestor()),
|
||||
prev(wentLeft ? parent->prev : &parent->item), next(wentLeft ? &parent->item : parent->next),
|
||||
leftChild(nullptr), rightChild(nullptr), raw(raw),
|
||||
item(raw->delta(large).apply(raw->delta(large).getPrefixSource() ? *prev : *next, arena)) {
|
||||
// printf("DecodedNode2 raw=%p delta=%s\n", raw, raw->delta(large).toString().c_str());
|
||||
}
|
||||
|
||||
// Returns true if otherAncestor is the previous ("greatest lesser") ancestor
|
||||
bool otherAncestorPrev() const {
|
||||
return parent && parent->leftChild == this;
|
||||
}
|
||||
bool otherAncestorPrev() const { return parent && parent->leftChild == this; }
|
||||
|
||||
// Returns true if otherAncestor is the next ("least greator") ancestor
|
||||
bool otherAncestorNext() const {
|
||||
return parent && parent->rightChild == this;
|
||||
}
|
||||
bool otherAncestorNext() const { return parent && parent->rightChild == this; }
|
||||
|
||||
DecodedNode * getPrevAncestor() const {
|
||||
return otherAncestorPrev() ? otherAncestor : parent;
|
||||
}
|
||||
DecodedNode* getPrevAncestor() const { return otherAncestorPrev() ? otherAncestor : parent; }
|
||||
|
||||
DecodedNode * getNextAncestor() const {
|
||||
return otherAncestorNext() ? otherAncestor : parent;
|
||||
}
|
||||
DecodedNode* getNextAncestor() const { return otherAncestorNext() ? otherAncestor : parent; }
|
||||
|
||||
DecodedNode * jumpUpNext(DecodedNode *root, bool &othersChild) const {
|
||||
if(parent != nullptr) {
|
||||
if(parent->rightChild == this) {
|
||||
DecodedNode* jumpUpNext(DecodedNode* root, bool& othersChild) const {
|
||||
if (parent != nullptr) {
|
||||
if (parent->rightChild == this) {
|
||||
return otherAncestor;
|
||||
}
|
||||
if(otherAncestor != nullptr) {
|
||||
if (otherAncestor != nullptr) {
|
||||
othersChild = true;
|
||||
return otherAncestor->rightChild;
|
||||
}
|
||||
|
@ -304,12 +272,12 @@ public:
|
|||
return parent;
|
||||
}
|
||||
|
||||
DecodedNode * jumpUpPrev(DecodedNode *root, bool &othersChild) const {
|
||||
if(parent != nullptr) {
|
||||
if(parent->leftChild == this) {
|
||||
DecodedNode* jumpUpPrev(DecodedNode* root, bool& othersChild) const {
|
||||
if (parent != nullptr) {
|
||||
if (parent->leftChild == this) {
|
||||
return otherAncestor;
|
||||
}
|
||||
if(otherAncestor != nullptr) {
|
||||
if (otherAncestor != nullptr) {
|
||||
othersChild = true;
|
||||
return otherAncestor->leftChild;
|
||||
}
|
||||
|
@ -317,62 +285,56 @@ public:
|
|||
return parent;
|
||||
}
|
||||
|
||||
DecodedNode * jumpNext(DecodedNode *root) const {
|
||||
if(otherAncestorNext()) {
|
||||
DecodedNode* jumpNext(DecodedNode* root) const {
|
||||
if (otherAncestorNext()) {
|
||||
return (otherAncestor != nullptr) ? otherAncestor : rightChild;
|
||||
}
|
||||
else {
|
||||
if(this == root) {
|
||||
} else {
|
||||
if (this == root) {
|
||||
return rightChild;
|
||||
}
|
||||
return (otherAncestor != nullptr) ? otherAncestor->rightChild : root;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedNode * jumpPrev(DecodedNode *root) const {
|
||||
if(otherAncestorPrev()) {
|
||||
DecodedNode* jumpPrev(DecodedNode* root) const {
|
||||
if (otherAncestorPrev()) {
|
||||
return (otherAncestor != nullptr) ? otherAncestor : leftChild;
|
||||
}
|
||||
else {
|
||||
if(this == root) {
|
||||
} else {
|
||||
if (this == root) {
|
||||
return leftChild;
|
||||
}
|
||||
return (otherAncestor != nullptr) ? otherAncestor->leftChild : root;
|
||||
}
|
||||
}
|
||||
|
||||
void setDeleted(bool deleted) {
|
||||
raw->delta(large).setDeleted(deleted);
|
||||
}
|
||||
void setDeleted(bool deleted) { raw->delta(large).setDeleted(deleted); }
|
||||
|
||||
bool isDeleted() const {
|
||||
return raw->delta(large).getDeleted();
|
||||
}
|
||||
bool isDeleted() const { return raw->delta(large).getDeleted(); }
|
||||
|
||||
bool large; // Node size
|
||||
Node *raw;
|
||||
DecodedNode *parent;
|
||||
DecodedNode *otherAncestor;
|
||||
DecodedNode *leftChild;
|
||||
DecodedNode *rightChild;
|
||||
const T *prev; // greatest ancestor to the left, or tree lower bound
|
||||
const T *next; // least ancestor to the right, or tree upper bound
|
||||
Node* raw;
|
||||
DecodedNode* parent;
|
||||
DecodedNode* otherAncestor;
|
||||
DecodedNode* leftChild;
|
||||
DecodedNode* rightChild;
|
||||
const T* prev; // greatest ancestor to the left, or tree lower bound
|
||||
const T* next; // least ancestor to the right, or tree upper bound
|
||||
T item;
|
||||
|
||||
DecodedNode *getRightChild(Arena &arena) {
|
||||
if(rightChild == nullptr) {
|
||||
Node *n = raw->rightChild(large);
|
||||
if(n != nullptr) {
|
||||
DecodedNode* getRightChild(Arena& arena) {
|
||||
if (rightChild == nullptr) {
|
||||
Node* n = raw->rightChild(large);
|
||||
if (n != nullptr) {
|
||||
rightChild = new (arena) DecodedNode(n, this, false, arena);
|
||||
}
|
||||
}
|
||||
return rightChild;
|
||||
}
|
||||
|
||||
DecodedNode *getLeftChild(Arena &arena) {
|
||||
if(leftChild == nullptr) {
|
||||
Node *n = raw->leftChild(large);
|
||||
if(n != nullptr) {
|
||||
DecodedNode* getLeftChild(Arena& arena) {
|
||||
if (leftChild == nullptr) {
|
||||
Node* n = raw->leftChild(large);
|
||||
if (n != nullptr) {
|
||||
leftChild = new (arena) DecodedNode(n, this, true, arena);
|
||||
}
|
||||
}
|
||||
|
@ -389,75 +351,69 @@ public:
|
|||
struct Mirror : FastAllocated<Mirror> {
|
||||
friend class Cursor;
|
||||
|
||||
Mirror(const void *treePtr = nullptr, const T *lowerBound = nullptr, const T *upperBound = nullptr)
|
||||
: tree((DeltaTree *)treePtr), lower(lowerBound), upper(upperBound)
|
||||
{
|
||||
// TODO: Remove these copies into arena and require users of Mirror to keep prev and next alive during its lifetime
|
||||
lower = new(arena) T(arena, *lower);
|
||||
upper = new(arena) T(arena, *upper);
|
||||
Mirror(const void* treePtr = nullptr, const T* lowerBound = nullptr, const T* upperBound = nullptr)
|
||||
: tree((DeltaTree*)treePtr), lower(lowerBound), upper(upperBound) {
|
||||
// TODO: Remove these copies into arena and require users of Mirror to keep prev and next alive during its
|
||||
// lifetime
|
||||
lower = new (arena) T(arena, *lower);
|
||||
upper = new (arena) T(arena, *upper);
|
||||
|
||||
root = (tree->nodeBytesUsed == 0) ? nullptr : new (arena) DecodedNode(&tree->root(), lower, upper, arena, tree->largeNodes);
|
||||
root = (tree->nodeBytesUsed == 0) ? nullptr
|
||||
: new (arena)
|
||||
DecodedNode(&tree->root(), lower, upper, arena, tree->largeNodes);
|
||||
}
|
||||
|
||||
const T *lowerBound() const {
|
||||
return lower;
|
||||
}
|
||||
const T* lowerBound() const { return lower; }
|
||||
|
||||
const T *upperBound() const {
|
||||
return upper;
|
||||
}
|
||||
const T* upperBound() const { return upper; }
|
||||
|
||||
private:
|
||||
private:
|
||||
Arena arena;
|
||||
DeltaTree *tree;
|
||||
DecodedNode *root;
|
||||
const T *lower;
|
||||
const T *upper;
|
||||
public:
|
||||
DeltaTree* tree;
|
||||
DecodedNode* root;
|
||||
const T* lower;
|
||||
const T* upper;
|
||||
|
||||
Cursor getCursor() {
|
||||
return Cursor(this);
|
||||
}
|
||||
public:
|
||||
Cursor getCursor() { return Cursor(this); }
|
||||
|
||||
// Try to insert k into the DeltaTree, updating byte counts and initialHeight if they
|
||||
// have changed (they won't if k already exists in the tree but was deleted).
|
||||
// Returns true if successful, false if k does not fit in the space available
|
||||
// or if k is already in the tree (and was not already deleted).
|
||||
bool insert(const T &k, int skipLen = 0, int maxHeightAllowed = std::numeric_limits<int>::max()) {
|
||||
bool insert(const T& k, int skipLen = 0, int maxHeightAllowed = std::numeric_limits<int>::max()) {
|
||||
int height = 1;
|
||||
DecodedNode *n = root;
|
||||
DecodedNode* n = root;
|
||||
bool addLeftChild = false;
|
||||
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
int cmp = k.compare(n->item, skipLen);
|
||||
|
||||
if(cmp >= 0) {
|
||||
if (cmp >= 0) {
|
||||
// If we found an item identical to k then if it is deleted, undeleted it,
|
||||
// otherwise fail
|
||||
if(cmp == 0) {
|
||||
auto &d = n->raw->delta(tree->largeNodes);
|
||||
if(d.getDeleted()) {
|
||||
if (cmp == 0) {
|
||||
auto& d = n->raw->delta(tree->largeNodes);
|
||||
if (d.getDeleted()) {
|
||||
d.setDeleted(false);
|
||||
++tree->numItems;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
DecodedNode *right = n->getRightChild(arena);
|
||||
DecodedNode* right = n->getRightChild(arena);
|
||||
|
||||
if(right == nullptr) {
|
||||
if (right == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
n = right;
|
||||
}
|
||||
else {
|
||||
DecodedNode *left = n->getLeftChild(arena);
|
||||
} else {
|
||||
DecodedNode* left = n->getLeftChild(arena);
|
||||
|
||||
if(left == nullptr) {
|
||||
if (left == nullptr) {
|
||||
addLeftChild = true;
|
||||
break;
|
||||
}
|
||||
|
@ -467,14 +423,14 @@ public:
|
|||
++height;
|
||||
}
|
||||
|
||||
if(height > maxHeightAllowed) {
|
||||
if (height > maxHeightAllowed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Insert k as the left or right child of n, depending on the value of addLeftChild
|
||||
// First, see if it will fit.
|
||||
const T *prev = addLeftChild ? n->prev : &n->item;
|
||||
const T *next = addLeftChild ? &n->item : n->next;
|
||||
const T* prev = addLeftChild ? n->prev : &n->item;
|
||||
const T* next = addLeftChild ? &n->item : n->next;
|
||||
|
||||
int common = prev->getCommonPrefixLen(*next, skipLen);
|
||||
int commonWithPrev = k.getCommonPrefixLen(*prev, common);
|
||||
|
@ -482,26 +438,25 @@ public:
|
|||
bool basePrev = commonWithPrev >= commonWithNext;
|
||||
|
||||
int commonPrefix = basePrev ? commonWithPrev : commonWithNext;
|
||||
const T *base = basePrev ? prev : next;
|
||||
const T* base = basePrev ? prev : next;
|
||||
|
||||
int deltaSize = k.deltaSize(*base, commonPrefix, false);
|
||||
int nodeSpace = deltaSize + Node::headerSize(tree->largeNodes);
|
||||
if(nodeSpace > tree->nodeBytesFree) {
|
||||
if (nodeSpace > tree->nodeBytesFree) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DecodedNode *newNode = new (arena) DecodedNode();
|
||||
Node *raw = &tree->newNode();
|
||||
DecodedNode* newNode = new (arena) DecodedNode();
|
||||
Node* raw = &tree->newNode();
|
||||
raw->setLeftChildOffset(tree->largeNodes, 0);
|
||||
raw->setRightChildOffset(tree->largeNodes, 0);
|
||||
int newOffset = (uint8_t *)raw - (uint8_t *)n->raw;
|
||||
//printf("Inserting %s at offset %d\n", k.toString().c_str(), newOffset);
|
||||
int newOffset = (uint8_t*)raw - (uint8_t*)n->raw;
|
||||
// printf("Inserting %s at offset %d\n", k.toString().c_str(), newOffset);
|
||||
|
||||
if(addLeftChild) {
|
||||
if (addLeftChild) {
|
||||
n->leftChild = newNode;
|
||||
n->raw->setLeftChildOffset(tree->largeNodes, newOffset);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
n->rightChild = newNode;
|
||||
n->raw->setRightChildOffset(tree->largeNodes, newOffset);
|
||||
}
|
||||
|
@ -518,7 +473,8 @@ public:
|
|||
ASSERT(deltaSize == k.writeDelta(raw->delta(tree->largeNodes), *base, commonPrefix));
|
||||
raw->delta(tree->largeNodes).setPrefixSource(basePrev);
|
||||
|
||||
// Initialize node's item from the delta (instead of copying into arena) to avoid unnecessary arena space usage
|
||||
// Initialize node's item from the delta (instead of copying into arena) to avoid unnecessary arena space
|
||||
// usage
|
||||
newNode->item = raw->delta(tree->largeNodes).apply(*base, arena);
|
||||
|
||||
tree->nodeBytesUsed += nodeSpace;
|
||||
|
@ -526,7 +482,7 @@ public:
|
|||
++tree->numItems;
|
||||
|
||||
// Update max height of the tree if necessary
|
||||
if(height > tree->maxHeight) {
|
||||
if (height > tree->maxHeight) {
|
||||
tree->maxHeight = height;
|
||||
}
|
||||
|
||||
|
@ -534,11 +490,11 @@ public:
|
|||
}
|
||||
|
||||
// Erase k by setting its deleted flag to true. Returns true only if k existed
|
||||
bool erase(const T &k, int skipLen = 0) {
|
||||
bool erase(const T& k, int skipLen = 0) {
|
||||
Cursor c = getCursor();
|
||||
int cmp = c.seek(k);
|
||||
// If exactly k is found
|
||||
if(cmp == 0 && !c.node->isDeleted()) {
|
||||
if (cmp == 0 && !c.node->isDeleted()) {
|
||||
c.erase();
|
||||
return true;
|
||||
}
|
||||
|
@ -549,34 +505,22 @@ public:
|
|||
// Cursor provides a way to seek into a DeltaTree and iterate over its contents
|
||||
// All Cursors from a Mirror share the same decoded node 'cache' (tree of DecodedNodes)
|
||||
struct Cursor {
|
||||
Cursor() : mirror(nullptr), node(nullptr) {
|
||||
}
|
||||
Cursor() : mirror(nullptr), node(nullptr) {}
|
||||
|
||||
Cursor(Mirror *r) : mirror(r), node(mirror->root) {
|
||||
}
|
||||
Cursor(Mirror* r) : mirror(r), node(mirror->root) {}
|
||||
|
||||
Mirror *mirror;
|
||||
DecodedNode *node;
|
||||
Mirror* mirror;
|
||||
DecodedNode* node;
|
||||
|
||||
bool valid() const {
|
||||
return node != nullptr;
|
||||
}
|
||||
bool valid() const { return node != nullptr; }
|
||||
|
||||
const T & get() const {
|
||||
return node->item;
|
||||
}
|
||||
const T& get() const { return node->item; }
|
||||
|
||||
const T & getOrUpperBound() const {
|
||||
return valid() ? node->item : *mirror->upperBound();
|
||||
}
|
||||
const T& getOrUpperBound() const { return valid() ? node->item : *mirror->upperBound(); }
|
||||
|
||||
bool operator==(const Cursor &rhs) const {
|
||||
return node == rhs.node;
|
||||
}
|
||||
bool operator==(const Cursor& rhs) const { return node == rhs.node; }
|
||||
|
||||
bool operator!=(const Cursor &rhs) const {
|
||||
return node != rhs.node;
|
||||
}
|
||||
bool operator!=(const Cursor& rhs) const { return node != rhs.node; }
|
||||
|
||||
void erase() {
|
||||
node->setDeleted(true);
|
||||
|
@ -584,72 +528,69 @@ public:
|
|||
moveNext();
|
||||
}
|
||||
|
||||
// TODO: Make hint-based seek() use the hint logic in this, which is better and actually improves seek times, then remove this function.
|
||||
bool seekLessThanOrEqualOld(const T &s, int skipLen, const Cursor *pHint, int initialCmp) {
|
||||
DecodedNode *n;
|
||||
// TODO: Make hint-based seek() use the hint logic in this, which is better and actually improves seek times,
|
||||
// then remove this function.
|
||||
bool seekLessThanOrEqualOld(const T& s, int skipLen, const Cursor* pHint, int initialCmp) {
|
||||
DecodedNode* n;
|
||||
|
||||
// If there's a hint position, use it
|
||||
// At the end of using the hint, if n is valid it should point to a node which has not yet been compared to.
|
||||
if(pHint != nullptr && pHint->node != nullptr) {
|
||||
if (pHint != nullptr && pHint->node != nullptr) {
|
||||
n = pHint->node;
|
||||
if(initialCmp == 0) {
|
||||
if (initialCmp == 0) {
|
||||
node = n;
|
||||
return _hideDeletedBackward();
|
||||
}
|
||||
if(initialCmp > 0) {
|
||||
if (initialCmp > 0) {
|
||||
node = n;
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
n = n->jumpNext(mirror->root);
|
||||
if(n == nullptr) {
|
||||
if (n == nullptr) {
|
||||
break;
|
||||
}
|
||||
int cmp = s.compare(n->item, skipLen);
|
||||
if(cmp > 0) {
|
||||
if (cmp > 0) {
|
||||
node = n;
|
||||
continue;
|
||||
}
|
||||
if(cmp == 0) {
|
||||
if (cmp == 0) {
|
||||
node = n;
|
||||
n = nullptr;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
n = n->leftChild;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
while(n != nullptr) {
|
||||
} else {
|
||||
while (n != nullptr) {
|
||||
n = n->jumpPrev(mirror->root);
|
||||
if(n == nullptr) {
|
||||
if (n == nullptr) {
|
||||
break;
|
||||
}
|
||||
int cmp = s.compare(n->item, skipLen);
|
||||
if(cmp >= 0) {
|
||||
if (cmp >= 0) {
|
||||
node = n;
|
||||
n = (cmp == 0) ? nullptr : n->rightChild;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Start at root, clear current position
|
||||
n = mirror->root;
|
||||
node = nullptr;
|
||||
}
|
||||
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
int cmp = s.compare(n->item, skipLen);
|
||||
|
||||
if(cmp < 0) {
|
||||
if (cmp < 0) {
|
||||
n = n->getLeftChild(mirror->arena);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// n <= s so store it in node as a potential result
|
||||
node = n;
|
||||
|
||||
if(cmp == 0) {
|
||||
if (cmp == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -665,54 +606,54 @@ public:
|
|||
// Then will not "see" erased records.
|
||||
// If successful, they return true, and if not then false a while making the cursor invalid.
|
||||
// These methods forward arguments to the seek() overloads, see those for argument descriptions.
|
||||
template<typename ...Args>
|
||||
template <typename... Args>
|
||||
bool seekLessThan(Args... args) {
|
||||
int cmp = seek(args...);
|
||||
if(cmp < 0 || (cmp == 0 && node != nullptr)) {
|
||||
if (cmp < 0 || (cmp == 0 && node != nullptr)) {
|
||||
movePrev();
|
||||
}
|
||||
return _hideDeletedBackward();
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
template <typename... Args>
|
||||
bool seekLessThanOrEqual(Args... args) {
|
||||
int cmp = seek(args...);
|
||||
if(cmp < 0) {
|
||||
if (cmp < 0) {
|
||||
movePrev();
|
||||
}
|
||||
return _hideDeletedBackward();
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
template <typename... Args>
|
||||
bool seekGreaterThan(Args... args) {
|
||||
int cmp = seek(args...);
|
||||
if(cmp > 0 || (cmp == 0 && node != nullptr)) {
|
||||
if (cmp > 0 || (cmp == 0 && node != nullptr)) {
|
||||
moveNext();
|
||||
}
|
||||
return _hideDeletedForward();
|
||||
}
|
||||
|
||||
template<typename ...Args>
|
||||
template <typename... Args>
|
||||
bool seekGreaterThanOrEqual(Args... args) {
|
||||
int cmp = seek(args...);
|
||||
if(cmp > 0) {
|
||||
if (cmp > 0) {
|
||||
moveNext();
|
||||
}
|
||||
return _hideDeletedForward();
|
||||
}
|
||||
|
||||
// seek() moves the cursor to a node containing s or the node that would be the parent of s if s were to be added to the tree.
|
||||
// If the tree was empty, the cursor will be invalid and the return value will be 0.
|
||||
// seek() moves the cursor to a node containing s or the node that would be the parent of s if s were to be
|
||||
// added to the tree. If the tree was empty, the cursor will be invalid and the return value will be 0.
|
||||
// Otherwise, returns the result of s.compare(item at cursor position)
|
||||
// Does not skip/avoid deleted nodes.
|
||||
int seek(const T &s, int skipLen = 0) {
|
||||
DecodedNode *n = mirror->root;
|
||||
int seek(const T& s, int skipLen = 0) {
|
||||
DecodedNode* n = mirror->root;
|
||||
node = nullptr;
|
||||
int cmp = 0;
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
node = n;
|
||||
cmp = s.compare(n->item, skipLen);
|
||||
if(cmp == 0) {
|
||||
if (cmp == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -726,32 +667,34 @@ public:
|
|||
// should be close to s in the tree to improve seek time.
|
||||
// initialCmp should be logically equivalent to s.compare(pHint->get()) or 0, in which
|
||||
// case the comparison will be done in this method.
|
||||
// TODO: This is broken, it's not faster than not using a hint. See Make thisUnfortunately in a microbenchmark attempting to approximate a common use case, this version
|
||||
// of using a cursor hint is actually slower than not using a hint.
|
||||
int seek(const T &s, int skipLen, const Cursor *pHint, int initialCmp = 0) {
|
||||
DecodedNode *n = mirror->root;
|
||||
// TODO: This is broken, it's not faster than not using a hint. See Make thisUnfortunately in a microbenchmark
|
||||
// attempting to approximate a common use case, this version of using a cursor hint is actually slower than not
|
||||
// using a hint.
|
||||
int seek(const T& s, int skipLen, const Cursor* pHint, int initialCmp = 0) {
|
||||
DecodedNode* n = mirror->root;
|
||||
node = nullptr;
|
||||
int cmp;
|
||||
|
||||
// If there's a hint position, use it
|
||||
// At the end of using the hint, if n is valid it should point to a node which has not yet been compared to.
|
||||
if(pHint->node != nullptr) {
|
||||
if (pHint->node != nullptr) {
|
||||
n = pHint->node;
|
||||
if(initialCmp == 0) {
|
||||
if (initialCmp == 0) {
|
||||
initialCmp = s.compare(pHint->get());
|
||||
}
|
||||
cmp = initialCmp;
|
||||
|
||||
while(true) {
|
||||
while (true) {
|
||||
node = n;
|
||||
if(cmp == 0) {
|
||||
if (cmp == 0) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
// Attempt to jump up and past s
|
||||
bool othersChild = false;
|
||||
n = (initialCmp > 0) ? n->jumpUpNext(mirror->root, othersChild) : n->jumpUpPrev(mirror->root, othersChild);
|
||||
if(n == nullptr) {
|
||||
n = (initialCmp > 0) ? n->jumpUpNext(mirror->root, othersChild)
|
||||
: n->jumpUpPrev(mirror->root, othersChild);
|
||||
if (n == nullptr) {
|
||||
n = (cmp > 0) ? node->rightChild : node->leftChild;
|
||||
break;
|
||||
}
|
||||
|
@ -760,15 +703,14 @@ public:
|
|||
cmp = s.compare(n->item, skipLen);
|
||||
|
||||
// n is on the oposite side of s than node is, then n is too far.
|
||||
if(cmp != 0 && ((initialCmp ^ cmp) < 0)) {
|
||||
if(!othersChild) {
|
||||
if (cmp != 0 && ((initialCmp ^ cmp) < 0)) {
|
||||
if (!othersChild) {
|
||||
n = (cmp < 0) ? node->rightChild : node->leftChild;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Start at root, clear current position
|
||||
n = mirror->root;
|
||||
node = nullptr;
|
||||
|
@ -776,10 +718,10 @@ public:
|
|||
}
|
||||
|
||||
// Search starting from n, which is either the root or the result of applying the hint
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
node = n;
|
||||
cmp = s.compare(n->item, skipLen);
|
||||
if(cmp == 0) {
|
||||
if (cmp == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -790,23 +732,21 @@ public:
|
|||
}
|
||||
|
||||
bool moveFirst() {
|
||||
DecodedNode *n = mirror->root;
|
||||
DecodedNode* n = mirror->root;
|
||||
node = n;
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
n = n->getLeftChild(mirror->arena);
|
||||
if(n != nullptr)
|
||||
node = n;
|
||||
if (n != nullptr) node = n;
|
||||
}
|
||||
return _hideDeletedForward();
|
||||
}
|
||||
|
||||
bool moveLast() {
|
||||
DecodedNode *n = mirror->root;
|
||||
DecodedNode* n = mirror->root;
|
||||
node = n;
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
n = n->getRightChild(mirror->arena);
|
||||
if(n != nullptr)
|
||||
node = n;
|
||||
if (n != nullptr) node = n;
|
||||
}
|
||||
return _hideDeletedBackward();
|
||||
}
|
||||
|
@ -814,15 +754,14 @@ public:
|
|||
// Try to move to next node, sees deleted nodes.
|
||||
void _moveNext() {
|
||||
// Try to go right
|
||||
DecodedNode *n = node->getRightChild(mirror->arena);
|
||||
DecodedNode* n = node->getRightChild(mirror->arena);
|
||||
|
||||
// If we couldn't go right, then the answer is our next ancestor
|
||||
if(n == nullptr) {
|
||||
if (n == nullptr) {
|
||||
node = node->getNextAncestor();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Go left as far as possible
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
node = n;
|
||||
n = n->getLeftChild(mirror->arena);
|
||||
}
|
||||
|
@ -832,15 +771,14 @@ public:
|
|||
// Try to move to previous node, sees deleted nodes.
|
||||
void _movePrev() {
|
||||
// Try to go left
|
||||
DecodedNode *n = node->getLeftChild(mirror->arena);
|
||||
DecodedNode* n = node->getLeftChild(mirror->arena);
|
||||
|
||||
// If we couldn't go left, then the answer is our prev ancestor
|
||||
if(n == nullptr) {
|
||||
if (n == nullptr) {
|
||||
node = node->getPrevAncestor();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Go right as far as possible
|
||||
while(n != nullptr) {
|
||||
while (n != nullptr) {
|
||||
node = n;
|
||||
n = n->getRightChild(mirror->arena);
|
||||
}
|
||||
|
@ -859,14 +797,14 @@ public:
|
|||
|
||||
private:
|
||||
bool _hideDeletedBackward() {
|
||||
while(node != nullptr && node->isDeleted()) {
|
||||
while (node != nullptr && node->isDeleted()) {
|
||||
_movePrev();
|
||||
}
|
||||
return node != nullptr;
|
||||
}
|
||||
|
||||
bool _hideDeletedForward() {
|
||||
while(node != nullptr && node->isDeleted()) {
|
||||
while (node != nullptr && node->isDeleted()) {
|
||||
_moveNext();
|
||||
}
|
||||
return node != nullptr;
|
||||
|
@ -874,7 +812,7 @@ public:
|
|||
};
|
||||
|
||||
// Returns number of bytes written
|
||||
int build(int spaceAvailable, const T *begin, const T *end, const T *prev, const T *next) {
|
||||
int build(int spaceAvailable, const T* begin, const T* end, const T* prev, const T* next) {
|
||||
largeNodes = spaceAvailable > SmallSizeLimit;
|
||||
int count = end - begin;
|
||||
numItems = count;
|
||||
|
@ -883,10 +821,9 @@ public:
|
|||
maxHeight = 0;
|
||||
|
||||
// The boundary leading to the new page acts as the last time we branched right
|
||||
if(begin != end) {
|
||||
if (begin != end) {
|
||||
nodeBytesUsed = buildSubtree(root(), begin, end, prev, next, prev->getCommonPrefixLen(*next, 0));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
nodeBytesUsed = 0;
|
||||
}
|
||||
nodeBytesFree = spaceAvailable - size();
|
||||
|
@ -894,28 +831,28 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
int buildSubtree(Node &node, const T *begin, const T *end, const T *prev, const T *next, int subtreeCommon) {
|
||||
//printf("build: %s to %s\n", begin->toString().c_str(), (end - 1)->toString().c_str());
|
||||
//printf("build: root at %p Node::headerSize %d delta at %p \n", &root, Node::headerSize(largeNodes), &node.delta(largeNodes));
|
||||
int buildSubtree(Node& node, const T* begin, const T* end, const T* prev, const T* next, int subtreeCommon) {
|
||||
// printf("build: %s to %s\n", begin->toString().c_str(), (end - 1)->toString().c_str());
|
||||
// printf("build: root at %p Node::headerSize %d delta at %p \n", &root, Node::headerSize(largeNodes),
|
||||
// &node.delta(largeNodes));
|
||||
ASSERT(end != begin);
|
||||
int count = end - begin;
|
||||
|
||||
// Find key to be stored in root
|
||||
int mid = perfectSubtreeSplitPointCached(count);
|
||||
const T &item = begin[mid];
|
||||
const T& item = begin[mid];
|
||||
|
||||
int commonWithPrev = item.getCommonPrefixLen(*prev, subtreeCommon);
|
||||
int commonWithNext = item.getCommonPrefixLen(*next, subtreeCommon);
|
||||
|
||||
bool prefixSourcePrev;
|
||||
int commonPrefix;
|
||||
const T *base;
|
||||
if(commonWithPrev >= commonWithNext) {
|
||||
const T* base;
|
||||
if (commonWithPrev >= commonWithNext) {
|
||||
prefixSourcePrev = true;
|
||||
commonPrefix = commonWithPrev;
|
||||
base = prev;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
prefixSourcePrev = false;
|
||||
commonPrefix = commonWithNext;
|
||||
base = next;
|
||||
|
@ -923,29 +860,27 @@ private:
|
|||
|
||||
int deltaSize = item.writeDelta(node.delta(largeNodes), *base, commonPrefix);
|
||||
node.delta(largeNodes).setPrefixSource(prefixSourcePrev);
|
||||
//printf("Serialized %s to %p\n", item.toString().c_str(), &root.delta(largeNodes));
|
||||
// printf("Serialized %s to %p\n", item.toString().c_str(), &root.delta(largeNodes));
|
||||
|
||||
// Continue writing after the serialized Delta.
|
||||
uint8_t *wptr = (uint8_t *)&node.delta(largeNodes) + deltaSize;
|
||||
uint8_t* wptr = (uint8_t*)&node.delta(largeNodes) + deltaSize;
|
||||
|
||||
// Serialize left child
|
||||
if(count > 1) {
|
||||
wptr += buildSubtree(*(Node *)wptr, begin, begin + mid, prev, &item, commonWithPrev);
|
||||
if (count > 1) {
|
||||
wptr += buildSubtree(*(Node*)wptr, begin, begin + mid, prev, &item, commonWithPrev);
|
||||
node.setLeftChildOffset(largeNodes, Node::headerSize(largeNodes) + deltaSize);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
node.setLeftChildOffset(largeNodes, 0);
|
||||
}
|
||||
|
||||
// Serialize right child
|
||||
if(count > 2) {
|
||||
node.setRightChildOffset(largeNodes, wptr - (uint8_t *)&node);
|
||||
wptr += buildSubtree(*(Node *)wptr, begin + mid + 1, end, &item, next, commonWithNext);
|
||||
}
|
||||
else {
|
||||
if (count > 2) {
|
||||
node.setRightChildOffset(largeNodes, wptr - (uint8_t*)&node);
|
||||
wptr += buildSubtree(*(Node*)wptr, begin + mid + 1, end, &item, next, commonWithNext);
|
||||
} else {
|
||||
node.setRightChildOffset(largeNodes, 0);
|
||||
}
|
||||
|
||||
return wptr - (uint8_t *)&node;
|
||||
return wptr - (uint8_t*)&node;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -30,23 +30,29 @@
|
|||
#define REDWOOD_DEBUG 0
|
||||
|
||||
#define debug_printf_stream stdout
|
||||
#define debug_printf_always(...) { fprintf(debug_printf_stream, "%s %f %04d ", g_network->getLocalAddress().toString().c_str(), now(), __LINE__); fprintf(debug_printf_stream, __VA_ARGS__); fflush(debug_printf_stream); }
|
||||
#define debug_printf_always(...) \
|
||||
{ \
|
||||
fprintf(debug_printf_stream, "%s %f %04d ", g_network->getLocalAddress().toString().c_str(), now(), __LINE__); \
|
||||
fprintf(debug_printf_stream, __VA_ARGS__); \
|
||||
fflush(debug_printf_stream); \
|
||||
}
|
||||
|
||||
#define debug_printf_noop(...)
|
||||
|
||||
#if defined(NO_INTELLISENSE)
|
||||
#if REDWOOD_DEBUG
|
||||
#define debug_printf debug_printf_always
|
||||
#else
|
||||
#define debug_printf debug_printf_noop
|
||||
#endif
|
||||
#if REDWOOD_DEBUG
|
||||
#define debug_printf debug_printf_always
|
||||
#else
|
||||
// To get error-checking on debug_printf statements in IDE
|
||||
#define debug_printf printf
|
||||
#define debug_printf debug_printf_noop
|
||||
#endif
|
||||
#else
|
||||
// To get error-checking on debug_printf statements in IDE
|
||||
#define debug_printf printf
|
||||
#endif
|
||||
|
||||
#define BEACON debug_printf_always("HERE\n")
|
||||
#define TRACE debug_printf_always("%s: %s line %d %s\n", __FUNCTION__, __FILE__, __LINE__, platform::get_backtrace().c_str());
|
||||
#define TRACE \
|
||||
debug_printf_always("%s: %s line %d %s\n", __FUNCTION__, __FILE__, __LINE__, platform::get_backtrace().c_str());
|
||||
|
||||
#ifndef VALGRIND
|
||||
#define VALGRIND_MAKE_MEM_UNDEFINED(x, y)
|
||||
|
@ -67,12 +73,10 @@ public:
|
|||
// Must return the same size for all pages created by the same pager instance
|
||||
virtual int size() const = 0;
|
||||
|
||||
StringRef asStringRef() const {
|
||||
return StringRef(begin(), size());
|
||||
}
|
||||
StringRef asStringRef() const { return StringRef(begin(), size()); }
|
||||
|
||||
virtual ~IPage() {
|
||||
if(userData != nullptr && userDataDestructor != nullptr) {
|
||||
if (userData != nullptr && userDataDestructor != nullptr) {
|
||||
userDataDestructor(userData);
|
||||
}
|
||||
}
|
||||
|
@ -82,8 +86,8 @@ public:
|
|||
virtual void addref() const = 0;
|
||||
virtual void delref() const = 0;
|
||||
|
||||
mutable void *userData;
|
||||
mutable void (*userDataDestructor)(void *);
|
||||
mutable void* userData;
|
||||
mutable void (*userDataDestructor)(void*);
|
||||
};
|
||||
|
||||
class IPagerSnapshot {
|
||||
|
|
|
@ -50,8 +50,9 @@ public:
|
|||
virtual StorageBytes getStorageBytes() = 0;
|
||||
|
||||
// Writes are provided in an ordered stream.
|
||||
// A write is considered part of (a change leading to) the version determined by the previous call to setWriteVersion()
|
||||
// A write shall not become durable until the following call to commit() begins, and shall be durable once the following call to commit() returns
|
||||
// A write is considered part of (a change leading to) the version determined by the previous call to
|
||||
// setWriteVersion() A write shall not become durable until the following call to commit() begins, and shall be
|
||||
// durable once the following call to commit() returns
|
||||
virtual void set(KeyValueRef keyValue) = 0;
|
||||
virtual void clear(KeyRangeRef range) = 0;
|
||||
virtual void mutate(int op, StringRef param1, StringRef param2) = 0;
|
||||
|
@ -63,11 +64,15 @@ public:
|
|||
virtual Future<Void> init() = 0;
|
||||
virtual Version getLatestVersion() = 0;
|
||||
|
||||
// readAtVersion() may only be called on a version which has previously been passed to setWriteVersion() and never previously passed
|
||||
// to forgetVersion. The returned results when violating this precondition are unspecified; the store is not required to be able to detect violations.
|
||||
// The returned read cursor provides a consistent snapshot of the versioned store, corresponding to all the writes done with write versions less
|
||||
// readAtVersion() may only be called on a version which has previously been passed to setWriteVersion() and never
|
||||
// previously passed
|
||||
// to forgetVersion. The returned results when violating this precondition are unspecified; the store is not
|
||||
// required to be able to detect violations.
|
||||
// The returned read cursor provides a consistent snapshot of the versioned store, corresponding to all the writes
|
||||
// done with write versions less
|
||||
// than or equal to the given version.
|
||||
// If readAtVersion() is called on the *current* write version, the given read cursor MAY reflect subsequent writes at the same
|
||||
// If readAtVersion() is called on the *current* write version, the given read cursor MAY reflect subsequent writes
|
||||
// at the same
|
||||
// write version, OR it may represent a snapshot as of the call to readAtVersion().
|
||||
virtual Reference<IStoreCursor> readAtVersion(Version) = 0;
|
||||
};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue