--!A cross-platform build utility based on Lua -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. -- -- Copyright (C) 2015-present, Xmake Open Source Community. -- -- @author ruki -- @file lz4.lua -- -- define module: lz4 local lz4 = lz4 or {} local _cstream = _cstream or {} local _dstream = _dstream or {} -- load modules local io = require("base/io") local utils = require("base/utils") local bytes = require("base/bytes") local table = require("base/table") -- save metatable and builtin functions lz4._compress = lz4._compress or lz4.compress lz4._decompress = lz4._decompress or lz4.decompress lz4._block_compress = lz4._block_compress or lz4.block_compress lz4._block_decompress = lz4._block_decompress or lz4.block_decompress lz4._compress_file = lz4._compress_file or lz4.compress_file lz4._decompress_file = lz4._decompress_file or lz4.decompress_file lz4._compress_stream_open = lz4._compress_stream_open or lz4.compress_stream_open lz4._compress_stream_read = lz4._compress_stream_read or lz4.compress_stream_read lz4._compress_stream_write = lz4._compress_stream_write or lz4.compress_stream_write lz4._compress_stream_close = lz4._compress_stream_close or lz4.compress_stream_close lz4._decompress_stream_open = lz4._decompress_stream_open or lz4.decompress_stream_open lz4._decompress_stream_read = lz4._decompress_stream_read or lz4.decompress_stream_read lz4._decompress_stream_write = lz4._decompress_stream_write or lz4.decompress_stream_write lz4._decompress_stream_close = lz4._decompress_stream_close or lz4.decompress_stream_close -- new a compress stream function _cstream.new(handle) local instance = table.inherit(_cstream) instance._HANDLE = handle setmetatable(instance, _cstream) return instance end -- get cdata of stream function _cstream:cdata() return self._HANDLE end -- read data from stream function _cstream:read(buff, size, opt) assert(buff) -- ensure opened local ok, errors = self:_ensure_opened() if not ok then return -1, errors end -- check buffer size = size or buff:size() if buff:size() < size then return -1, string.format("%s: too small buffer!", self) end -- check size if size == 0 then return 0 elseif size == nil or size < 0 then return -1, string.format("%s: invalid size(%d)!", self, size) end -- init start in buffer opt = opt or {} local start = opt.start or 1 local pos = start - 1 if start >= buff:size() or start < 1 then return -1, string.format("%s: invalid start(%d)!", self, start) end -- read it local read, data_or_errors = lz4._compress_stream_read(self:cdata(), buff:caddr() + pos, math.min(buff:size() - pos, size)) if read > 0 then data_or_errors = buff:slice(start, read) end if read < 0 and data_or_errors then data_or_errors = string.format("%s: %s", self, data_or_errors) end return read, data_or_errors end -- write data to stream function _cstream:write(data, opt) -- ensure opened local ok, errors = self:_ensure_opened() if not ok then return -1, errors end -- get data address and size for bytes and string if type(data) == "string" then data = bytes(data) end local datasize = data:size() local dataaddr = data:caddr() -- init start and last opt = opt or {} local start = opt.start or 1 local last = opt.last or datasize if start < 1 or start > datasize then return -1, string.format("%s: invalid start(%d)!", self, start) end if last < start - 1 or last > datasize + start - 1 then return -1, string.format("%s: invalid last(%d)!", self, last) end -- write it local errors = nil local write, errors = lz4._compress_stream_write(self:cdata(), dataaddr + start - 1, last + 1 - start, opt.beof) if write < 0 and errors then errors = string.format("%s: %s", self, errors) end return write, errors end -- ensure it is opened function _cstream:_ensure_opened() if not self:cdata() then return false, string.format("%s: has been closed!", self) end return true end -- tostring(stream) function _cstream:__tostring() return string.format("", self:cdata()) end -- gc(stream) function _cstream:__gc() if self:cdata() and lz4._compress_stream_close(self:cdata()) then self._HANDLE = nil end end -- new a decompress stream function _dstream.new(handle) local instance = table.inherit(_dstream) instance._HANDLE = handle setmetatable(instance, _dstream) return instance end -- get cdata of stream function _dstream:cdata() return self._HANDLE end -- read data from stream function _dstream:read(buff, size, opt) assert(buff) -- ensure opened local ok, errors = self:_ensure_opened() if not ok then return -1, errors end -- check buffer size = size or buff:size() if buff:size() < size then return -1, string.format("%s: too small buffer!", self) end -- check size if size == 0 then return 0 elseif size == nil or size < 0 then return -1, string.format("%s: invalid size(%d)!", self, size) end -- init start in buffer opt = opt or {} local start = opt.start or 1 local pos = start - 1 if start >= buff:size() or start < 1 then return -1, string.format("%s: invalid start(%d)!", self, start) end -- read it local read, data_or_errors = lz4._decompress_stream_read(self:cdata(), buff:caddr() + pos, math.min(buff:size() - pos, size)) if read > 0 then data_or_errors = buff:slice(start, read) end if read < 0 and data_or_errors then data_or_errors = string.format("%s: %s", self, data_or_errors) end return read, data_or_errors end -- write data to stream function _dstream:write(data, opt) -- ensure opened local ok, errors = self:_ensure_opened() if not ok then return -1, errors end -- get data address and size for bytes and string if type(data) == "string" then data = bytes(data) end local datasize = data:size() local dataaddr = data:caddr() -- init start and last opt = opt or {} local start = opt.start or 1 local last = opt.last or datasize if start < 1 or start > datasize then return -1, string.format("%s: invalid start(%d)!", self, start) end if last < start - 1 or last > datasize + start - 1 then return -1, string.format("%s: invalid last(%d)!", self, last) end -- write it local errors = nil local write, errors = lz4._decompress_stream_write(self:cdata(), dataaddr + start - 1, last + 1 - start, opt.beof) if write < 0 and errors then errors = string.format("%s: %s", self, errors) end return write, errors end -- ensure it is opened function _dstream:_ensure_opened() if not self:cdata() then return false, string.format("%s: has been closed!", self) end return true end -- tostring(stream) function _dstream:__tostring() return string.format("", self:cdata()) end -- gc(stream) function _dstream:__gc() if self:cdata() and lz4._decompress_stream_close(self:cdata()) then self._HANDLE = nil end end -- open a compress stream function lz4.compress_stream(opt) local handle, errors = lz4._compress_stream_open() if handle then return _cstream.new(handle) else return nil, errors or "failed to open compress stream!" end end -- open a decompress stream function lz4.decompress_stream(opt) local handle, errors = lz4._decompress_stream_open() if handle then return _dstream.new(handle) else return nil, errors or "failed to open decompress stream!" end end -- compress frame data -- -- @param data the data -- @param opt the options -- -- @return the result data -- function lz4.compress(data, opt) if type(data) == "string" then data = bytes(data) end local datasize = data:size() local dataaddr = data:caddr() local result, errors = lz4._compress(dataaddr, datasize) if not result then return nil, errors or string.format("compress frame data failed, %s", errors or "unknown") end return bytes(result) end -- decompres frame data -- -- @param data the data -- @param opt the options -- -- @return the result data -- function lz4.decompress(data, opt) if type(data) == "string" then data = bytes(data) end local datasize = data:size() local dataaddr = data:caddr() local result, errors = lz4._decompress(dataaddr, datasize) if not result then return nil, string.format("decompress frame data failed, %s", errors or "unknown") end return bytes(result) end -- compress file data function lz4.compress_file(srcpath, dstpath, opt) local ok, errors = lz4._compress_file(tostring(srcpath), tostring(dstpath)) if not ok then errors = string.format("compress file %s failed!", srcpath, errors or os.strerror() or "unknown") end return ok, errors end -- decompress file data function lz4.decompress_file(srcpath, dstpath, opt) local ok, errors = lz4._decompress_file(tostring(srcpath), tostring(dstpath)) if not ok then errors = string.format("decompress file %s failed!", srcpath, errors or os.strerror() or "unknown") end return ok, errors end -- compress block data -- -- @param data the data -- @param opt the options -- -- @return the result data -- function lz4.block_compress(data, opt) if type(data) == "string" then data = bytes(data) end local datasize = data:size() local dataaddr = data:caddr() local result, errors = lz4._block_compress(dataaddr, datasize) if not result then return nil, errors or string.format("compress block data failed, %s", errors or "unknown") end return bytes(result) end -- decompres block data -- -- @param data the data -- @param realsize the decompressed real size -- @param opt the options -- -- @return the result data -- function lz4.block_decompress(data, realsize, opt) local datasize = data:size() local dataaddr = data:caddr() local result, errors = lz4._block_decompress(dataaddr, datasize, realsize) if not result then return nil, string.format("decompress block data failed, %s", errors or "unknown") end return bytes(result) end -- return module: lz4 return lz4