lluuid.py

Go to the documentation of this file.
00001 """\
00002 @file lluuid.py
00003 @brief UUID parser/generator.
00004 
00005 $LicenseInfo:firstyear=2004&license=mit$
00006 
00007 Copyright (c) 2004-2008, Linden Research, Inc.
00008 
00009 Permission is hereby granted, free of charge, to any person obtaining a copy
00010 of this software and associated documentation files (the "Software"), to deal
00011 in the Software without restriction, including without limitation the rights
00012 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013 copies of the Software, and to permit persons to whom the Software is
00014 furnished to do so, subject to the following conditions:
00015 
00016 The above copyright notice and this permission notice shall be included in
00017 all copies or substantial portions of the Software.
00018 
00019 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025 THE SOFTWARE.
00026 $/LicenseInfo$
00027 """
00028 
00029 import md5, random, socket, string, time, re
00030 import uuid
00031 
00032 def _int2binstr(i,l):
00033     s=''
00034     for a in range(l):
00035         s=chr(i&0xFF)+s
00036         i>>=8
00037     return s
00038 
00039 def _binstr2int(s):
00040     i = long(0)
00041     for c in s:
00042         i = (i<<8) + ord(c)
00043     return i
00044 
00045 class UUID(object):
00046     """
00047     A class which represents a 16 byte integer. Stored as a 16 byte 8
00048     bit character string.
00049 
00050     The string version is to be of the form:
00051     AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC  (a 128-bit number in hex)
00052     where A=network address, B=timestamp, C=random.
00053     """
00054 
00055     NULL_STR = "00000000-0000-0000-0000-000000000000"
00056 
00057     # the UUIDREGEX_STRING is helpful for parsing UUID's in text
00058     hex_wildcard = r"[0-9a-fA-F]"
00059     word = hex_wildcard + r"{4,4}-"
00060     long_word = hex_wildcard + r"{8,8}-"
00061     very_long_word = hex_wildcard + r"{12,12}"
00062     UUID_REGEX_STRING = long_word + word + word + word + very_long_word
00063     uuid_regex = re.compile(UUID_REGEX_STRING)
00064 
00065     rand = random.Random()
00066     ip = ''
00067     try:
00068         ip = socket.gethostbyname(socket.gethostname())
00069     except(socket.gaierror):
00070         # no ip address, so just default to somewhere in 10.x.x.x
00071         ip = '10'
00072         for i in range(3):
00073             ip += '.' + str(rand.randrange(1,254))
00074     hexip = ''.join(["%04x" % long(i) for i in ip.split('.')])
00075     lastid = ''
00076 
00077     def __init__(self, possible_uuid=None):
00078         """
00079         Initialize to first valid UUID in argument (if a string),
00080         or to null UUID if none found or argument is not supplied.
00081 
00082         If the argument is a UUID, the constructed object will be a copy of it.
00083         """
00084         self._bits = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
00085         if possible_uuid is None:
00086             return
00087 
00088         if isinstance(possible_uuid, type(self)):
00089             self.set(possible_uuid)
00090             return
00091 
00092         uuid_match = UUID.uuid_regex.search(possible_uuid)
00093         if uuid_match:
00094             uuid_string = uuid_match.group()
00095             s = string.replace(uuid_string, '-', '')
00096             self._bits = _int2binstr(string.atol(s[:8],16),4) + \
00097                          _int2binstr(string.atol(s[8:16],16),4) + \
00098                          _int2binstr(string.atol(s[16:24],16),4) + \
00099                          _int2binstr(string.atol(s[24:],16),4) 
00100 
00101     def __len__(self):
00102         """
00103         Used by the len() builtin.
00104         """
00105         return 36
00106 
00107     def __nonzero__(self):
00108         return self._bits != "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
00109 
00110     def __str__(self):
00111         uuid_string = self.toString()
00112         return uuid_string
00113 
00114     __repr__ = __str__
00115 
00116     def __getitem__(self, index):
00117         return str(self)[index]
00118 
00119     def __eq__(self, other):
00120         if isinstance(other, (str, unicode)):
00121             return other == str(self)
00122         return self._bits == getattr(other, '_bits', '')
00123 
00124     def __ne__(self, other):
00125         return not self.__eq__(other)
00126 
00127     def __le__(self, other):
00128         return self._bits <= other._bits
00129 
00130     def __ge__(self, other):
00131         return self._bits >= other._bits
00132 
00133     def __lt__(self, other):
00134         return self._bits < other._bits
00135 
00136     def __gt__(self, other):
00137         return self._bits > other._bits
00138 
00139     def __hash__(self):
00140         return hash(self._bits)
00141 
00142     def set(self, uuid):
00143         self._bits = uuid._bits
00144 
00145     def setFromString(self, uuid_string):
00146         """
00147         Given a string version of a uuid, set self bits
00148         appropriately. Returns self.
00149         """
00150         s = string.replace(uuid_string, '-', '')
00151         self._bits = _int2binstr(string.atol(s[:8],16),4) + \
00152                      _int2binstr(string.atol(s[8:16],16),4) + \
00153                      _int2binstr(string.atol(s[16:24],16),4) + \
00154                      _int2binstr(string.atol(s[24:],16),4) 
00155         return self
00156 
00157     def setFromMemoryDump(self, gdb_string):
00158         """
00159         We expect to get gdb_string as four hex units. eg:
00160         0x147d54db              0xc34b3f1b              0x714f989b              0x0a892fd2
00161         Which will be translated to:
00162         db547d14-1b3f4bc3-9b984f71-d22f890a
00163         Returns self.
00164         """
00165         s = string.replace(gdb_string, '0x', '')
00166         s = string.replace(s, ' ', '')
00167         t = ''
00168         for i in range(8,40,8):
00169             for j in range(0,8,2):
00170                 t = t + s[i-j-2:i-j]
00171         self.setFromString(t)
00172 
00173     def toString(self):
00174         """
00175         Return as a string matching the LL standard
00176         AAAAAAAA-AAAA-BBBB-BBBB-BBBBBBCCCCCC  (a 128-bit number in hex)
00177         where A=network address, B=timestamp, C=random.
00178         """
00179         return uuid_bits_to_string(self._bits)
00180 
00181     def getAsString(self):
00182         """
00183         Return a different string representation of the form
00184         AAAAAAAA-AAAABBBB-BBBBBBBB-BBCCCCCC      (a 128-bit number in hex)
00185         where A=network address, B=timestamp, C=random.
00186         """
00187         i1 = _binstr2int(self._bits[0:4])
00188         i2 = _binstr2int(self._bits[4:8])
00189         i3 = _binstr2int(self._bits[8:12])
00190         i4 = _binstr2int(self._bits[12:16])
00191         return '%08lx-%08lx-%08lx-%08lx' % (i1,i2,i3,i4)
00192 
00193     def generate(self):
00194         """
00195         Generate a new uuid. This algorithm is slightly different
00196         from c++ implementation for portability reasons.
00197         Returns self.
00198         """
00199         m = md5.new()
00200         m.update(uuid.uuid1().bytes)
00201         self._bits = m.digest()
00202         return self
00203 
00204     def isNull(self):
00205         """
00206         Returns 1 if the uuid is null - ie, equal to default uuid.
00207         """
00208         return (self._bits == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
00209 
00210     def xor(self, rhs):
00211         """
00212         xors self with rhs.
00213         """
00214         v1 = _binstr2int(self._bits[0:4]) ^ _binstr2int(rhs._bits[0:4])
00215         v2 = _binstr2int(self._bits[4:8]) ^ _binstr2int(rhs._bits[4:8])
00216         v3 = _binstr2int(self._bits[8:12]) ^ _binstr2int(rhs._bits[8:12])
00217         v4 = _binstr2int(self._bits[12:16]) ^ _binstr2int(rhs._bits[12:16])
00218         self._bits = _int2binstr(v1,4) + \
00219                      _int2binstr(v2,4) + \
00220                      _int2binstr(v3,4) + \
00221                      _int2binstr(v4,4) 
00222 
00223 
00224 # module-level null constant
00225 NULL = UUID()
00226 
00227 def printTranslatedMemory(four_hex_uints):
00228     """
00229     We expect to get the string as four hex units. eg:
00230     0x147d54db          0xc34b3f1b              0x714f989b              0x0a892fd2
00231     Which will be translated to:
00232     db547d14-1b3f4bc3-9b984f71-d22f890a
00233     """
00234     uuid = UUID()
00235     uuid.setFromMemoryDump(four_hex_uints)
00236     print uuid.toString()
00237 
00238 def isUUID(id_str):
00239     """
00240     This function returns:
00241     - 1 if the string passed is a UUID
00242     - 0 is the string passed is not a UUID
00243     - None if it neither of the if's below is satisfied
00244     """
00245     if not id_str or len(id_str) <  5 or len(id_str) > 36:
00246         return 0
00247 
00248     if isinstance(id_str, UUID) or UUID.uuid_regex.match(id_str):
00249         return 1
00250 
00251     return None
00252 
00253 def isPossiblyID(id_str):
00254     """
00255     This function returns 1 if the string passed has some uuid-like
00256     characteristics. Otherwise returns 0.
00257     """
00258 
00259     is_uuid = isUUID(id_str)
00260     if is_uuid is not None:
00261         return is_uuid
00262 
00263     # build a string which matches every character.
00264     hex_wildcard = r"[0-9a-fA-F]"
00265     chars = len(id_str)
00266     next = min(chars, 8)
00267     matcher = hex_wildcard+"{"+str(next)+","+str(next)+"}"
00268     chars = chars - next
00269     if chars > 0:
00270         matcher = matcher + "-"
00271         chars = chars - 1
00272     for block in range(3):
00273         next = max(min(chars, 4), 0)
00274         if next:
00275             matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}"
00276             chars = chars - next
00277         if chars > 0:
00278             matcher = matcher + "-"
00279             chars = chars - 1
00280     if chars > 0:
00281         next = min(chars, 12)
00282         matcher = matcher + hex_wildcard+"{"+str(next)+","+str(next)+"}"
00283     #print matcher
00284     uuid_matcher = re.compile(matcher)
00285     if uuid_matcher.match(id_str):
00286         return 1
00287     return 0
00288 
00289 def uuid_bits_to_string(bits):
00290     i1 = _binstr2int(bits[0:4])
00291     i2 = _binstr2int(bits[4:6])
00292     i3 = _binstr2int(bits[6:8])
00293     i4 = _binstr2int(bits[8:10])
00294     i5 = _binstr2int(bits[10:12])
00295     i6 = _binstr2int(bits[12:16])
00296     return '%08lx-%04lx-%04lx-%04lx-%04lx%08lx' % (i1,i2,i3,i4,i5,i6)
00297 
00298 def uuid_bits_to_uuid(bits):
00299     return UUID(uuid_bits_to_string(bits))
00300 
00301 
00302 try:
00303     from mulib import stacked
00304     stacked.NoProducer()  # just to exercise stacked
00305 except:
00306     #print "Couldn't import mulib.stacked, not registering UUID converter"
00307     pass
00308 else:
00309     def convertUUID(uuid, req):
00310         req.write(str(uuid))
00311 
00312     stacked.add_producer(UUID, convertUUID, "*/*")
00313     stacked.add_producer(UUID, convertUUID, "text/html")

Generated on Fri May 16 08:31:53 2008 for SecondLife by  doxygen 1.5.5