oids.nim 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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 is thus binary compatible with a MongoDB OID.
  13. ##
  14. ## This implementation calls `initRand()` for the first call of
  15. ## `genOid`.
  16. import hashes, times, endians, random
  17. from std/private/decode_helpers import handleHexChar
  18. type
  19. Oid* = object ## An OID.
  20. time: int32
  21. fuzz: int32
  22. count: int32
  23. proc `==`*(oid1: Oid, oid2: Oid): bool {.inline.} =
  24. ## Compares two OIDs for equality.
  25. result = (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and
  26. (oid1.count == oid2.count)
  27. proc hash*(oid: Oid): Hash =
  28. ## Generates the hash of an OID for use in hashtables.
  29. var h: Hash = 0
  30. h = h !& hash(oid.time)
  31. h = h !& hash(oid.fuzz)
  32. h = h !& hash(oid.count)
  33. result = !$h
  34. proc hexbyte*(hex: char): int {.inline.} =
  35. result = handleHexChar(hex)
  36. proc parseOid*(str: cstring): Oid =
  37. ## Parses an OID.
  38. var bytes = cast[cstring](addr(result.time))
  39. var i = 0
  40. while i < 12:
  41. bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1]))
  42. inc(i)
  43. template toStringImpl[T: string | cstring](result: var T, oid: Oid) =
  44. ## Stringifies `oid`.
  45. const hex = "0123456789abcdef"
  46. const N = 24
  47. when T is string:
  48. result.setLen N
  49. var o = oid
  50. var bytes = cast[cstring](addr(o))
  51. var i = 0
  52. while i < 12:
  53. let b = bytes[i].ord
  54. result[2 * i] = hex[(b and 0xF0) shr 4]
  55. result[2 * i + 1] = hex[b and 0xF]
  56. inc(i)
  57. when T is cstring:
  58. result[N] = '\0'
  59. proc oidToString*(oid: Oid, str: cstring) {.deprecated: "unsafe; use `$`".} =
  60. ## Converts an oid to a string which must have space allocated for 25 elements.
  61. # work around a compiler bug:
  62. var str = str
  63. toStringImpl(str, oid)
  64. proc `$`*(oid: Oid): string =
  65. ## Converts an OID to a string.
  66. toStringImpl(result, oid)
  67. let
  68. t = getTime().toUnix.int32
  69. var
  70. seed = initRand(t)
  71. incr: int = seed.rand(int.high)
  72. let fuzz = cast[int32](seed.rand(high(int)))
  73. template genOid(result: var Oid, incr: var int, fuzz: int32) =
  74. var time = getTime().toUnix.int32
  75. var i = cast[int32](atomicInc(incr))
  76. bigEndian32(addr result.time, addr(time))
  77. result.fuzz = fuzz
  78. bigEndian32(addr result.count, addr(i))
  79. proc genOid*(): Oid =
  80. ## Generates a new OID.
  81. runnableExamples:
  82. doAssert ($genOid()).len == 24
  83. runnableExamples("-r:off"):
  84. echo $genOid() # for example, "5fc7f546ddbbc84800006aaf"
  85. genOid(result, incr, fuzz)
  86. proc generatedTime*(oid: Oid): Time =
  87. ## Returns the generated timestamp of the OID.
  88. var tmp: int32
  89. var dummy = oid.time
  90. bigEndian32(addr(tmp), addr(dummy))
  91. result = fromUnix(tmp)