forked from koreader/koreader-base
-
Notifications
You must be signed in to change notification settings - Fork 0
/
zstd.lua
81 lines (68 loc) · 3.22 KB
/
zstd.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
--[[--
LuaJIT FFI wrapper for ZSTD.
@module ffi.zstd
]]
local ffi = require("ffi")
local C = ffi.C
require("ffi/posix_h")
require("ffi/zstd_h")
local zst = ffi.loadlib("zstd", "1")
local zstd = {}
-- c.f., https://github.com/facebook/zstd/tree/dev/examples
function zstd.zstd_compress(ptr, size)
--print("zstd_compress:", ptr, size)
local n = zst.ZSTD_compressBound(size)
local cbuff = C.calloc(n, 1)
assert(cbuff ~= nil, "Failed to allocate ZSTD compression buffer (" .. tonumber(n) .. " bytes)")
-- NOTE: We should be quite all right with the default (3), which will most likely trounce zlib's 9 in every respect...
local clen = zst.ZSTD_compress(cbuff, n, ptr, size, zst.ZSTD_CLEVEL_DEFAULT)
if zst.ZSTD_isError(clen) ~= 0 then
C.free(cbuff)
error(ffi.string(zst.ZSTD_getErrorName(clen)))
end
return cbuff, clen
end
function zstd.zstd_uncompress(ptr, size)
--print("zstd_uncompress:", ptr, size)
-- The decompressed size is encoded in the ZST frame header
local n = zst.ZSTD_getFrameContentSize(ptr, size)
local buff = C.calloc(n, 1)
assert(buff ~= nil, "Failed to allocate ZSTD decompression buffer (" .. tonumber(n) .. " bytes)")
local ulen = zst.ZSTD_decompress(buff, n, ptr, size)
if zst.ZSTD_isError(ulen) ~= 0 then
C.free(buff)
error(ffi.string(zst.ZSTD_getErrorName(ulen)))
end
return buff, ulen
end
-- Same idea, but with a re-usable decompression context
-- NOTE: We currently don't bother with that for compression,
-- since the only user (BookInfoManager) runs that in a subprocess anyway.
-- More for correctness than anything, make sure the GC will actually free the resources when the variable goes out of scope...
-- NOTE: In Lua 5.1/LuaJIT 2.1, the __gc metamethod is *only* called for userdata, *NOT* tables.
-- There are funky workarounds involving newproxy() available
-- (c.f., https://stackoverflow.com/q/55585619 & https://github.com/LuaJIT/LuaJIT/issues/47),
-- but, for cdata, LuaJIT provides custom finalizer handling, so, do that instead ;).
-- And do it explicitly via ffi.gc, instead of relying on the __gc metamethod of a metatable on a cdata
-- (as bound by via ffi.metatype, c.f., LuaJIT docs and the comments about this in BB.gc @ ffi/blitbuffer.lua).
local DCtx
function zstd.zstd_uncompress_ctx(ptr, size)
--print("zstd_uncompress_ctx:", ptr, size)
-- Lazy init the decompression context
if DCtx == nil then
DCtx = ffi.gc(zst.ZSTD_createDCtx(), zst.ZSTD_freeDCtx)
assert(DCtx ~= nil, "Failed to allocate ZSTD decompression context")
else
-- Reset the context
local ret = zst.ZSTD_DCtx_reset(DCtx, zst.ZSTD_reset_session_only)
assert(zst.ZSTD_isError(ret) == 0, ffi.string(zst.ZSTD_getErrorName(ret)))
end
-- The decompressed size is encoded in the ZST frame header
local n = zst.ZSTD_getFrameContentSize(ptr, size)
local buff = C.calloc(n, 1)
assert(buff ~= nil, "Failed to allocate ZSTD decompression buffer (" .. tonumber(n) .. " bytes)")
local ulen = zst.ZSTD_decompressDCtx(DCtx, buff, n, ptr, size)
assert(zst.ZSTD_isError(ulen) == 0, ffi.string(zst.ZSTD_getErrorName(ulen)))
return buff, ulen
end
return zstd