2 """Functions to deal with hashes (node IDs and keys).
4 @var HASH_LENGTH: the length of the hash to use in bytes
10 from twisted.trial import unittest
15 """Convert a hash (big-endian) to a long python integer."""
16 assert len(hstr) == HASH_LENGTH
17 return long(hstr.encode('hex'), 16)
20 """Convert a long python integer to a hash."""
26 str = str.decode('hex')
27 return (HASH_LENGTH - len(str)) *'\x00' + str
30 """Calculate the distance between two hashes expressed as strings."""
31 return intify(a) ^ intify(b)
33 def newID(suffix = ''):
34 """Get a new pseudorandom globally unique hash string.
36 @param suffix: an optional string to end the ID with
38 assert len(suffix) < 20, 'The suffix must be shorter than the SHA1 hash'
40 h.update(urandom(HASH_LENGTH))
42 return h.digest()[:-len(suffix)] + suffix
46 def newIDInRange(min, max):
47 """Get a new pseudorandom globally unique hash string in the range."""
48 return stringify(randRange(min,max))
50 def randRange(min, max):
51 """Get a new pseudorandom globally unique hash number in the range."""
52 return min + intify(newID()) % (max - min)
55 """Get a new pseudorandom transaction ID number."""
56 return randRange(-2**30, 2**30)
58 class TestNewID(unittest.TestCase):
59 """Test the newID function."""
61 self.failUnlessEqual(len(newID()), HASH_LENGTH)
62 def testHundreds(self):
67 self.failUnlessEqual(len(id), HASH_LENGTH)
68 self.failUnlessEqual(id[-4:], 'T012')
70 class TestIntify(unittest.TestCase):
71 """Test the intify function."""
72 known = [('\0' * HASH_LENGTH, 0),
73 ('\xff' * HASH_LENGTH, 2L**(HASH_LENGTH*8) - 1),
76 for str, value in self.known:
77 self.failUnlessEqual(intify(str), value)
78 def testEndianessOnce(self):
80 while h[-1] == '\xff':
82 k = h[:-1] + chr(ord(h[-1]) + 1)
83 self.failUnlessEqual(intify(k) - intify(h), 1)
84 def testEndianessLots(self):
86 self.testEndianessOnce()
88 class TestDisantance(unittest.TestCase):
89 """Test the distance function."""
91 (("\0" * HASH_LENGTH, "\xff" * HASH_LENGTH), 2L**(HASH_LENGTH*8) -1),
92 ((sha("foo").digest(), sha("foo").digest()), 0),
93 ((sha("bar").digest(), sha("bar").digest()), 0)
96 for pair, dist in self.known:
97 self.failUnlessEqual(distance(pair[0], pair[1]), dist)
98 def testCommutitive(self):
100 x, y, z = newID(), newID(), newID()
101 self.failUnlessEqual(distance(x,y) ^ distance(y, z), distance(x, z))
103 class TestRandRange(unittest.TestCase):
104 """Test the randRange function."""
110 self.failUnlessEqual(a <= c < b, True, "output out of range %d %d %d" % (b, c, a))
113 self.failUnlessEqual(b <= c < a, True, "output out of range %d %d %d" % (b, c, a))
115 def testOneHundredTimes(self):
116 for i in xrange(100):