guid.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 
3 """
4 usage: %(progname)s [args]
5 """
6 
7 # GUID.py
8 # Version 2.1.
9 #
10 # Copyright (C) 2003 Dr. Conan C. Albrecht <conan_albrechtATbyu.edu>
11 #
12 # This library is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU Lesser General Public
14 # License as published by the Free Software Foundation; either
15 # version 2.1 of the License, or (at your option) any later version.
16 #
17 # This library is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 # Lesser General Public License for more details.
21 #
22 # You should have received a copy of the GNU Lesser General Public
23 # License along with this library; if not, write to the Free Software
24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 
26 
27 
28 ##################################################################################################
29 ### A globally-unique identifier made up of time and ip and 8 random digits: 16 characters wide
30 ###
31 ### A globally unique identifier that combines ip, time, and random bits. Since the
32 ### time is listed first, you can sort records by guid. You can also extract the time
33 ### and ip if needed.
34 ###
35 ### GUIDs make wonderful database keys. They require no access to the
36 ### database (to get the max index number), they are extremely unique, and they sort
37 ### automatically by time. GUIDs prevent key clashes when merging
38 ### two databases together, combining data, or generating keys in distributed
39 ### systems.
40 ###
41 ### There is an Internet Draft for UUIDs, but this module does not implement it.
42 ### If the draft catches on, perhaps I'll conform the module to it.
43 ###
44 
45 
46 # Changelog
47 # Sometime, 1997 Created the Java version of GUID
48 # Went through many versions in Java
49 # Sometime, 2002 Created the Python version of GUID, mirroring the Java version
50 # November 24, 2003 Changed Python version to be more pythonic, took out object and made just a module
51 # December 2, 2003 Fixed duplicating GUIDs. Sometimes they duplicate if multiples are created
52 # in the same millisecond (it checks the last 100 GUIDs now and has a larger random part)
53 # December 9, 2003 Fixed MAX_RANDOM, which was going over sys.maxint
54 # November 21, 2005 Changed to pack the guid using 64 bit encoding 0-9, A-Z, a-z, @, [
55 # Now, the guid is only 16 bytes long.
56 #
57 
58 import math
59 import random
60 import socket
61 import os, sys
62 import time
63 import threading
64 import string, struct
65 
66 import getopt
67 
68 # The size of the circular queue. Larger sizes give more assurance for uniqueness.
69 # Smaller sizes take less memory and are a tiny bit faster
70 QUEUE_SIZE = 100
71 
72 
73 #############################
74 ### global module variables
75 
76 MAX_RANDOM = sys.maxint # converted to hex goes to 8 chars (at least, in Python 2.3)
77 rand = random.Random()
78 ip = ''
79 lock = threading.RLock()
80 lastguid = ''
81 try:
82  ip = socket.gethostbyname(socket.gethostname())
83 except (socket.gaierror): # if we don't have an ip, default to someting in the 10.x.x.x private range
84  ip = '10'
85  for i in range(3):
86  ip += '.' + str(rand.randrange(1, 254))
87 hexip = ''.join(["%04x" % long(i) for i in ip.split('.')]) # leave space for ip v6 (65K in each sub)
88 ipaddrStr = socket.inet_aton(ip)
89 (ipaddr, ) = struct.unpack(">I", ipaddrStr)
90 
91 def encode64Char(i):
92  if i<10: return chr(i+48)
93  elif i<38: return chr(i-10+64)
94  elif i<64: return chr(i-38+97)
95  raise Error
96 
97 def pack64(i, bytes=6):
98  parts = []
99  for j in range(bytes):
100  a = i & 0x3f
101  i = (i >> 6)
102  parts.append(encode64Char(a))
103  parts.reverse()
104  p = string.join(parts, "")
105  return p
106 
108  n = ord(c)
109  if n>=48 and n<=58:
110  return n-48
111  elif n >= 64 and n <= 91:
112  return n - 54
113  elif n >= 97 and n <= 122:
114  return n - 59
115  raise Error
116 
117 def unpack64(s, bytes=6):
118  i = 0L
119 
120  for j in range(bytes):
121  c = s[j]
122  a = decode64Char(c)
123  i = i | a
124  if j < bytes-1:
125  i = i << 6
126  return i
127 
128 if 0:
129  for i in range(64):
132  if i != j:
133  print i, c, j
134 
135 ipaddr64 = pack64(ipaddr)
136 
137 #######################################
138 ### A simple circular set
139 ### to ensure we don't duplicate
140 ### GUIDs in the same millisecond
141 
143  '''A circular set. A set that maxes at a given size, replacing the oldest element after maximum size.
144  This implementation is NOT thread safe. (generate() below is thread safe, though)
145  '''
146  def __init__(self):
147  self.queue = []
148  self.queue_map = {} # for efficiency, we keep a map of everything
149  self.queueindex = 0
150 
151  def add(self, val):
152  '''Adds a value to the queue'''
153  # check to see if we have this value. If so, throw an exception
154  assert not self.queue_map.has_key(val), 'This value is already in the set!'
155 
156  # add the new one to the list
157  if len(self.queue) > self.queueindex:
158  # first remove the previous key at this location
159  del self.queue_map[self.queue[self.queueindex]]
160  self.queue[self.queueindex] = val
161  else:
162  self.queue.append(val)
163 
164  # now add to the map for efficiency
165  self.queue_map[val] = val
166 
167  # increment the queue index
168  self.queueindex += 1
169  if self.queueindex >= QUEUE_SIZE:
170  self.queueindex = 0
171 
172 queue = CircularSet()
173 
174 #################################
175 ### Public module functions
176 
177 
178 def generate(time_t = None):
179  '''Generates a new guid'''
180  global lock, queue # since we modify the module variable
181  if time_t > 1136102400:
182  raise ValueError, "time_t is too large %s" % time_t
183 
184  try:
185  lock.acquire() # can't generate two guids at the same time
186  while 1:
187  # time part
188  if time_t == None:
189  t = long(time.time() * 100)
190  else:
191  t = time_t * 100
192 
193  # random part
194  r = int(rand.random() * MAX_RANDOM) & 0xffff
195  n = 0L | (long(t)<<48) | (ipaddr<<16) | r
196  guid = pack64(n, bytes=16)
197 
198  try:
199  queue.add(guid) # throws the AssertionError if this GUID is a duplicate of the queue
200  return guid
201  except AssertionError: # signals we already have this GUID in the queue
202  pass
203  finally:
204  lock.release()
205 
206 InvalidGUID = "Invalid GUID"
207 
208 def extract_time(guid):
209  '''Extracts the time portion out of the guid and returns the
210  number of seconds since the epoch as a float'''
211  if len(guid) != 16: raise InvalidGUID
212  n = unpack64(guid, bytes=16)
213  t = long(n >> 48) / 100
214  return t
215 
216 
217 def extract_ip(guid):
218  '''Extracts the ip portion out of the guid and returns it
219  as a string like 10.10.10.10'''
220 
221  if len(guid) != 16: raise InvalidGUID
222 
223  n = unpack64(guid, bytes=16)
224  n = n >> 16
225  n = n & 0xffffffffL
226 
227  ip = struct.pack(">L", n)
228  ipaddrStr = socket.inet_ntoa(ip)
229  return ipaddrStr
230 
231 
232 def extract_random(guid):
233  '''Extracts the random bits from the guid (returns the bits in decimal)'''
234  if len(guid) != 16: raise InvalidGUID
235 
236  n = unpack64(guid, bytes=16)
237  r = int(n & 0xffff)
238  return r
239 
240 
241 ### TESTING OF GUID CLASS ###
242 def test():
243  pass
244 
245 def usage(progname):
246  print __doc__ % vars()
247 
248 def main(argv, stdout, environ):
249  progname = argv[0]
250  optlist, args = getopt.getopt(argv[1:], "", ["help", "test", "debug"])
251 
252  testflag = 0
253 
254  for (field, val) in optlist:
255  if field == "--help":
256  usage(progname)
257  return
258  elif field == "--debug":
259  debugfull()
260  elif field == "--test":
261  testflag = 1
262 
263  if testflag:
264  test()
265  return
266 
267  guids = []
268  if len(args) == 0:
269  guids.append(generate())
270  else:
271  for arg in args:
272  guids.append(arg)
273 
274  for guid in guids:
275  print "GUID:", guid
276  print "Time:", time.strftime('%a, %d %b %Y %H:%M:%S', time.localtime(extract_time(guid)))
277  print "IP: ", extract_ip(guid)
278  print "Rand:", extract_random(guid)
279  print
280 
281 
282 if __name__ == "__main__":
283  main(sys.argv, sys.stdout, os.environ)
284 
285 
def encode64Char(i)
Definition: guid.py:91
def main(argv, stdout, environ)
Definition: guid.py:248
def extract_time(guid)
Definition: guid.py:208
def pack64(i, bytes=6)
Definition: guid.py:97
def test()
TESTING OF GUID CLASS ###.
Definition: guid.py:242
def debugfull()
Definition: log.py:120
def generate(time_t=None)
Public module functions.
Definition: guid.py:178
A simple circular set to ensure we don&#39;t duplicate GUIDs in the same millisecond. ...
Definition: guid.py:142
def extract_random(guid)
Definition: guid.py:232
def add(self, val)
Definition: guid.py:151
def extract_ip(guid)
Definition: guid.py:217
def decode64Char(c)
Definition: guid.py:107
def unpack64(s, bytes=6)
Definition: guid.py:117
def usage(progname)
Definition: guid.py:245


pyclearsilver
Author(s): Scott Noob Hassan
autogenerated on Mon Jun 10 2019 15:51:13