2017-05-26 04:48:44 +08:00
|
|
|
#
|
|
|
|
# set.py
|
|
|
|
#
|
|
|
|
# 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.
|
|
|
|
#
|
|
|
|
|
|
|
|
import fdb
|
|
|
|
import fdb.tuple
|
|
|
|
|
|
|
|
fdb.api_version(16)
|
|
|
|
|
2018-01-25 11:06:58 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
def nextStopToNone(gen):
|
|
|
|
try:
|
|
|
|
return gen.next()
|
|
|
|
except StopIteration:
|
|
|
|
return None
|
|
|
|
|
2018-01-25 11:06:58 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
class FdbSet (object):
|
|
|
|
def __init__(self, path):
|
|
|
|
self._path = path
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def length(self, tr):
|
|
|
|
setLength = 0
|
2018-01-25 11:06:58 +08:00
|
|
|
for k, v in tr[fdb.tuple.range((self._path,))]:
|
2017-05-26 04:48:44 +08:00
|
|
|
setLength += 1
|
|
|
|
return setLength
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def iterate(self, tr):
|
2018-01-25 11:06:58 +08:00
|
|
|
for k, v in tr[fdb.tuple.range((self._path,))]:
|
2017-05-26 04:48:44 +08:00
|
|
|
yield fdb.tuple.unpack(k)[1]
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def contains(self, tr, x):
|
|
|
|
return tr[fdb.tuple.pack((self._path, x))].present()
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def issubset(self, tr, t): # s <= t
|
|
|
|
for k, v in tr[fdb.tuple.range((self._path,))]:
|
2017-05-26 04:48:44 +08:00
|
|
|
if not t.contains(tr, fdb.tuple.unpack(k)[1]):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def issuperset(self, tr, t): # s >= t
|
2017-05-26 04:48:44 +08:00
|
|
|
return t.issubset(tr, self)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def union(self, tr, t): # s | t
|
2017-05-26 04:48:44 +08:00
|
|
|
s_gen = self.iterate(tr)
|
|
|
|
t_gen = t.iterate(tr)
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if t_key == None and s_key == None:
|
|
|
|
return
|
|
|
|
elif t_key == None or (s_key != None and s_key < t_key):
|
|
|
|
yield s_key
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
elif s_key == None or s_key > t_key:
|
|
|
|
yield t_key
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
else:
|
|
|
|
yield s_key
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def intersection(self, tr, t): # s & t
|
2017-05-26 04:48:44 +08:00
|
|
|
s_key = self.first_greater_or_equal(tr, "")
|
|
|
|
t_key = t.first_greater_or_equal(tr, "")
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if t_key == None or s_key == None:
|
|
|
|
return
|
|
|
|
elif s_key < t_key:
|
|
|
|
s_key = self.first_greater_or_equal(tr, t_key)
|
|
|
|
elif s_key > t_key:
|
|
|
|
t_key = t.first_greater_or_equal(tr, s_key)
|
|
|
|
else:
|
|
|
|
yield s_key
|
|
|
|
s_key = self.first_greater_than(tr, s_key)
|
|
|
|
t_key = t.first_greater_than(tr, t_key)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def difference(self, tr, t): # s - t
|
2017-05-26 04:48:44 +08:00
|
|
|
s_gen = self.iterate(tr)
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = t.first_greater_or_equal(tr, "")
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if s_key == None:
|
|
|
|
return
|
|
|
|
elif t_key == None or s_key < t_key:
|
|
|
|
yield s_key
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
elif s_key > t_key:
|
|
|
|
t_key = t.first_greater_or_equal(tr, s_key)
|
|
|
|
else:
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def symmetric_difference(self, tr, t): # s ^ t
|
2017-05-26 04:48:44 +08:00
|
|
|
s_gen = self.iterate(tr)
|
|
|
|
t_gen = t.iterate(tr)
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if t_key == None and s_key == None:
|
|
|
|
return
|
|
|
|
elif t_key == None or (s_key != None and s_key < t_key):
|
|
|
|
yield s_key
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
elif s_key == None or s_key > t_key:
|
|
|
|
yield t_key
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
else:
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def update(self, tr, t): # s |= t T
|
2017-05-26 04:48:44 +08:00
|
|
|
for k in t.iterate(tr):
|
|
|
|
self.add(tr, k)
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def intersection_update(self, tr, t): # s &= t
|
2017-05-26 04:48:44 +08:00
|
|
|
lastValue = fdb.tuple.pack((self._path,))
|
|
|
|
for k in self.intersection(tr, t):
|
|
|
|
if k != lastValue:
|
|
|
|
del tr[lastValue + '\x00':fdb.tuple.pack((self._path, k))]
|
|
|
|
lastValue = fdb.tuple.pack((self._path, k))
|
|
|
|
del tr[lastValue + '\x00':fdb.tuple.pack((self._path + chr(0),))]
|
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def difference_update(self, tr, t): # s -= t
|
2017-05-26 04:48:44 +08:00
|
|
|
for k in self.intersection(tr, t):
|
2018-01-25 11:06:58 +08:00
|
|
|
del tr[fdb.tuple.pack((self._path, k))]
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
@fdb.transactional
|
2018-01-25 11:06:58 +08:00
|
|
|
def symmetric_difference_update(self, tr, t): # s ^ t
|
2017-05-26 04:48:44 +08:00
|
|
|
s_gen = self.iterate(tr)
|
|
|
|
t_gen = t.iterate(tr)
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
|
|
|
|
while True:
|
|
|
|
if t_key == None and s_key == None:
|
|
|
|
return
|
|
|
|
elif t_key == None or (s_key != None and s_key < t_key):
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
elif s_key == None or s_key > t_key:
|
|
|
|
self.add(tr, t_key)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
else:
|
|
|
|
remove_key = s_key
|
|
|
|
s_key = nextStopToNone(s_gen)
|
|
|
|
t_key = nextStopToNone(t_gen)
|
|
|
|
self.remove(tr, remove_key)
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def add(self, tr, x):
|
2018-01-25 11:06:58 +08:00
|
|
|
tr[fdb.tuple.pack((self._path, x))] = ""
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def remove(self, tr, x):
|
2018-01-25 11:06:58 +08:00
|
|
|
if tr[fdb.tuple.pack((self._path, x))] == None:
|
2017-05-26 04:48:44 +08:00
|
|
|
raise KeyError
|
2018-01-25 11:06:58 +08:00
|
|
|
del tr[fdb.tuple.pack((self._path, x))]
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def discard(self, tr, x):
|
2018-01-25 11:06:58 +08:00
|
|
|
del tr[fdb.tuple.pack((self._path, x))]
|
2017-05-26 04:48:44 +08:00
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def pop(self, tr):
|
|
|
|
key = tr.get_key(fdb.KeySelector.first_greater_or_equal(self._path))
|
|
|
|
if self._keyInRange(key):
|
|
|
|
del tr[key]
|
|
|
|
return fdb.tuple.unpack(key)[1]
|
|
|
|
raise KeyError
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def clear(self, tr):
|
|
|
|
tr.clear_range_startswith(self._path)
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def first_greater_than(self, tr, x):
|
|
|
|
key = tr.get_key(fdb.KeySelector.first_greater_than(fdb.tuple.pack((self._path, x))))
|
|
|
|
if self._keyInRange(key):
|
|
|
|
return fdb.tuple.unpack(key)[1]
|
|
|
|
return None
|
|
|
|
|
|
|
|
@fdb.transactional
|
|
|
|
def first_greater_or_equal(self, tr, x):
|
|
|
|
key = tr.get_key(fdb.KeySelector.first_greater_or_equal(fdb.tuple.pack((self._path, x))))
|
|
|
|
if self._keyInRange(key):
|
|
|
|
return fdb.tuple.unpack(key)[1]
|
|
|
|
return None
|
|
|
|
|
|
|
|
def _keyInRange(self, key):
|
|
|
|
return key < fdb.tuple.pack((self._path + chr(0),))
|
|
|
|
|
2018-01-25 11:06:58 +08:00
|
|
|
|
2017-05-26 04:48:44 +08:00
|
|
|
def test(db):
|
|
|
|
print "starting set test"
|
|
|
|
tr = db.create_transaction()
|
|
|
|
|
|
|
|
del tr[:]
|
|
|
|
|
|
|
|
a = FdbSet("a")
|
|
|
|
a.add(tr, "apple")
|
|
|
|
a.add(tr, "banana")
|
|
|
|
a.add(tr, "orange")
|
|
|
|
|
|
|
|
b = FdbSet("b")
|
|
|
|
b.add(tr, "banana")
|
|
|
|
b.add(tr, "grape")
|
|
|
|
b.add(tr, "strawberry")
|
|
|
|
|
|
|
|
c = FdbSet("c")
|
|
|
|
c.add(tr, "grape")
|
|
|
|
|
|
|
|
print "set a:"
|
|
|
|
for k in a.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "set b:"
|
|
|
|
for k in b.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "set c:"
|
|
|
|
for k in c.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "b contains strawberry: {0}".format(b.contains(tr, "strawberry"))
|
|
|
|
print "remove strawberry from b"
|
|
|
|
|
|
|
|
b.remove(tr, "strawberry")
|
|
|
|
|
|
|
|
print "b contains strawberry: {0}".format(b.contains(tr, "strawberry"))
|
|
|
|
|
|
|
|
try:
|
|
|
|
b.remove(tr, "strawberry")
|
|
|
|
print "survived second remove of strawberry"
|
|
|
|
except KeyError:
|
|
|
|
print "failed second remove of strawberry"
|
|
|
|
|
|
|
|
print "insert strawberry into b"
|
|
|
|
|
|
|
|
b.add(tr, "strawberry")
|
|
|
|
|
|
|
|
print "b contains strawberry: {0}".format(b.contains(tr, "strawberry"))
|
|
|
|
|
|
|
|
print "discard strawberry from b"
|
|
|
|
|
|
|
|
b.discard(tr, "strawberry")
|
|
|
|
|
|
|
|
print "b contains strawberry: {0}".format(b.contains(tr, "strawberry"))
|
|
|
|
|
|
|
|
b.discard(tr, "strawberry")
|
|
|
|
|
|
|
|
print "b length: {0}".format(b.length(tr))
|
|
|
|
|
|
|
|
print "c issubset a: {0}".format(c.issubset(tr, a))
|
|
|
|
|
|
|
|
print "c issubset b: {0}".format(c.issubset(tr, b))
|
|
|
|
|
|
|
|
print "a union b:"
|
|
|
|
for k in a.union(tr, b):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "a intersection b:"
|
|
|
|
for k in a.intersection(tr, b):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "a difference b:"
|
|
|
|
for k in a.difference(tr, b):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "b difference a:"
|
|
|
|
for k in b.difference(tr, a):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "a symmetric_difference b:"
|
|
|
|
for k in a.symmetric_difference(tr, b):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "a update c:"
|
|
|
|
a.update(tr, c)
|
|
|
|
for k in a.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "b intersection_update a:"
|
|
|
|
b.intersection_update(tr, a)
|
|
|
|
for k in b.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "a difference_update c"
|
|
|
|
a.difference_update(tr, c)
|
|
|
|
for k in a.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "b symmetric_difference_update a"
|
|
|
|
b.symmetric_difference_update(tr, a)
|
|
|
|
for k in b.iterate(tr):
|
|
|
|
print k
|
|
|
|
|
|
|
|
print "popping items from a"
|
|
|
|
try:
|
|
|
|
while True:
|
|
|
|
print "popped {0}".format(a.pop(tr))
|
|
|
|
except KeyError:
|
|
|
|
print "finished popping"
|
|
|
|
|
|
|
|
print "a length: {0}".format(a.length(tr))
|
|
|
|
|
|
|
|
print "clearing b"
|
|
|
|
|
|
|
|
b.clear(tr)
|
|
|
|
|
|
|
|
print "b length: {0}".format(b.length(tr))
|
|
|
|
|
|
|
|
|
|
|
|
db = fdb.open()
|
|
|
|
test(db)
|