import types, string, BufferFile, BplusTreeBytes, BplusTreeLong from BplusTreeLong import BplusTreeException, BplusTreeBadKeyValue, BplusTreeKeyMissing, ENCODER, DECODER def Initialize(treefilename, blockfilename, KeyLength, CultureId=BplusTreeLong.INVARIANTCULTUREID, nodesize=BplusTreeBytes.DEFAULTNODESIZE, buffersize=BplusTreeBytes.DEFAULTBLOCKSIZE): return BplusTreeBytes.Initialize(treefilename, blockfilename, KeyLength, CultureId,nodesize,buffersize, initFunction=BplusTree) def ReOpen(treefilename, blockfilename, access="r+b"): return BplusTreeBytes.ReOpen(treefilename, blockfilename, access, initFunction=xBplusTreeBytes) def ReadOnly(treefilename, blockfilename): return BplusTreeBytes.ReadOnly(treefilename, blockfilename, initFunction=xBplusTreeBytes) class xBplusTreeBytes(BplusTreeBytes.BplusTreeBytes): BucketSizeLimit = -1 ByteGet = BplusTreeBytes.BplusTreeBytes.Get ByteSet = BplusTreeBytes.BplusTreeBytes.Set ByteRemove = BplusTreeBytes.BplusTreeBytes.RemoveKey ByteFirstKey = BplusTreeBytes.BplusTreeBytes.FirstKey ByteNextKey = BplusTreeBytes.BplusTreeBytes.NextKey MaxPrefixLength = None # use MaxKeyLength in place of prefix length def LimitBucketSize(this, limit): this.BucketSizeLimit = limit def PrefixForByteCount(this, s, maxbytecount): #print "maxbytecount is", maxbytecount if len(s)<1: return u""; (bytes, count) = ENCODER(s) while len(bytes)>maxbytecount: s = s[:-1] (bytes, count) = ENCODER(s) return s def FindBucketForPrefix(this, key, keyIsPrefix): "returns (bucket or None, prefix)" bucket = None prefix = key M = this.MaxPrefixLength if M is None: M = this.MaxPrefixLength = this.MaxKeyLen() if not keyIsPrefix: prefix = this.PrefixForByteCount(key, M) bytes = this.ByteGet(prefix, None) if bytes is not None: bucket = xBucket(this) bucket.Load(bytes) if (bucket.Count()<1): raise BplusTreeException, "empty bucket loaded" return (bucket, prefix) def RemoveKey(this, key): (bucket, prefix) = this.FindBucketForPrefix(key, False) if bucket is None: raise BplusTreeKeyMissing, "no such key to delete "+repr(key) bucket.Remove(key) if bucket.Count()<1: this.ByteRemove(prefix) else: this.ByteSet(prefix, bucket.dump()) def FirstKey(this): prefix = this.ByteFirstKey() if prefix is None: return None (bucket, dummy) = this.FindBucketForPrefix(prefix, True) if bucket is None: raise BplusTreeException, "byte tree gave bad first prefix" return bucket.FirstKey() def NextKey(this, AfterThisKey): (bucket, prefix) = this.FindBucketForPrefix(AfterThisKey, False) if bucket is not None: result = bucket.NextKey(AfterThisKey) if result is not None: return result # otherwise look in next bucket nextprefix = this.ByteNextKey(prefix) if nextprefix is None: return None (bucket, dummy) = this.FindBucketForPrefix(nextprefix, True) return bucket.FirstKey() def ContainsKey(this, key): (bucket, prefix) = this.FindBucketForPrefix(key, False) if bucket is None: return False test = bucket.Find(key) if test is None: return False return True def Get(this, key, defaultValue): (bucket, prefix) = this.FindBucketForPrefix(key, False) if bucket is not None: test = bucket.Find(key) if test is not None: return test return defaultValue def __getitem__(this, key): test = this.Get(key, None) if test is None: raise BplusTreeKeyMissing, "key not found "+key return test def __setitem__(this, key, value): (bucket, prefix) = this.FindBucketForPrefix(key, False) if bucket is None: bucket = xBucket(this) bucket.Add(key, value) this.ByteSet(prefix, bucket.dump()) Set = __setitem__ class xBucket: "bucket for elements with same prefix. Intended for small buckets" def __init__(this, owner): # owner not needed for python implementation? this.BucketSizeLimit = owner.BucketSizeLimit this.MaxKeyLen = owner.MaxKeyLen() this.values = [] this.keys = [] def Count(this): return len(this.keys) def Load(this, serialization): keys = this.keys values = this.values if len(keys)>0: raise BplusTreeException, "cannot load into non-empty bucket" index = 0 bytecount = len(serialization) while index<bytecount: keylength = BufferFile.RetrieveInt(serialization, index) index += BufferFile.INTSTORAGE nextindex = index+keylength keybytes = serialization[index:nextindex] index = nextindex (key, dummy) = DECODER(keybytes) valuelength = BufferFile.RetrieveInt(serialization, index) index += BufferFile.INTSTORAGE nextindex = index+valuelength valuebytes = serialization[index:nextindex] (value, dummy) = DECODER(valuebytes) index = nextindex keys.append(key) values.append(value) if index!=bytecount: raise BplusTreeException, "error counting bytes "+repr((index,bytecount)) def dump(this): allbytes = [] keys = this.keys values = this.values for index in xrange(len(this.keys)): thisKey = keys[index] thisValue = this.values[index] keyPrefix = BufferFile.StoreInt(len(thisKey)) (keybytes, dummy) = ENCODER(thisKey) allbytes.append(keyPrefix) allbytes.append(keybytes) valuePrefix = BufferFile.StoreInt(len(thisValue)) (valuebytes, dummy) = ENCODER(thisValue) allbytes.append(valuePrefix) allbytes.append(valuebytes) #print allbytes return string.join(allbytes, "") def Add(this, key, map): keys = this.keys values = this.values index = 0 limit = this.BucketSizeLimit maxlen = len(this.keys) while index<maxlen: thiskey = keys[index] thisvalue = values[index] if thiskey==key: keys[index] = key value[index] = map return if thiskey>key: values.insert(index, map) keys.insert(index, key) if limit>0 and len(this.keys)>limit: raise BplusTreeException, "bucket size limit exceeded" return index += 1 keys.append(key) values.append(map) if limit>0 and len(this.keys)>limit: raise BplusTreeException, "bucket size limit exceeded" def Remove(this, key): keys = this.keys values = this.values index = 0 maxlen = len(this.keys) while index<maxlen: thiskey = keys[index] if thiskey==key: del values[index] del keys[index] return index+=1 raise BplusTreeKeyMissing, "no such key to delete "+repr(key) def Find(this, key): "return value found or None" keys = this.keys values = this.values index = 0 maxlen = len(this.keys) while index<maxlen: thiskey = this.keys[index] if thiskey==key: return this.values[index] index+=1 return None def FirstKey(this): if len(this.keys)<1: return None return this.keys[0] def NextKey(this, AfterThisKey): index = 0 keys = this.keys maxlen = len(keys) while index<maxlen: thiskey = keys[index] if thiskey>AfterThisKey: return thiskey index+=1 return None