2017-05-26 04:48:44 +08:00
/*
* VersionedMap . h
*
* This source file is part of the FoundationDB open source project
*
* Copyright 2013 - 2018 Apple Inc . and the FoundationDB project authors
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Licensed under the Apache License , Version 2.0 ( the " License " ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* http : //www.apache.org/licenses/LICENSE-2.0
2018-02-22 02:25:11 +08:00
*
2017-05-26 04:48:44 +08:00
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an " AS IS " BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
*/
# ifndef FDBCLIENT_VERSIONEDMAP_H
# define FDBCLIENT_VERSIONEDMAP_H
# pragma once
# include "flow/flow.h"
# include "flow/IndexedSet.h"
# include "FDBTypes.h"
# include "flow/IRandom.h"
# include "VersionedMap.actor.h"
// PTree is a persistent balanced binary tree implementation. It is based on a treap as a way to guarantee O(1) space for node insertion (rotating is asymptotically cheap),
// but the constant factors are very large.
//
// Each node has three pointers - the first two are its left and right children, respectively, and the third can be set to point to a newer version of the node.
// This third pointer allows us to maintain persistence without full path copying, and is employed to achieve O(1) space node insertion.
//
// PTree also supports efficient finger searches.
namespace PTreeImpl {
# pragma warning(disable: 4800)
template < class T >
struct PTree : public ReferenceCounted < PTree < T > > , FastAllocated < PTree < T > > , NonCopyable {
uint32_t priority ;
Reference < PTree > pointer [ 3 ] ;
Version lastUpdateVersion ;
bool updated ;
bool replacedPointer ;
T data ;
Reference < PTree > child ( bool which , Version at ) const {
if ( updated & & lastUpdateVersion < = at & & which = = replacedPointer )
return pointer [ 2 ] ;
else
return pointer [ which ] ;
}
Reference < PTree > left ( Version at ) const { return child ( false , at ) ; }
Reference < PTree > right ( Version at ) const { return child ( true , at ) ; }
PTree ( const T & data , Version ver ) : data ( data ) , lastUpdateVersion ( ver ) , updated ( false ) {
priority = g_random - > randomUInt32 ( ) ;
}
PTree ( uint32_t pri , T const & data , Reference < PTree > const & left , Reference < PTree > const & right , Version ver ) : priority ( pri ) , data ( data ) , lastUpdateVersion ( ver ) , updated ( false ) {
pointer [ 0 ] = left ; pointer [ 1 ] = right ;
}
private :
PTree ( PTree const & ) ;
} ;
template < class T >
static Reference < PTree < T > > update ( Reference < PTree < T > > const & node , bool which , Reference < PTree < T > > const & ptr , Version at ) {
if ( ptr . getPtr ( ) = = node - > child ( which , at ) . getPtr ( ) /* && node->replacedVersion <= at*/ ) {
return node ;
}
if ( node - > lastUpdateVersion = = at ) {
//&& (!node->updated || node->replacedPointer==which)) {
if ( node - > updated & & node - > replacedPointer ! = which ) {
// We are going to have to copy this node, but its aux pointer will never be used again
// and should drop its reference count
Reference < PTree < T > > r ;
if ( which )
r = Reference < PTree < T > > ( new PTree < T > ( node - > priority , node - > data , node - > child ( 0 , at ) , ptr , at ) ) ;
else
r = Reference < PTree < T > > ( new PTree < T > ( node - > priority , node - > data , ptr , node - > child ( 1 , at ) , at ) ) ;
node - > pointer [ 2 ] . clear ( ) ;
return r ;
} else {
if ( node - > updated )
node - > pointer [ 2 ] = ptr ;
else
node - > pointer [ which ] = ptr ;
return node ;
}
}
if ( node - > updated ) {
if ( which )
return Reference < PTree < T > > ( new PTree < T > ( node - > priority , node - > data , node - > child ( 0 , at ) , ptr , at ) ) ;
else
return Reference < PTree < T > > ( new PTree < T > ( node - > priority , node - > data , ptr , node - > child ( 1 , at ) , at ) ) ;
} else {
node - > lastUpdateVersion = at ;
node - > replacedPointer = which ;
node - > pointer [ 2 ] = ptr ;
node - > updated = true ;
return node ;
}
}
template < class T , class X >
bool contains ( const Reference < PTree < T > > & p , Version at , const X & x ) {
if ( ! p ) return false ;
bool less = x < p - > data ;
if ( ! less & & ! ( p - > data < x ) ) return true ; // x == p->data
return contains ( p - > child ( ! less , at ) , at , x ) ;
}
template < class T , class X >
void lower_bound ( const Reference < PTree < T > > & p , Version at , const X & x , std : : vector < const PTree < T > * > & f ) {
if ( ! p ) {
while ( f . size ( ) & & ! ( x < f . back ( ) - > data ) )
f . pop_back ( ) ;
return ;
}
f . push_back ( p . getPtr ( ) ) ;
bool less = x < p - > data ;
if ( ! less & & ! ( p - > data < x ) ) return ; // x == p->data
lower_bound ( p - > child ( ! less , at ) , at , x , f ) ;
}
template < class T , class X >
void upper_bound ( const Reference < PTree < T > > & p , Version at , const X & x , std : : vector < const PTree < T > * > & f ) {
if ( ! p ) {
while ( f . size ( ) & & ! ( x < f . back ( ) - > data ) )
f . pop_back ( ) ;
return ;
}
f . push_back ( p . getPtr ( ) ) ;
upper_bound ( p - > child ( ! ( x < p - > data ) , at ) , at , x , f ) ;
}
template < class T , bool forward >
void move ( Version at , std : : vector < const PTree < T > * > & f ) {
ASSERT ( f . size ( ) ) ;
const PTree < T > * n ;
n = f . back ( ) ;
if ( n - > child ( forward , at ) ) {
n = n - > child ( forward , at ) . getPtr ( ) ;
do {
f . push_back ( n ) ;
n = n - > child ( ! forward , at ) . getPtr ( ) ;
} while ( n ) ;
} else {
do {
n = f . back ( ) ;
f . pop_back ( ) ;
} while ( f . size ( ) & & f . back ( ) - > child ( forward , at ) . getPtr ( ) = = n ) ;
}
}
template < class T , bool forward >
int halfMove ( Version at , std : : vector < const PTree < T > * > & f ) {
// Post: f[:return_value] is the finger that would have been returned by move<forward>(at,f), and f[:original_length_of_f] is unmodified
ASSERT ( f . size ( ) ) ;
const PTree < T > * n ;
n = f . back ( ) ;
if ( n - > child ( forward , at ) ) {
n = n - > child ( forward , at ) . getPtr ( ) ;
do {
f . push_back ( n ) ;
n = n - > child ( ! forward , at ) . getPtr ( ) ;
} while ( n ) ;
return f . size ( ) ;
} else {
int s = f . size ( ) ;
do {
n = f [ s - 1 ] ;
- - s ;
} while ( s & & f [ s - 1 ] - > child ( forward , at ) . getPtr ( ) = = n ) ;
return s ;
}
}
template < class T >
void next ( Version at , std : : vector < const PTree < T > * > & f ) {
move < T , true > ( at , f ) ;
}
template < class T >
void previous ( Version at , std : : vector < const PTree < T > * > & f ) {
move < T , false > ( at , f ) ;
}
template < class T >
int halfNext ( Version at , std : : vector < const PTree < T > * > & f ) {
return halfMove < T , true > ( at , f ) ;
}
template < class T >
int halfPrevious ( Version at , std : : vector < const PTree < T > * > & f ) {
return halfMove < T , false > ( at , f ) ;
}
template < class T >
T get ( std : : vector < const PTree < T > * > & f ) {
ASSERT ( f . size ( ) ) ;
return f . back ( ) - > data ;
}
// Modifies p to point to a PTree with x inserted
template < class T >
void insert ( Reference < PTree < T > > & p , Version at , const T & x ) {
if ( ! p ) {
p = Reference < PTree < T > > ( new PTree < T > ( x , at ) ) ;
} else {
bool direction = ! ( x < p - > data ) ;
Reference < PTree < T > > child = p - > child ( direction , at ) ;
insert ( child , at , x ) ;
p = update ( p , direction , child , at ) ;
if ( p - > child ( direction , at ) - > priority > p - > priority )
rotate ( p , at , ! direction ) ;
}
}
template < class T >
Reference < PTree < T > > firstNode ( const Reference < PTree < T > > & p , Version at ) {
if ( ! p ) ASSERT ( false ) ;
if ( ! p - > left ( at ) ) return p ;
return firstNode ( p - > left ( at ) , at ) ;
}
template < class T >
Reference < PTree < T > > lastNode ( const Reference < PTree < T > > & p , Version at ) {
if ( ! p ) ASSERT ( false ) ;
if ( ! p - > right ( at ) ) return p ;
return lastNode ( p - > right ( at ) , at ) ;
}
template < class T , bool last >
void firstOrLastFinger ( const Reference < PTree < T > > & p , Version at , std : : vector < const PTree < T > * > & f ) {
if ( ! p ) return ;
f . push_back ( p . getPtr ( ) ) ;
firstOrLastFinger < T , last > ( p - > child ( last , at ) , at , f ) ;
}
template < class T >
void first ( const Reference < PTree < T > > & p , Version at , std : : vector < const PTree < T > * > & f ) {
return firstOrLastFinger < T , false > ( p , at , f ) ;
}
template < class T >
void last ( const Reference < PTree < T > > & p , Version at , std : : vector < const PTree < T > * > & f ) {
return firstOrLastFinger < T , true > ( p , at , f ) ;
}
// modifies p to point to a PTree with the root of p removed
template < class T >
void removeRoot ( Reference < PTree < T > > & p , Version at ) {
if ( ! p - > right ( at ) )
p = p - > left ( at ) ;
else if ( ! p - > left ( at ) )
p = p - > right ( at ) ;
else {
bool direction = p - > right ( at ) - > priority < p - > left ( at ) - > priority ;
rotate ( p , at , direction ) ;
Reference < PTree < T > > child = p - > child ( direction , at ) ;
removeRoot ( child , at ) ;
p = update ( p , direction , child , at ) ;
}
}
// changes p to point to a PTree with x removed
template < class T , class X >
void remove ( Reference < PTree < T > > & p , Version at , const X & x ) {
if ( ! p ) ASSERT ( false ) ; // attempt to remove item not present in PTree
if ( x < p - > data ) {
Reference < PTree < T > > child = p - > child ( 0 , at ) ;
remove ( child , at , x ) ;
p = update ( p , 0 , child , at ) ;
} else if ( p - > data < x ) {
Reference < PTree < T > > child = p - > child ( 1 , at ) ;
remove ( child , at , x ) ;
p = update ( p , 1 , child , at ) ;
} else
removeRoot ( p , at ) ;
}
template < class T , class X >
void remove ( Reference < PTree < T > > & p , Version at , const X & begin , const X & end ) {
if ( ! p ) return ;
int beginDir , endDir ;
if ( begin < p - > data ) beginDir = - 1 ;
else if ( p - > data < begin ) beginDir = + 1 ;
else beginDir = 0 ;
if ( ! ( p - > data < end ) ) endDir = - 1 ;
else endDir = + 1 ;
if ( beginDir = = endDir ) {
Reference < PTree < T > > child = p - > child ( beginDir = = + 1 , at ) ;
remove ( child , at , begin , end ) ;
p = update ( p , beginDir = = + 1 , child , at ) ;
} else {
if ( beginDir = = - 1 ) {
Reference < PTree < T > > left = p - > child ( 0 , at ) ;
removeBeyond ( left , at , begin , 1 ) ;
p = update ( p , 0 , left , at ) ;
}
if ( endDir = = + 1 ) {
Reference < PTree < T > > right = p - > child ( 1 , at ) ;
removeBeyond ( right , at , end , 0 ) ;
p = update ( p , 1 , right , at ) ;
}
if ( beginDir < endDir )
removeRoot ( p , at ) ;
}
}
template < class T , class X >
void removeBeyond ( Reference < PTree < T > > & p , Version at , const X & pivot , bool dir ) {
if ( ! p ) return ;
if ( ( p - > data < pivot ) ^ dir ) {
p = p - > child ( ! dir , at ) ;
removeBeyond ( p , at , pivot , dir ) ;
} else {
Reference < PTree < T > > child = p - > child ( dir , at ) ;
removeBeyond ( child , at , pivot , dir ) ;
p = update ( p , dir , child , at ) ;
}
}
/*template<class T, class X>
void remove ( Reference < PTree < T > > & p , Version at , const X & begin , const X & end ) {
Reference < PTree < T > > left , center , right ;
split ( p , begin , left , center , at ) ;
split ( center , end , center , right , at ) ;
p = append ( left , right , at ) ;
} */
// inputs a PTree with the root node potentially violating the heap property
// modifies p to point to a valid PTree
template < class T >
void demoteRoot ( Reference < PTree < T > > & p , Version at ) {
if ( ! p ) ASSERT ( false ) ;
uint32_t priority [ 2 ] ;
for ( int i = 0 ; i < 2 ; i + + )
if ( p - > child ( i , at ) ) priority [ i ] = p - > child ( i , at ) - > priority ;
else priority [ i ] = 0 ;
bool higherDirection = priority [ 1 ] > priority [ 0 ] ;
if ( priority [ higherDirection ] < p - > priority ) return ;
// else, child(higherDirection) is a greater priority than us and the other child...
rotate ( p , at , ! higherDirection ) ;
Reference < PTree < T > > child = p - > child ( ! higherDirection , at ) ;
demoteRoot ( child , at ) ;
p = update ( p , ! higherDirection , child , at ) ;
}
template < class T >
Reference < PTree < T > > append ( const Reference < PTree < T > > & left , const Reference < PTree < T > > & right , Version at ) {
if ( ! left ) return right ;
if ( ! right ) return left ;
Reference < PTree < T > > r = Reference < PTree < T > > ( new PTree < T > ( lastNode ( left , at ) - > data , at ) ) ;
ASSERT ( r - > data < firstNode ( right , at ) - > data ) ;
Reference < PTree < T > > a = left ;
remove ( a , at , r - > data ) ;
r - > pointer [ 0 ] = a ;
r - > pointer [ 1 ] = right ;
demoteRoot ( r , at ) ;
return r ;
}
template < class T , class X >
void split ( Reference < PTree < T > > p , const X & x , Reference < PTree < T > > & left , Reference < PTree < T > > & right , Version at ) {
if ( ! p ) {
left = Reference < PTree < T > > ( ) ;
right = Reference < PTree < T > > ( ) ;
return ;
}
if ( p - > data < x ) {
left = p ;
Reference < PTree < T > > lr = left - > right ( at ) ;
split ( lr , x , lr , right , at ) ;
left = update ( left , 1 , lr , at ) ;
} else {
right = p ;
Reference < PTree < T > > rl = right - > left ( at ) ;
split ( rl , x , left , rl , at ) ;
right = update ( right , 0 , rl , at ) ;
}
}
template < class T >
void rotate ( Reference < PTree < T > > & p , Version at , bool right ) {
auto r = p - > child ( ! right , at ) ;
auto n1 = r - > child ( ! right , at ) ;
auto n2 = r - > child ( right , at ) ;
auto n3 = p - > child ( right , at ) ;
auto newC = update ( p , ! right , n2 , at ) ;
newC = update ( newC , right , n3 , at ) ;
p = update ( r , ! right , n1 , at ) ;
p = update ( p , right , newC , at ) ;
}
template < class T >
void printTree ( const Reference < PTree < T > > & p , Version at , int depth = 0 ) {
if ( p - > left ( at ) ) printTree ( p - > left ( at ) , at , depth + 1 ) ;
for ( int i = 0 ; i < depth ; i + + )
printf ( " " ) ;
printf ( " :%s \n " , describe ( p - > data ) . c_str ( ) ) ;
if ( p - > right ( at ) ) printTree ( p - > right ( at ) , at , depth + 1 ) ;
}
template < class T >
void printTreeDetails ( const Reference < PTree < T > > & p , int depth = 0 ) {
printf ( " Node %p (depth %d): %s \n " , p . getPtr ( ) , depth , describe ( p - > data ) . c_str ( ) ) ;
printf ( " Left: %p \n " , p - > pointer [ 0 ] . getPtr ( ) ) ;
printf ( " Right: %p \n " , p - > pointer [ 1 ] . getPtr ( ) ) ;
if ( p - > pointer [ 2 ] )
printf ( " Version %lld %s: %p \n " , p - > lastUpdateVersion , p - > replacedPointer ? " Right " : " Left " , p - > pointer [ 2 ] . getPtr ( ) ) ;
for ( int i = 0 ; i < 3 ; i + + )
if ( p - > pointer [ i ] ) printTreeDetails ( p - > pointer [ i ] , depth + 1 ) ;
}
/*static int depth(const Reference<PTree<int>>& p, Version at) {
if ( ! p ) return 0 ;
int d1 = depth ( p - > left ( at ) , at ) + 1 ;
int d2 = depth ( p - > right ( at ) , at ) + 1 ;
return d1 > d2 ? d1 : d2 ;
} */
template < class T >
void validate ( const Reference < PTree < T > > & p , Version at , T * min , T * max , int & count , int & height , int depth = 0 ) {
if ( ! p ) { height = 0 ; return ; }
ASSERT ( ( ! min | | * min < = p - > data ) & & ( ! max | | p - > data < = * max ) ) ;
for ( int i = 0 ; i < 2 ; i + + ) {
if ( p - > child ( i , at ) )
ASSERT ( p - > child ( i , at ) - > priority < = p - > priority ) ;
}
+ + count ;
int h1 , h2 ;
validate ( p - > left ( at ) , at , min , & p - > data , count , h1 , depth + 1 ) ;
validate ( p - > right ( at ) , at , & p - > data , max , count , h2 , depth + 1 ) ;
height = std : : max ( h1 , h2 ) + 1 ;
}
template < class T >
void check ( const Reference < PTree < T > > & p ) {
int count = 0 , height ;
validate ( p , ( T * ) 0 , ( T * ) 0 , count , height ) ;
if ( count & & height > 4.3 * log ( double ( count ) ) ) {
//printf("height %d; count %d\n", height, count);
ASSERT ( false ) ;
}
}
}
// VersionedMap provides an interface to a partially persistent tree, allowing you to read the values at a particular version,
// create new versions, modify the current version of the tree, and forget versions prior to a specific version.
template < class K , class T >
class VersionedMap : NonCopyable {
//private:
public :
typedef PTreeImpl : : PTree < MapPair < K , std : : pair < T , Version > > > PTreeT ;
typedef Reference < PTreeT > Tree ;
Version oldestVersion , latestVersion ;
std : : map < Version , Tree > roots ;
Tree * latestRoot ;
Tree const & getRoot ( Version v ) const {
auto r = roots . upper_bound ( v ) ;
- - r ;
return r - > second ;
}
static const int overheadPerItem = 128 * 4 ;
struct iterator ;
VersionedMap ( ) : oldestVersion ( 0 ) , latestVersion ( 0 ) {
latestRoot = & roots [ 0 ] ;
}
VersionedMap ( VersionedMap & & v ) noexcept ( true ) : oldestVersion ( v . oldestVersion ) , latestVersion ( v . latestVersion ) , roots ( std : : move ( v . roots ) ) {
latestRoot = & roots [ latestVersion ] ;
}
void operator = ( VersionedMap & & v ) noexcept ( true ) {
oldestVersion = v . oldestVersion ;
latestVersion = v . latestVersion ;
roots = std : : move ( v . roots ) ;
latestRoot = & roots [ latestVersion ] ;
}
Version getLatestVersion ( ) const { return latestVersion ; }
Version getOldestVersion ( ) const { return oldestVersion ; }
Version getNextOldestVersion ( ) const { return roots . upper_bound ( oldestVersion ) - > first ; }
void forgetVersionsBefore ( Version newOldestVersion ) {
ASSERT ( newOldestVersion < = latestVersion ) ;
roots [ newOldestVersion ] = getRoot ( newOldestVersion ) ;
roots . erase ( roots . begin ( ) , roots . lower_bound ( newOldestVersion ) ) ;
oldestVersion = newOldestVersion ;
}
Future < Void > forgetVersionsBeforeAsync ( Version newOldestVersion , int taskID = 7000 ) {
ASSERT ( newOldestVersion < = latestVersion ) ;
roots [ newOldestVersion ] = getRoot ( newOldestVersion ) ;
vector < Tree > toFree ;
2018-07-13 06:29:02 +08:00
toFree . reserve ( 10000 ) ;
2017-05-26 04:48:44 +08:00
auto newBegin = roots . lower_bound ( newOldestVersion ) ;
2018-07-13 06:29:02 +08:00
Tree * lastRoot = nullptr ;
for ( auto root = roots . begin ( ) ; root ! = newBegin ; + + root ) {
if ( root - > second ) {
if ( lastRoot ! = nullptr & & root - > second = = * lastRoot ) {
( * lastRoot ) . clear ( ) ;
}
if ( root - > second - > isSoleOwner ( ) ) {
toFree . push_back ( root - > second ) ;
}
lastRoot = & root - > second ;
}
2017-05-26 04:48:44 +08:00
}
roots . erase ( roots . begin ( ) , newBegin ) ;
oldestVersion = newOldestVersion ;
return deferredCleanupActor ( toFree , taskID ) ;
}
public :
void createNewVersion ( Version version ) { // following sets and erases are into the given version, which may now be passed to at(). Must be called in monotonically increasing order.
if ( version > latestVersion ) {
latestVersion = version ;
Tree r = getRoot ( version ) ;
latestRoot = & roots [ version ] ;
* latestRoot = r ;
} else ASSERT ( version = = latestVersion ) ;
}
// insert() and erase() invalidate atLatest() and all iterators into it
void insert ( const K & k , const T & t ) {
insert ( k , t , latestVersion ) ;
}
void insert ( const K & k , const T & t , Version insertAt ) {
if ( PTreeImpl : : contains ( * latestRoot , latestVersion , k ) ) PTreeImpl : : remove ( * latestRoot , latestVersion , k ) ; // FIXME: Make PTreeImpl::insert do this automatically (see also WriteMap.h FIXME)
PTreeImpl : : insert ( * latestRoot , latestVersion , MapPair < K , std : : pair < T , Version > > ( k , std : : make_pair ( t , insertAt ) ) ) ;
}
void erase ( const K & begin , const K & end ) {
PTreeImpl : : remove ( * latestRoot , latestVersion , begin , end ) ;
}
void erase ( const K & key ) { // key must be present
PTreeImpl : : remove ( * latestRoot , latestVersion , key ) ;
}
void erase ( iterator const & item ) { // iterator must be in latest version!
// SOMEDAY: Optimize to use item.finger and avoid repeated search
K key = item . key ( ) ;
erase ( key ) ;
}
// for(auto i = vm.at(version).lower_bound(range.begin); i < range.end; ++i)
struct iterator {
explicit iterator ( Tree const & root , Version at ) : root ( root ) , at ( at ) { }
K const & key ( ) const { return finger . back ( ) - > data . key ; }
Version insertVersion ( ) const { return finger . back ( ) - > data . value . second ; } // Returns the version at which the current item was inserted
operator bool ( ) const { return finger . size ( ) ! = 0 ; }
bool operator < ( const K & key ) const { return this - > key ( ) < key ; }
T const & operator * ( ) { return finger . back ( ) - > data . value . first ; }
T const * operator - > ( ) { return & finger . back ( ) - > data . value . first ; }
void operator + + ( ) { if ( finger . size ( ) ) PTreeImpl : : next ( at , finger ) ; else PTreeImpl : : first ( root , at , finger ) ; }
void operator - - ( ) { if ( finger . size ( ) ) PTreeImpl : : previous ( at , finger ) ; else PTreeImpl : : last ( root , at , finger ) ; }
bool operator = = ( const iterator & r ) const { if ( finger . size ( ) & & r . finger . size ( ) ) return finger . back ( ) = = r . finger . back ( ) ; else return finger . size ( ) = = r . finger . size ( ) ; }
bool operator ! = ( const iterator & r ) const { if ( finger . size ( ) & & r . finger . size ( ) ) return finger . back ( ) ! = r . finger . back ( ) ; else return finger . size ( ) ! = r . finger . size ( ) ; }
private :
friend class VersionedMap < K , T > ;
Tree root ;
Version at ;
vector < PTreeT const * > finger ;
} ;
class ViewAtVersion {
public :
ViewAtVersion ( Tree const & root , Version at ) : root ( root ) , at ( at ) { }
iterator begin ( ) const { iterator i ( root , at ) ; PTreeImpl : : first ( root , at , i . finger ) ; return i ; }
iterator end ( ) const { return iterator ( root , at ) ; }
// Returns x such that key==*x, or end()
template < class X >
iterator find ( const X & key ) const {
iterator i ( root , at ) ;
PTreeImpl : : lower_bound ( root , at , key , i . finger ) ;
if ( i & & i . key ( ) = = key )
return i ;
else
return end ( ) ;
}
// Returns the smallest x such that *x>=key, or end()
template < class X >
iterator lower_bound ( const X & key ) const {
iterator i ( root , at ) ;
PTreeImpl : : lower_bound ( root , at , key , i . finger ) ;
return i ;
}
// Returns the smallest x such that *x>key, or end()
template < class X >
iterator upper_bound ( const X & key ) const {
iterator i ( root , at ) ;
PTreeImpl : : upper_bound ( root , at , key , i . finger ) ;
return i ;
}
// Returns the largest x such that *x<=key, or end()
template < class X >
iterator lastLessOrEqual ( const X & key ) const {
iterator i ( root , at ) ;
PTreeImpl : : upper_bound ( root , at , key , i . finger ) ;
- - i ;
return i ;
}
// Returns the largest x such that *x<key, or end()
template < class X >
iterator lastLess ( const X & key ) const {
iterator i ( root , at ) ;
PTreeImpl : : lower_bound ( root , at , key , i . finger ) ;
- - i ;
return i ;
}
void validate ( ) {
int count = 0 , height = 0 ;
PTreeImpl : : validate < MapPair < K , std : : pair < T , Version > > > ( root , at , NULL , NULL , count , height ) ;
if ( height > 100 )
2018-06-09 02:11:08 +08:00
TraceEvent ( SevWarnAlways , " DiabolicalPTreeSize " ) . detail ( " Size " , count ) . detail ( " Height " , height ) ;
2017-05-26 04:48:44 +08:00
}
private :
Tree root ;
Version at ;
} ;
ViewAtVersion at ( Version v ) const { return ViewAtVersion ( getRoot ( v ) , v ) ; }
ViewAtVersion atLatest ( ) const { return ViewAtVersion ( * latestRoot , latestVersion ) ; }
// TODO: getHistory?
} ;
# endif