ripped out xmlrpc, experimented with xmlrpc but with bencode, finally
[quix0rs-apt-p2p.git] / hash.py
diff --git a/hash.py b/hash.py
index 5f70c9bc546f6b66e249deb36d6dede13d142aa8..2a312461155d21137407223553251f1b59e04162 100644 (file)
--- a/hash.py
+++ b/hash.py
 ## Copyright 2002 Andrew Loewenstern, All Rights Reserved
 
 from sha import sha
-from whrandom import randrange
+import whrandom
+
+random = open('/dev/urandom', 'r') # sucks for windoze
 
-## takes a 20 bit hash, big-endian, and returns it expressed a python integer
-## ha ha ha ha    if this were a C module I wouldn't resort to such sillyness
 def intify(hstr):
-    assert(len(hstr) == 20)
-    i = 0L
-    i = i + ord(hstr[19])
-    i = i + ord(hstr[18]) * 256L
-    i = i + ord(hstr[17]) * 65536L
-    i = i + ord(hstr[16]) * 16777216L
-    i = i + ord(hstr[15]) * 4294967296L
-    i = i + ord(hstr[14]) * 1099511627776L
-    i = i + ord(hstr[13]) * 281474976710656L
-    i = i + ord(hstr[12]) * 72057594037927936L
-    i = i + ord(hstr[11]) * 18446744073709551616L
-    i = i + ord(hstr[10]) * 4722366482869645213696L
-    i = i + ord(hstr[9]) * 1208925819614629174706176L
-    i = i + ord(hstr[8]) * 309485009821345068724781056L
-    i = i + ord(hstr[7]) * 79228162514264337593543950336L
-    i = i + ord(hstr[6]) * 20282409603651670423947251286016L
-    i = i + ord(hstr[5]) * 5192296858534827628530496329220096L
-    i = i + ord(hstr[4]) * 1329227995784915872903807060280344576L
-    i = i + ord(hstr[3]) * 340282366920938463463374607431768211456L
-    i = i + ord(hstr[2]) * 87112285931760246646623899502532662132736L
-    i = i + ord(hstr[1]) * 22300745198530623141535718272648361505980416L
-    i = i + ord(hstr[0]) * 5708990770823839524233143877797980545530986496L
-    return i
-
-## returns the distance between two 160-bit hashes expressed as 20-character strings
+    """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)
 
 
-## returns a new pseudorandom globally unique ID string
 def newID():
+    """returns a new pseudorandom globally unique ID string"""
     h = sha()
-    for i in range(20):
-       h.update(chr(randrange(0,256)))
+    h.update(random.read(20))
     return h.digest()
 
+def newIDInRange(min, max):
+    return stringify(randRange(min,max))
+    
 def randRange(min, max):
     return min + intify(newID()) % (max - min)
+    
 
+### Test Cases ###
 import unittest
 
 class NewID(unittest.TestCase):
     def testLength(self):
-       self.assertEqual(len(newID()), 20)
+        self.assertEqual(len(newID()), 20)
     def testHundreds(self):
-       for x in xrange(100):
-           self.testLength
+        for x in xrange(100):
+            self.testLength
 
 class Intify(unittest.TestCase):
     known = [('\0' * 20, 0),
-            ('\xff' * 20, 2L**160 - 1),
-           ]
+            ('\xff' * 20, 2L**160 - 1),
+            ]
     def testKnown(self):
-       for str, value in self.known: 
-           self.assertEqual(intify(str),  value)
+        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)
+        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()
+        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)
-           ]
+            (("\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)
+        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))
-       
+        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)
+        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()
+        for i in xrange(100):
+            self.testOnce()
 
 
 
 if __name__ == '__main__':
     unittest.main()   
 
-    
\ No newline at end of file
+