delete uuid.lua redis.lua upload.lua

This commit is contained in:
李勇
2021-08-07 15:58:24 +08:00
parent 8802cbb8f8
commit f7586cee44
8 changed files with 86 additions and 1072 deletions
+19 -21
View File
@@ -25,19 +25,27 @@
return 200 "hello world! ";
}
# Upload dir
location /append_file_output {
default_type "";
alias /file_upload;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
# Upload image file
location /upload_image {
content_by_lua_file /usr/local/openresty/resty_funcs/upload_image.lua;
}
#Image append
location /image_append {
content_by_lua_file /usr/local/openresty/nginx/conf/lua/image_append.lua;
}
```
- 安装opm
```bash
sudo dnf -f install dnf -y install openresty-opm
```
- 安装 lua-resty-jit-uuid https://github.com/thibaultcha/lua-resty-jit-uuid
```bash
opm get thibaultcha/lua-resty-jit-uuid
```
- 添加lua脚本
@@ -47,10 +55,7 @@
```bash
/usr/local/openresty/resty_funcs
.
── redis.lua
├── upload_image.lua
├── upload.lua
└── uuid.lua
── upload_image.lua
```
- 重启服务
@@ -187,15 +192,8 @@
-rw-rw-rw- 1 nobody nobody 99699 7月 21 22:25 9ec1a383-9678-4c16-834f-57b4d7f87a1d.jpg
```
- 浏览器查看
![image](https://github.com/alchemy-studio/resty_functions/blob/master/file_upload/image-20210722200549281.png)
https://music-room.alchemy-studio.cn/append_file_output/6143c43c-e2ab-4002-b391-9f82f6008931.jpg
![image](https://github.com/alchemy-studio/resty_functions/blob/master/file_upload/image-20210722200812133.png)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

+58
View File
@@ -0,0 +1,58 @@
local uuid = require "resty.jit-uuid"
local cjson = require "cjson"
local json_string = nil
---- Automatic seeding with os.time(), LuaSocket, or ngx.time()
uuid.seed()
uuid()
-- Get post json argv
local arg = ngx.req.get_post_args()
for k,v in pairs(arg) do
ngx.log(ngx.INFO , "Image append [POST] key:", k .." | ".." v:", v)
json_string = v
end
-- Decode json string
local json = cjson.decode(json_string)
ngx.say("Image append input filename number is :" , table.getn(json.data))
local image_append_input_files_number = table.getn(json.data)
for i = 1, image_append_input_files_number do
ngx.log(ngx.INFO , "Image append input filename is :" , json.data[i])
end
-- Image append
---- Upload file save path config in nginx.conf
local save_upload_file_path = ngx.var.store_dir
local append_image_table = {}
local output_image_uuid = uuid.generate_v4();
-- Add path to input image filename
for i = 1, image_append_input_files_number do
append_image_table[i] = save_upload_file_path .. json.data[i] .. " "
ngx.log(ngx.INFO, "Insert append image table "..i.." with "..append_image_table[i])
end
-- Concat input image to a command string
local append_input_images = table.concat(append_image_table)
ngx.log(ngx.INFO , "Input images list : " , append_input_images)
-- Add path to output image filename
local append_output_image = save_upload_file_path .. output_image_uuid..".jpg ";
ngx.log(ngx.INFO , "Output image is : "..append_output_image)
-- Debug
ngx.log(ngx.INFO , '/usr/local/ImageMagick/bin/magick convert -append '..append_input_images..' '..append_output_image)
local call_imagemagick_cmd = io.popen('/usr/local/ImageMagick/bin/magick convert -append '..append_input_images..' '..append_output_image)
local imagemagick_cmd_replay = call_imagemagick_cmd:read("*all")
ngx.log(ngx.INFO , imagemagick_cmd_replay)
-676
View File
@@ -1,676 +0,0 @@
-- Copyright (C) Yichun Zhang (agentzh)
local sub = string.sub
local byte = string.byte
local tab_insert = table.insert
local tab_remove = table.remove
local tcp = ngx.socket.tcp
local null = ngx.null
local ipairs = ipairs
local type = type
local pairs = pairs
local unpack = unpack
local setmetatable = setmetatable
local tonumber = tonumber
local tostring = tostring
local rawget = rawget
local select = select
--local error = error
local ok, new_tab = pcall(require, "table.new")
if not ok or type(new_tab) ~= "function" then
new_tab = function (narr, nrec) return {} end
end
local _M = new_tab(0, 55)
_M._VERSION = '0.29'
local common_cmds = {
"get", "set", "mget", "mset",
"del", "incr", "decr", -- Strings
"llen", "lindex", "lpop", "lpush",
"lrange", "linsert", -- Lists
"hexists", "hget", "hset", "hmget",
--[[ "hmset", ]] "hdel", -- Hashes
"smembers", "sismember", "sadd", "srem",
"sdiff", "sinter", "sunion", -- Sets
"zrange", "zrangebyscore", "zrank", "zadd",
"zrem", "zincrby", -- Sorted Sets
"auth", "eval", "expire", "script",
"sort" -- Others
}
local sub_commands = {
"subscribe", "psubscribe"
}
local unsub_commands = {
"unsubscribe", "punsubscribe"
}
local mt = { __index = _M }
function _M.new(self)
local sock, err = tcp()
if not sock then
return nil, err
end
return setmetatable({ _sock = sock,
_subscribed = false,
_n_channel = {
unsubscribe = 0,
punsubscribe = 0,
},
}, mt)
end
function _M.set_timeout(self, timeout)
local sock = rawget(self, "_sock")
if not sock then
error("not initialized", 2)
return
end
sock:settimeout(timeout)
end
function _M.set_timeouts(self, connect_timeout, send_timeout, read_timeout)
local sock = rawget(self, "_sock")
if not sock then
error("not initialized", 2)
return
end
sock:settimeouts(connect_timeout, send_timeout, read_timeout)
end
function _M.connect(self, host, port_or_opts, opts)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
local unix
do
local typ = type(host)
if typ ~= "string" then
error("bad argument #1 host: string expected, got " .. typ, 2)
end
if sub(host, 1, 5) == "unix:" then
unix = true
end
if unix then
typ = type(port_or_opts)
if port_or_opts ~= nil and typ ~= "table" then
error("bad argument #2 opts: nil or table expected, got " ..
typ, 2)
end
else
typ = type(port_or_opts)
if typ ~= "number" then
port_or_opts = tonumber(port_or_opts)
if port_or_opts == nil then
error("bad argument #2 port: number expected, got " ..
typ, 2)
end
end
if opts ~= nil then
typ = type(opts)
if typ ~= "table" then
error("bad argument #3 opts: nil or table expected, got " ..
typ, 2)
end
end
end
end
self._subscribed = false
local ok, err
if unix then
-- second argument of sock:connect() cannot be nil
if port_or_opts ~= nil then
ok, err = sock:connect(host, port_or_opts)
opts = port_or_opts
else
ok, err = sock:connect(host)
end
else
ok, err = sock:connect(host, port_or_opts, opts)
end
if not ok then
return ok, err
end
if opts and opts.ssl then
ok, err = sock:sslhandshake(false, opts.server_name, opts.ssl_verify)
if not ok then
return ok, "failed to do ssl handshake: " .. err
end
end
return ok, err
end
function _M.set_keepalive(self, ...)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
if rawget(self, "_subscribed") then
return nil, "subscribed state"
end
return sock:setkeepalive(...)
end
function _M.get_reused_times(self)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
return sock:getreusedtimes()
end
local function close(self)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
return sock:close()
end
_M.close = close
local function _read_reply(self, sock)
local line, err = sock:receive()
if not line then
if err == "timeout" and not rawget(self, "_subscribed") then
sock:close()
end
return nil, err
end
local prefix = byte(line)
if prefix == 36 then -- char '$'
-- print("bulk reply")
local size = tonumber(sub(line, 2))
if size < 0 then
return null
end
local data, err = sock:receive(size)
if not data then
if err == "timeout" then
sock:close()
end
return nil, err
end
local dummy, err = sock:receive(2) -- ignore CRLF
if not dummy then
if err == "timeout" then
sock:close()
end
return nil, err
end
return data
elseif prefix == 43 then -- char '+'
-- print("status reply")
return sub(line, 2)
elseif prefix == 42 then -- char '*'
local n = tonumber(sub(line, 2))
-- print("multi-bulk reply: ", n)
if n < 0 then
return null
end
local vals = new_tab(n, 0)
local nvals = 0
for i = 1, n do
local res, err = _read_reply(self, sock)
if res then
nvals = nvals + 1
vals[nvals] = res
elseif res == nil then
return nil, err
else
-- be a valid redis error value
nvals = nvals + 1
vals[nvals] = {false, err}
end
end
return vals
elseif prefix == 58 then -- char ':'
-- print("integer reply")
return tonumber(sub(line, 2))
elseif prefix == 45 then -- char '-'
-- print("error reply: ", n)
return false, sub(line, 2)
else
-- when `line` is an empty string, `prefix` will be equal to nil.
return nil, "unknown prefix: \"" .. tostring(prefix) .. "\""
end
end
local function _gen_req(args)
local nargs = #args
local req = new_tab(nargs * 5 + 1, 0)
req[1] = "*" .. nargs .. "\r\n"
local nbits = 2
for i = 1, nargs do
local arg = args[i]
if type(arg) ~= "string" then
arg = tostring(arg)
end
req[nbits] = "$"
req[nbits + 1] = #arg
req[nbits + 2] = "\r\n"
req[nbits + 3] = arg
req[nbits + 4] = "\r\n"
nbits = nbits + 5
end
-- it is much faster to do string concatenation on the C land
-- in real world (large number of strings in the Lua VM)
return req
end
local function _check_msg(self, res)
return rawget(self, "_subscribed") and
type(res) == "table" and (res[1] == "message" or res[1] == "pmessage")
end
local function _do_cmd(self, ...)
local args = {...}
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
local req = _gen_req(args)
local reqs = rawget(self, "_reqs")
if reqs then
reqs[#reqs + 1] = req
return
end
-- print("request: ", table.concat(req))
local bytes, err = sock:send(req)
if not bytes then
return nil, err
end
local res, err = _read_reply(self, sock)
while _check_msg(self, res) do
if rawget(self, "_buffered_msg") == nil then
self._buffered_msg = new_tab(1, 0)
end
tab_insert(self._buffered_msg, res)
res, err = _read_reply(self, sock)
end
return res, err
end
local function _check_unsubscribed(self, res)
if type(res) == "table"
and (res[1] == "unsubscribe" or res[1] == "punsubscribe")
then
self._n_channel[res[1]] = self._n_channel[res[1]] - 1
local buffered_msg = rawget(self, "_buffered_msg")
if buffered_msg then
-- remove messages of unsubscribed channel
local msg_type =
(res[1] == "punsubscribe") and "pmessage" or "message"
local j = 1
for _, msg in ipairs(buffered_msg) do
if msg[1] == msg_type and msg[2] ~= res[2] then
-- move messages to overwrite the removed ones
buffered_msg[j] = msg
j = j + 1
end
end
-- clear remain messages
for i = j, #buffered_msg do
buffered_msg[i] = nil
end
if #buffered_msg == 0 then
self._buffered_msg = nil
end
end
if res[3] == 0 then
-- all channels are unsubscribed
self._subscribed = false
end
end
end
local function _check_subscribed(self, res)
if type(res) == "table"
and (res[1] == "subscribe" or res[1] == "psubscribe")
then
if res[1] == "subscribe" then
self._n_channel.unsubscribe = self._n_channel.unsubscribe + 1
elseif res[1] == "psubscribe" then
self._n_channel.punsubscribe = self._n_channel.punsubscribe + 1
end
end
end
function _M.read_reply(self)
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
if not rawget(self, "_subscribed") then
return nil, "not subscribed"
end
local buffered_msg = rawget(self, "_buffered_msg")
if buffered_msg then
local msg = buffered_msg[1]
tab_remove(buffered_msg, 1)
if #buffered_msg == 0 then
self._buffered_msg = nil
end
return msg
end
local res, err = _read_reply(self, sock)
_check_unsubscribed(self, res)
return res, err
end
for i = 1, #common_cmds do
local cmd = common_cmds[i]
_M[cmd] =
function (self, ...)
return _do_cmd(self, cmd, ...)
end
end
local function handle_subscribe_result(self, cmd, nargs, res)
local err
_check_subscribed(self, res)
if nargs <= 1 then
return res
end
local results = new_tab(nargs, 0)
results[1] = res
local sock = rawget(self, "_sock")
for i = 2, nargs do
res, err = _read_reply(self, sock)
if not res then
return nil, err
end
_check_subscribed(self, res)
results[i] = res
end
return results
end
for i = 1, #sub_commands do
local cmd = sub_commands[i]
_M[cmd] =
function (self, ...)
if not rawget(self, "_subscribed") then
self._subscribed = true
end
local nargs = select("#", ...)
local res, err = _do_cmd(self, cmd, ...)
if not res then
return nil, err
end
return handle_subscribe_result(self, cmd, nargs, res)
end
end
local function handle_unsubscribe_result(self, cmd, nargs, res)
local err
_check_unsubscribed(self, res)
if self._n_channel[cmd] == 0 or nargs == 1 then
return res
end
local results = new_tab(nargs, 0)
results[1] = res
local sock = rawget(self, "_sock")
local i = 2
while nargs == 0 or i <= nargs do
res, err = _read_reply(self, sock)
if not res then
return nil, err
end
results[i] = res
i = i + 1
_check_unsubscribed(self, res)
if self._n_channel[cmd] == 0 then
-- exit the loop for unsubscribe() call
break
end
end
return results
end
for i = 1, #unsub_commands do
local cmd = unsub_commands[i]
_M[cmd] =
function (self, ...)
-- assume all channels are unsubscribed by only one time
if not rawget(self, "_subscribed") then
return nil, "not subscribed"
end
local nargs = select("#", ...)
local res, err = _do_cmd(self, cmd, ...)
if not res then
return nil, err
end
return handle_unsubscribe_result(self, cmd, nargs, res)
end
end
function _M.hmset(self, hashname, ...)
if select('#', ...) == 1 then
local t = select(1, ...)
local n = 0
for k, v in pairs(t) do
n = n + 2
end
local array = new_tab(n, 0)
local i = 0
for k, v in pairs(t) do
array[i + 1] = k
array[i + 2] = v
i = i + 2
end
-- print("key", hashname)
return _do_cmd(self, "hmset", hashname, unpack(array))
end
-- backwards compatibility
return _do_cmd(self, "hmset", hashname, ...)
end
function _M.init_pipeline(self, n)
self._reqs = new_tab(n or 4, 0)
end
function _M.cancel_pipeline(self)
self._reqs = nil
end
function _M.commit_pipeline(self)
local reqs = rawget(self, "_reqs")
if not reqs then
return nil, "no pipeline"
end
self._reqs = nil
local sock = rawget(self, "_sock")
if not sock then
return nil, "not initialized"
end
local bytes, err = sock:send(reqs)
if not bytes then
return nil, err
end
local nvals = 0
local nreqs = #reqs
local vals = new_tab(nreqs, 0)
for i = 1, nreqs do
local res, err = _read_reply(self, sock)
if res then
nvals = nvals + 1
vals[nvals] = res
elseif res == nil then
if err == "timeout" then
close(self)
end
return nil, err
else
-- be a valid redis error value
nvals = nvals + 1
vals[nvals] = {false, err}
end
end
return vals
end
function _M.array_to_hash(self, t)
local n = #t
-- print("n = ", n)
local h = new_tab(0, n / 2)
for i = 1, n, 2 do
h[t[i]] = t[i + 1]
end
return h
end
-- this method is deperate since we already do lazy method generation.
function _M.add_commands(...)
local cmds = {...}
for i = 1, #cmds do
local cmd = cmds[i]
_M[cmd] =
function (self, ...)
return _do_cmd(self, cmd, ...)
end
end
end
setmetatable(_M, {__index = function(self, cmd)
local method =
function (self, ...)
return _do_cmd(self, cmd, ...)
end
-- cache the lazily generated method in our
-- module table
_M[cmd] = method
return method
end})
return _M
-267
View File
@@ -1,267 +0,0 @@
-- Copyright (C) Yichun Zhang (agentzh)
-- local sub = string.sub
local req_socket = ngx.req.socket
local match = string.match
local setmetatable = setmetatable
local type = type
local ngx_var = ngx.var
-- local print = print
local _M = { _VERSION = '0.10' }
local CHUNK_SIZE = 4096
local MAX_LINE_SIZE = 512
local STATE_BEGIN = 1
local STATE_READING_HEADER = 2
local STATE_READING_BODY = 3
local STATE_EOF = 4
local mt = { __index = _M }
local state_handlers
local function get_boundary()
local header = ngx_var.content_type
if not header then
return nil
end
if type(header) == "table" then
header = header[1]
end
local m = match(header, ";%s*boundary=\"([^\"]+)\"")
if m then
return m
end
return match(header, ";%s*boundary=([^\",;]+)")
end
function _M.new(self, chunk_size, max_line_size)
local boundary = get_boundary()
-- print("boundary: ", boundary)
if not boundary then
return nil, "no boundary defined in Content-Type"
end
-- print('boundary: "', boundary, '"')
local sock, err = req_socket()
if not sock then
return nil, err
end
local read2boundary, err = sock:receiveuntil("--" .. boundary)
if not read2boundary then
return nil, err
end
local read_line, err = sock:receiveuntil("\r\n")
if not read_line then
return nil, err
end
return setmetatable({
sock = sock,
size = chunk_size or CHUNK_SIZE,
line_size = max_line_size or MAX_LINE_SIZE,
read2boundary = read2boundary,
read_line = read_line,
boundary = boundary,
state = STATE_BEGIN
}, mt)
end
function _M.set_timeout(self, timeout)
local sock = self.sock
if not sock then
return nil, "not initialized"
end
return sock:settimeout(timeout)
end
local function discard_line(self)
local read_line = self.read_line
local line, err = read_line(self.line_size)
if not line then
return nil, err
end
local dummy, err = read_line(1)
if dummy then
return nil, "line too long: " .. line .. dummy .. "..."
end
if err then
return nil, err
end
return 1
end
local function discard_rest(self)
local sock = self.sock
local size = self.size
while true do
local dummy, err = sock:receive(size)
if err and err ~= 'closed' then
return nil, err
end
if not dummy then
return 1
end
end
end
local function read_body_part(self)
local read2boundary = self.read2boundary
local chunk, err = read2boundary(self.size)
if err then
return nil, nil, err
end
if not chunk then
local sock = self.sock
local data = sock:receive(2)
if data == "--" then
local ok, err = discard_rest(self)
if not ok then
return nil, nil, err
end
self.state = STATE_EOF
return "part_end"
end
if data ~= "\r\n" then
local ok, err = discard_line(self)
if not ok then
return nil, nil, err
end
end
self.state = STATE_READING_HEADER
return "part_end"
end
return "body", chunk
end
local function read_header(self)
local read_line = self.read_line
local line, err = read_line(self.line_size)
if err then
return nil, nil, err
end
local dummy, err = read_line(1)
if dummy then
return nil, nil, "line too long: " .. line .. dummy .. "..."
end
if err then
return nil, nil, err
end
-- print("read line: ", line)
if line == "" then
-- after the last header
self.state = STATE_READING_BODY
return read_body_part(self)
end
local key, value = match(line, "([^: \t]+)%s*:%s*(.+)")
if not key then
return 'header', line
end
return 'header', {key, value, line}
end
local function eof()
return "eof", nil
end
function _M.read(self)
-- local size = self.size
local handler = state_handlers[self.state]
if handler then
return handler(self)
end
return nil, nil, "bad state: " .. self.state
end
local function read_preamble(self)
local sock = self.sock
if not sock then
return nil, nil, "not initialized"
end
local size = self.size
local read2boundary = self.read2boundary
while true do
local preamble = read2boundary(size)
if not preamble then
break
end
-- discard the preamble data chunk
-- print("read preamble: ", preamble)
end
local ok, err = discard_line(self)
if not ok then
return nil, nil, err
end
local read2boundary, err = sock:receiveuntil("\r\n--" .. self.boundary)
if not read2boundary then
return nil, nil, err
end
self.read2boundary = read2boundary
self.state = STATE_READING_HEADER
return read_header(self)
end
state_handlers = {
read_preamble,
read_header,
read_body_part,
eof
}
return _M
+9 -5
View File
@@ -1,7 +1,7 @@
-- Require lua modules
local upload = require "upload"
local uuid = require "uuid"
local redis = require "redis"
local upload = require "resty/upload"
local uuid = require "resty.jit-uuid"
local redis = require "resty.redis"
local cjson = require "cjson"
---------------------------------------------------------------------------------------
@@ -30,6 +30,10 @@ if not form then
ngx.exit(500)
end
---- Automatic seeding with os.time(), LuaSocket, or ngx.time()
uuid.seed()
uuid()
---- Redis init
------ Connect to redis
local redis_task_database = redis:new()
@@ -92,7 +96,7 @@ while true do
-- Save upload file
if upload_image_filename then
-- Save file name with generate uuid
save_image_filename = uuid:generate() -- Generate save filename uuid
save_image_filename = uuid:generate_v4() -- Generate save filename uuid
save_image_filename = save_image_filename .. '.' .. upload_image_filename:match(".+%.(%w+)$")
file_to_save = io.open(save_upload_file_path .. save_image_filename, "w+")
@@ -154,7 +158,7 @@ if ret_save then
-- Communicate with front end
---- Generate task uuid
local task_uuid = uuid.generate();
local task_uuid = uuid.generate_v4();
---------------------------------------------------------------------------------------
-- Construct {key: task_id | value: [images] } in json
-103
View File
@@ -1,103 +0,0 @@
local ffi = require "ffi"
local ffi_new = ffi.new
local ffi_str = ffi.string
local ffi_load = ffi.load
local ffi_cdef = ffi.cdef
local C = ffi.C
local OSX = ffi.os == "OSX"
local pcall = pcall
local assert = assert
local tonumber = tonumber
local setmetatable = setmetatable
ffi_cdef[[
typedef unsigned char uuid_t[16];
typedef long time_t;
typedef struct timeval {
time_t tv_sec;
time_t tv_usec;
} timeval;
void uuid_generate(uuid_t out);
void uuid_generate_random(uuid_t out);
void uuid_generate_time(uuid_t out);
int uuid_generate_time_safe(uuid_t out);
int uuid_parse(const char *in, uuid_t uu);
void uuid_unparse(const uuid_t uu, char *out);
int uuid_type(const uuid_t uu);
int uuid_variant(const uuid_t uu);
time_t uuid_time(const uuid_t uu, struct timeval *ret_tv);
]]
local function L(n)
local ok, lib = pcall(ffi_load, n)
if ok then return lib end
ok, lib = pcall(ffi_load, n .. '.so.1')
assert(ok, lib)
return lib
end
local lib = OSX and C or L "uuid"
local uid = ffi_new "uuid_t"
local tvl = ffi_new "timeval"
local buf = ffi_new("char[?]", 36)
local uuid = {}
local mt = {}
local function unparse(id)
lib.uuid_unparse(id, buf)
return ffi_str(buf, 36)
end
local function parse(id)
return lib.uuid_parse(id, uid) == 0 and uid or nil
end
function uuid.generate()
lib.uuid_generate(uid)
return unparse(uid)
end
function uuid.generate_random()
lib.uuid_generate_random(uid)
return unparse(uid)
end
function uuid.generate_time()
lib.uuid_generate_time(uid)
return unparse(uid)
end
function uuid.generate_time_safe()
assert(not OSX, "uuid_generate_time_safe is not supported on OS X.")
local safe = lib.uuid_generate_time_safe(uid) == 0
return unparse(uid), safe
end
function uuid.type(id)
assert(not OSX, "uuid_type is not supported on OS X.")
local parsed = parse(id)
return parsed and lib.uuid_type(parsed)
end
function uuid.variant(id)
assert(not OSX, "uuid_variant is not supported on OS X.")
local parsed = parse(id)
return parsed and lib.uuid_variant(parsed)
end
function uuid.time(id)
local parsed = parse(id)
if parsed then
local secs = lib.uuid_time(parsed, tvl)
return tonumber(secs), tonumber(tvl.tv_usec)
end
end
function uuid.is_valid(id)
return not not parse(id)
end
mt.__call = uuid.generate
return setmetatable(uuid, mt)