batteries/identifier.lua

77 lines
2.1 KiB
Lua
Raw Normal View History

--[[
2023-01-11 13:38:52 +00:00
identifier generation
2023-01-09 19:16:29 +00:00
uuid is version 4, ulid is an alternative to uuid (see
https://github.com/ulid/spec).
todo:
this ulid isn't guaranteed to be sortable for ulids generated
within the same second yet
]]
2023-01-11 13:38:52 +00:00
local path = (...):gsub("identifier", "")
2023-01-11 13:38:52 +00:00
local identifier = {}
--(internal; use a provided random generator object, or not)
local function _random(rng, ...)
if rng then return rng:random(...) end
2023-01-09 15:25:26 +00:00
if love then return love.math.random(...) end
return math.random(...)
end
2023-01-09 15:02:32 +00:00
local uuid4_template = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
2023-01-09 19:16:29 +00:00
--generate a UUID version 4
2023-01-11 13:38:52 +00:00
function identifier.uuid4(rng)
2023-01-09 19:16:29 +00:00
--x should be 0x0-0xf, the single y should be 0x8-0xb
--4 should always just be 4 (denoting uuid version)
local out = uuid4_template:gsub("[xy]", function (c)
return string.format(
"%x",
2023-01-09 19:16:29 +00:00
c == "x" and _random(rng, 0x0, 0xf) or _random(rng, 0x8, 0xb)
)
end)
2023-01-09 15:23:11 +00:00
return out
end
2023-01-09 19:16:29 +00:00
--crockford's base32 https://en.wikipedia.org/wiki/Base32
2023-01-09 16:51:12 +00:00
local _encoding = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "M",
"N", "P", "Q", "R", "S", "T", "V", "W", "X", "Y", "Z"
}
--since ulid needs time since unix epoch with miliseconds, we can just
--use socket. if that's not loaded, they'll have to provide their own
local function _now(time_func, ...)
if package.loaded.socket then return package.loaded.socket.gettime(...) end
if pcall(require, "socket") then return require("socket").gettime(...) end
2023-01-09 16:51:12 +00:00
if time_func then return time_func(...) end
error("assertion failed: socket can't be found and no time function provided")
end
2023-01-09 19:07:50 +00:00
--generate an ULID using this rng at this time (now by default)
--implementation based on https://github.com/Tieske/ulid.lua
2023-01-11 13:38:52 +00:00
function identifier.ulid(rng, time)
2023-01-09 16:51:12 +00:00
time = math.floor((time or _now()) * 1000)
2023-01-09 19:07:50 +00:00
local time_part = {}
local random_part = {}
2023-01-09 16:51:12 +00:00
for i = 10, 1, -1 do
local mod = time % #_encoding
2023-01-09 19:07:50 +00:00
time_part[i] = _encoding[mod + 1]
2023-01-09 16:51:12 +00:00
time = (time - mod) / #_encoding
end
2023-01-09 19:02:30 +00:00
for i = 1, 16 do
2023-01-09 19:07:50 +00:00
random_part[i] = _encoding[math.floor(_random(rng) * #_encoding) + 1]
2023-01-09 16:51:12 +00:00
end
2023-01-09 19:07:50 +00:00
return table.concat(time_part) .. table.concat(random_part)
2023-01-09 16:51:12 +00:00
end
2023-01-11 13:38:52 +00:00
return identifier