2 changed files with 194 additions and 0 deletions
@ -0,0 +1,71 @@ |
|||
local lz = require "lzss" |
|||
local buffer = require "buffer" |
|||
|
|||
lz16 = {} |
|||
|
|||
local function cint(n,l) |
|||
local t={} |
|||
for i = 0, 7 do |
|||
t[i+1] = (n >> (i * 8)) & 0xFF |
|||
end |
|||
return string.reverse(string.char(table.unpack(t)):sub(1,l)) |
|||
end |
|||
local function toint(s) |
|||
local n = 0 |
|||
local i = 1 |
|||
for p in s:gmatch(".") do |
|||
n = n << 8 |
|||
n = n | string.byte(p) |
|||
i=i+1 |
|||
end |
|||
return n |
|||
end |
|||
|
|||
local function readBuffer(fi) |
|||
local stream = {} |
|||
if fi:read(4) ~= "lz16" then |
|||
return false, "not an lz16 archive" |
|||
end |
|||
function stream.read() |
|||
local len = toint(fi:read(2) or "\0\0") |
|||
if len < 1 then |
|||
return nil |
|||
end |
|||
coroutine.yield() |
|||
return lz.decompress(fi:read(len)) |
|||
end |
|||
function stream.close() |
|||
fi:close() |
|||
end |
|||
return buffer.new("rb",stream) |
|||
end |
|||
|
|||
local function writeBuffer(fo) |
|||
local stream = {} |
|||
function stream:write(data) |
|||
local cblock = lz.compress(data) |
|||
fo:write(cint(cblock:len(),2)..cblock) |
|||
return cblock:len()+2 |
|||
end |
|||
function stream.close() |
|||
fo:close() |
|||
end |
|||
fo:write("lz16") -- write header |
|||
return buffer.new("wb",stream) |
|||
end |
|||
|
|||
function lz16.buffer(stream) |
|||
if stream.mode.w then |
|||
return writeBuffer(stream) |
|||
end |
|||
return readBuffer(stream) |
|||
end |
|||
|
|||
function lz16.open(fname, mode) |
|||
local f = io.open(fname, mode) |
|||
if not f then return false end |
|||
f.mode.b = true |
|||
return lz16.buffer(f) |
|||
end |
|||
|
|||
return lz16 |
@ -0,0 +1,123 @@ |
|||
--[[---------------------------------------------------------------------------- |
|||
|
|||
LZSS - encoder / decoder |
|||
|
|||
This is free and unencumbered software released into the public domain. |
|||
|
|||
Anyone is free to copy, modify, publish, use, compile, sell, or |
|||
distribute this software, either in source code form or as a compiled |
|||
binary, for any purpose, commercial or non-commercial, and by any |
|||
means. |
|||
|
|||
In jurisdictions that recognize copyright laws, the author or authors |
|||
of this software dedicate any and all copyright interest in the |
|||
software to the public domain. We make this dedication for the benefit |
|||
of the public at large and to the detriment of our heirs and |
|||
successors. We intend this dedication to be an overt act of |
|||
relinquishment in perpetuity of all present and future rights to this |
|||
software under copyright law. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
|||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
|||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
|||
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR |
|||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
|||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
|||
OTHER DEALINGS IN THE SOFTWARE. |
|||
|
|||
For more information, please refer to <http://unlicense.org/> |
|||
|
|||
--]]---------------------------------------------------------------------------- |
|||
-------------------------------------------------------------------------------- |
|||
local M = {} |
|||
local string, table = string, table |
|||
|
|||
-------------------------------------------------------------------------------- |
|||
local POS_BITS = 12 |
|||
local LEN_BITS = 16 - POS_BITS |
|||
local POS_SIZE = 1 << POS_BITS |
|||
local LEN_SIZE = 1 << LEN_BITS |
|||
local LEN_MIN = 3 |
|||
|
|||
-------------------------------------------------------------------------------- |
|||
function M.compress(input) |
|||
local offset, output = 1, {} |
|||
local window = '' |
|||
|
|||
local function search() |
|||
for i = LEN_SIZE + LEN_MIN - 1, LEN_MIN, -1 do |
|||
local str = string.sub(input, offset, offset + i - 1) |
|||
local pos = string.find(window, str, 1, true) |
|||
if pos then |
|||
return pos, str |
|||
end |
|||
end |
|||
end |
|||
|
|||
while offset <= #input do |
|||
local flags, buffer = 0, {} |
|||
|
|||
for i = 0, 7 do |
|||
if offset <= #input then |
|||
local pos, str = search() |
|||
if pos and #str >= LEN_MIN then |
|||
local tmp = ((pos - 1) << LEN_BITS) | (#str - LEN_MIN) |
|||
buffer[#buffer + 1] = string.pack('>I2', tmp) |
|||
else |
|||
flags = flags | (1 << i) |
|||
str = string.sub(input, offset, offset) |
|||
buffer[#buffer + 1] = str |
|||
end |
|||
window = string.sub(window .. str, -POS_SIZE) |
|||
offset = offset + #str |
|||
else |
|||
break |
|||
end |
|||
end |
|||
|
|||
if #buffer > 0 then |
|||
output[#output + 1] = string.char(flags) |
|||
output[#output + 1] = table.concat(buffer) |
|||
end |
|||
end |
|||
|
|||
return table.concat(output) |
|||
end |
|||
|
|||
-------------------------------------------------------------------------------- |
|||
function M.decompress(input) |
|||
local offset, output = 1, {} |
|||
local window = '' |
|||
|
|||
while offset <= #input do |
|||
local flags = string.byte(input, offset) |
|||
offset = offset + 1 |
|||
|
|||
for i = 1, 8 do |
|||
local str = nil |
|||
if (flags & 1) ~= 0 then |
|||
if offset <= #input then |
|||
str = string.sub(input, offset, offset) |
|||
offset = offset + 1 |
|||
end |
|||
else |
|||
if offset + 1 <= #input then |
|||
local tmp = string.unpack('>I2', input, offset) |
|||
offset = offset + 2 |
|||
local pos = (tmp >> LEN_BITS) + 1 |
|||
local len = (tmp & (LEN_SIZE - 1)) + LEN_MIN |
|||
str = string.sub(window, pos, pos + len - 1) |
|||
end |
|||
end |
|||
flags = flags >> 1 |
|||
if str then |
|||
output[#output + 1] = str |
|||
window = string.sub(window .. str, -POS_SIZE) |
|||
end |
|||
end |
|||
end |
|||
|
|||
return table.concat(output) |
|||
end |
|||
|
|||
return M |
Loading…
issues.context.reference_issue