| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 |
- # The contents of this file are subject to the BitTorrent Open Source License
- # Version 1.1 (the License). You may not copy or use this file, in either
- # source code or executable form, except in compliance with the License. You
- # may obtain a copy of the License at http://www.bittorrent.com/license/.
- #
- # Software distributed under the License is distributed on an AS IS basis,
- # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- # for the specific language governing rights and limitations under the
- # License.
- from sha import sha
- from random import randint
- #this is ugly, hopefully os.entropy will be in 2.4
- try:
- from entropy import entropy
- except ImportError:
- def entropy(n):
- s = ''
- for i in range(n):
- s += chr(randint(0,255))
- return s
- def intify(hstr):
- """20 bit hash, big-endian -> long python integer"""
- assert len(hstr) == 20
- return long(hstr.encode('hex'), 16)
- def stringify(num):
- """long int -> 20-character string"""
- str = hex(num)[2:]
- if str[-1] == 'L':
- str = str[:-1]
- if len(str) % 2 != 0:
- str = '0' + str
- str = str.decode('hex')
- return (20 - len(str)) *'\x00' + str
-
- def distance(a, b):
- """distance between two 160-bit hashes expressed as 20-character strings"""
- return intify(a) ^ intify(b)
- def newID():
- """returns a new pseudorandom globally unique ID string"""
- h = sha()
- h.update(entropy(20))
- return h.digest()
- def newIDInRange(min, max):
- return stringify(randRange(min,max))
-
- def randRange(min, max):
- return min + intify(newID()) % (max - min)
-
- def newTID():
- return randRange(-2**30, 2**30)
- ### Test Cases ###
- import unittest
- class NewID(unittest.TestCase):
- def testLength(self):
- self.assertEqual(len(newID()), 20)
- def testHundreds(self):
- for x in xrange(100):
- self.testLength
- class Intify(unittest.TestCase):
- known = [('\0' * 20, 0),
- ('\xff' * 20, 2L**160 - 1),
- ]
- def testKnown(self):
- for str, value in self.known:
- self.assertEqual(intify(str), value)
- def testEndianessOnce(self):
- h = newID()
- while h[-1] == '\xff':
- h = newID()
- k = h[:-1] + chr(ord(h[-1]) + 1)
- self.assertEqual(intify(k) - intify(h), 1)
- def testEndianessLots(self):
- for x in xrange(100):
- self.testEndianessOnce()
- class Disantance(unittest.TestCase):
- known = [
- (("\0" * 20, "\xff" * 20), 2**160L -1),
- ((sha("foo").digest(), sha("foo").digest()), 0),
- ((sha("bar").digest(), sha("bar").digest()), 0)
- ]
- def testKnown(self):
- for pair, dist in self.known:
- self.assertEqual(distance(pair[0], pair[1]), dist)
- def testCommutitive(self):
- for i in xrange(100):
- x, y, z = newID(), newID(), newID()
- self.assertEqual(distance(x,y) ^ distance(y, z), distance(x, z))
-
- class RandRange(unittest.TestCase):
- def testOnce(self):
- a = intify(newID())
- b = intify(newID())
- if a < b:
- c = randRange(a, b)
- self.assertEqual(a <= c < b, 1, "output out of range %d %d %d" % (b, c, a))
- else:
- c = randRange(b, a)
- assert b <= c < a, "output out of range %d %d %d" % (b, c, a)
- def testOneHundredTimes(self):
- for i in xrange(100):
- self.testOnce()
- if __name__ == '__main__':
- unittest.main()
-
|