oids.nim 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2013 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Nim OID support. An OID is a global ID that consists of a timestamp,
  10. ## a unique counter and a random value. This combination should suffice to
  11. ## produce a globally distributed unique ID. This implementation was extracted
  12. ## from the Mongodb interface and it thus binary compatible with a Mongo OID.
  13. ##
  14. ## This implementation calls ``math.randomize()`` for the first call of
  15. ## ``genOid``.
  16. import times, endians
  17. type
  18. Oid* = object ## an OID
  19. time: int32 ##
  20. fuzz: int32 ##
  21. count: int32 ##
  22. proc `==`*(oid1: Oid, oid2: Oid): bool =
  23. ## Compare two Mongo Object IDs for equality
  24. return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count)
  25. proc hexbyte*(hex: char): int =
  26. case hex
  27. of '0'..'9': result = (ord(hex) - ord('0'))
  28. of 'a'..'f': result = (ord(hex) - ord('a') + 10)
  29. of 'A'..'F': result = (ord(hex) - ord('A') + 10)
  30. else: discard
  31. proc parseOid*(str: cstring): Oid =
  32. ## parses an OID.
  33. var bytes = cast[cstring](addr(result.time))
  34. var i = 0
  35. while i < 12:
  36. bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
  37. inc(i)
  38. proc oidToString*(oid: Oid, str: cstring) =
  39. const hex = "0123456789abcdef"
  40. # work around a compiler bug:
  41. var str = str
  42. var o = oid
  43. var bytes = cast[cstring](addr(o))
  44. var i = 0
  45. while i < 12:
  46. let b = bytes[i].ord
  47. str[2 * i] = hex[(b and 0xF0) shr 4]
  48. str[2 * i + 1] = hex[b and 0xF]
  49. inc(i)
  50. str[24] = '\0'
  51. proc `$`*(oid: Oid): string =
  52. result = newString(24)
  53. oidToString(oid, result)
  54. var
  55. incr: int
  56. fuzz: int32
  57. proc genOid*(): Oid =
  58. ## generates a new OID.
  59. proc rand(): cint {.importc: "rand", header: "<stdlib.h>", nodecl.}
  60. proc srand(seed: cint) {.importc: "srand", header: "<stdlib.h>", nodecl.}
  61. var t = getTime().toUnix.int32
  62. var i = int32(atomicInc(incr))
  63. if fuzz == 0:
  64. # racy, but fine semantically:
  65. srand(t)
  66. fuzz = rand()
  67. bigEndian32(addr result.time, addr(t))
  68. result.fuzz = fuzz
  69. bigEndian32(addr result.count, addr(i))
  70. proc generatedTime*(oid: Oid): Time =
  71. ## returns the generated timestamp of the OID.
  72. var tmp: int32
  73. var dummy = oid.time
  74. bigEndian32(addr(tmp), addr(dummy))
  75. result = fromUnix(tmp)
  76. when not defined(testing) and isMainModule:
  77. let xo = genOid()
  78. echo xo.generatedTime