Skip to content

Instantly share code, notes, and snippets.

@fowlmouth
Last active August 29, 2015 14:24
Show Gist options
  • Save fowlmouth/74d53453aff4fc057b1f to your computer and use it in GitHub Desktop.
Save fowlmouth/74d53453aff4fc057b1f to your computer and use it in GitHub Desktop.
import strutils,times
import unsigned
type
UIDT* = uint64
UID* = distinct UIDT
proc sh (s: UID|UIDT|uint8): string =
"0x"&
toUpper(toHex(s.BiggestInt, sizeof(s)*2))
type
IDgen = object
epoch: Time
idCount: int8
ids: array[8,(uint8,uint8)]
idState: uint64 # array[8,uint64]
prepNext*: proc(id:var IDgen)
template idSize* (g:IDgen; st:SomeInteger): uint8 =
g.ids[st][0]
template idOffs* (g:IDgen; st:SomeInteger): uint8 =
g.ids[st][1]
template maskBits(x:expr): expr =
not(not(0u64) shl x.uint64)
template maskComponent (g:IDgen; st:SomeInteger): uint64 =
maskBits(g.idSize(st)) shl g.idOffs(st)
proc validState* (g:IDgen; st:int; val:uint64): bool =
(val and not maskBits(g.idSize(st))) == 0
proc setState* (g:IDgen; st:int; val:uint64; id:UIDT): (bool,UIDT) =
result[0] = g.validState(st,val)
if not result[0]: return
let offs = g.idOffs(st).uint64
result[1] = (id and not(g.maskComponent(st)))
result[1] = result[1] or
(val shl offs)
proc setState* (g:var IDgen; st:int; val:uint64): bool =
let (has,res) = g.setState(st,val, g.idState)
if has:
g.idState = res
return true
proc getState* (g:IDgen; st:int; id:UIDT): uint64 =
let offs = g.idOffs(st).uint64
result =
(id shr offs) and maskBits(g.idSize(st))
proc getState* (g:var IDgen; st:int): uint64 =
g.getState(st, g.idState)
proc initGen* (idSizes: openarray[int8]; epoch = getTime()): IDgen =
assert idSizes.len > 0
assert idSizes.len < 9
result.epoch = epoch
result.idCount = idSizes.len.int8
var bits = 0
for idx,i in idSizes.pairs:
assert i > 0
assert bits < 64
result.idSize(idx) = i.uint8
result.idOffs(idx) = bits.uint8
bits += i.int
# if bits > 64:
# raise newException(EIO, "too many bits "& $bits)
proc next* (g: var IDgen): UID =
g.prepNext(g)
result = g.idState.UID
#echo "next: ", sh(result)
proc showState* (g:var IDgen) =
for i in 0 .. < g.idCount:
echo "reg ", i, " = ", g.getState(i)
echo " ", sh(maskBits(g.idSize(i)) shl g.idOffs(i)),
" (", sh(g.getState(i) shl g.idOffs(i)), ")"
discard
proc breakdown* (g:var IDgen; id:UID): seq[UIDT] =
newSeq result, g.idCount
for idx in 0 .. < g.idCount:
result[idx] = g.getState(idx, id.UIDT)
when isMainModule:
import os
var basicflake_layout =
@[16i8, 16i8, 32i8]
let
counter_state = 0
machine_id_state = 1
timestamp_state = 2
var basicflake = initGen(basicflake_layout)
# middle state used for worker ID
assert basicflake.idSize(counter_state) == 16
assert basicflake.idSize(machine_id_state) == 16
assert basicflake.idSize(timestamp_state) == 32
assert basicflake.idOffs(counter_state) == 0
assert basicflake.idOffs(machine_id_state) == 16
assert basicflake.idOffs(timestamp_state) == 32
echo sh(basicflake.maskComponent(counter_state))
echo sh(basicflake.maskComponent(1))
echo sh(basicflake.maskComponent(timestamp_state))
assert basicflake.setState(machine_id_state, 15)
basicflake.showState
echo "---"
# setup its tick function
basicflake.prepNext = proc(g: var IDgen) =
let t = uint64(getTime() - g.epoch)
echo t
if t != g.getState(timestamp_state):
# reset the counter
assert g.setState(counter_state,0)
assert g.setState(timestamp_state,t)
else:
assert g.setState(counter_state, g.getState(counter_state) + 1)
var ids: seq[UID]
ids = @[]
for i in 0 .. <30:
if i mod 7 == 0:
sleep 850
let id = basicflake.next
ids.add id
for id in ids:
echo basicflake.breakdown(id)
echo sizeof IDgen
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment