From b51843de14193e5b2325e466b2ad03b761b4e1b1 Mon Sep 17 00:00:00 2001 From: Bilal2453 Date: Sun, 19 Jan 2025 18:17:10 +0300 Subject: [PATCH] initial work for adding docs --- docs/README.md | 5 + docs/gen.lua | 31 ++ docs/generators/init.lua | 3 + docs/generators/markdown.lua | 481 ++++++++++++++++++ docs/modules/miniz.lua | 907 ++++++++++++++++++++++++++++++++++ docs/output/markdown/miniz.md | 392 +++++++++++++++ 6 files changed, 1819 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/gen.lua create mode 100644 docs/generators/init.lua create mode 100644 docs/generators/markdown.lua create mode 100644 docs/modules/miniz.lua create mode 100644 docs/output/markdown/miniz.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..70c06220 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +# Luvi documentation + +Modules: + +- [lminiz](./output/markdown/miniz.md) diff --git a/docs/gen.lua b/docs/gen.lua new file mode 100644 index 00000000..046929f3 --- /dev/null +++ b/docs/gen.lua @@ -0,0 +1,31 @@ +local fs = require 'fs' +local join = require 'pathjoin'.pathJoin +local generators = require './generators' + +local function dir(path) + if not fs.existsSync(path) then + assert(fs.mkdirpSync(path)) + end + return path +end + +-- TODO: a full CLI tool? + +local output_dir = dir('./output') +local input_dir = dir('./modules') + +local modules_docs = {} +for entry, entry_type in fs.scandirSync(input_dir) do + if entry_type == 'file' then + table.insert(modules_docs, dofile(join(input_dir, entry))) + end +end + +for _, docs in ipairs(modules_docs) do + for gen_name, generator in pairs(generators) do + local content = generator.generate(docs) + local dir_path = dir(join(output_dir, gen_name)) + local file_name = docs.name .. generator.extension + assert(fs.writeFileSync(join(dir_path, file_name), content)) + end +end diff --git a/docs/generators/init.lua b/docs/generators/init.lua new file mode 100644 index 00000000..75f95431 --- /dev/null +++ b/docs/generators/init.lua @@ -0,0 +1,3 @@ +return { + markdown = require './markdown', +} diff --git a/docs/generators/markdown.lua b/docs/generators/markdown.lua new file mode 100644 index 00000000..ec25a774 --- /dev/null +++ b/docs/generators/markdown.lua @@ -0,0 +1,481 @@ +local fmt = string.format +local insert, concat = table.insert, table.concat + +---@class markdown.Context +---@field buffer string[] +---@field aliases_map alias_types +local Context = {} + +---@return markdown.Context +function Context.initiate(docs_meta) + -- the final output buffer + local buffer = {} + + -- read all of the aliases and flatten them in a single lookup map + ---@type table + local aliases_map = {} + for _, section in ipairs(docs_meta) do + if section.aliases then + for _, alias in ipairs(section.aliases) do + assert(not aliases_map[alias.name], 'alias with the name @' .. alias.name .. ' is already defined') + aliases_map[alias.name] = alias.types + end + end + end + + return setmetatable({ + buffer = buffer, + aliases_map = aliases_map, + }, { + __index = Context + }) +end + +-- we generate the markdown on the go essentially +-- and any markdown output is written to the buffer through this +function Context:write(input, ...) + if select('#', ...) > 0 then + input = input:format(...) + end + self.buffer[#self.buffer+1] = input +end + +-- concat the buffer into a single string and return it +function Context:finalize() + return concat(self.buffer) +end + +--- if value is truthy, format s and return it, otherwise return an empty string. +---@param value any +---@param s string +---@param ... any +---@return string +local function cond(value, s, ...) + if select('#', ...) > 0 then + s = s:format(...) + end + if value then + return s + else + return '' + end +end + +-- Format insert. Format string s if varargs are supplied, then insert it into tbl. +---@param tbl table +---@param s string +---@param ... any +local function insertfmt(tbl, s, ...) + if select('#', ...) > 0 then + s = s:format(...) + end + return insert(tbl, s) +end + +---given a value such as `{filename: string, index: integer}`, return it as a table. +---note it only accepts relevant values defined throughout the docs. +---@param str string +function Context:resolveTable(str, tbl) + tbl = tbl or {} + local body = str:match('{(.-)}') + for key, value in body:gmatch('(%S+):%s*([^%s,]+)') do + tbl[key] = value + end + return tbl +end + +---given an array of options output a formatted string array. +---@generic T: string[] +---@param tbl T? +---@return T +function Context:resolveOptions(options, tbl) + tbl = tbl or {} + for _, option in ipairs(options) do + insertfmt(tbl, '`%s`%s%s', + option.value, + cond(option.default, ' (default)'), + cond(option.description, ' — %s', option.description) + ) + end + return tbl +end + +---given a value such as `@name.of.alias`, return its value set in the aliases_map. +---@param type string +---@return string, table +function Context:resolveAlias(type, collected_fields) + collected_fields = collected_fields or {} + local name = type:match('^@(%S+)') + if not name then + return type, collected_fields + end + + local value = self.aliases_map[name] + assert(value, 'could not find alias @' .. name .. ', is it really defined?') + + -- Note: when the type is table, we only respect the first + -- value because table aliases can't have more than one + type = value[1].type + if type == 'table' then + -- TODO: handle when .value is not set + self:resolveTable(value[1].value, collected_fields) + else + self:resolveOptions(value, collected_fields) + end + return type, collected_fields +end + +---given an array of strings or a table of key/values format it into a bullet list and return it. +---@return string +local function formatBulletList(collected_tbl, prefix) + prefix = prefix or '' + local entries = {} + -- is it a list? + if collected_tbl[1] then + for _, entry in ipairs(collected_tbl) do + insertfmt(entries, '%s- %s', prefix, entry) + end + else + for k, v in pairs(collected_tbl) do + insertfmt(entries, '%s- `%s`: `%s`', prefix, k, v) + end + end + return concat(entries, '\n') +end + + +---Given a function definition, write its signature into the buffer. +---Example output `foo.bar(a[, b], c)`. +--- +---The parameters are analyzed for a more consistent and standard annotation. +---When the parameter has `omissible` set or it's an optional argument at the end +---of the parameters it will be wrapped in `[]` to indicate it can be completely left out. +--- +---Multiple parameters may set omissible to the same value to combine into one group. +---That is, to group them under one shared `[]`, i.e. `a[, b, c, d]`. +--- +---Internally this is done by parsing parameters into `collected_params`. +---For example, `a[, b], c, [, d, e]` will become `{a, {b}, c, {d, e}}`. +---Currently only one level down is supported (i.e. at most `a[, b]`, no `a[, b[, c]]`). +---@param interface string +---@param func method +---@param is_method boolean? +function Context:writeFuncSignature(interface, func, is_method) + local sep = is_method and ':' or '.' + local name = interface .. sep .. func.name + local collected_params = {} + + local last_required_param = 0 + for index, param in ipairs(func.params) do + if not param.optional then + last_required_param = index + end + end + + -- TODO: run this resolution recursively? + -- local last_required_group + local last_omit_group + for index, param in ipairs(func.params) do + -- if the parameter is not optional, or isn't explicitly omissible + -- and also isn't at the end of the parameters it cannot be omitted + if not param.optional + or not param.omissible and last_required_param > index then + insert(collected_params, param) + -- last_required_group = #collected_params + last_omit_group = nil + goto next + end + + -- is this the first parameter to be grouped for omit? + if not last_omit_group then + last_omit_group = {group = true} + insert(last_omit_group, param) + insert(collected_params, last_omit_group) + goto next + end + + -- if a previous parameter has been grouped, check if this one should + -- go with the old group or have its own group + local pass = { + ['nil'] = true, + ['true'] = true, + } + if last_omit_group[1].omissible == param.omissible + or (pass[tostring(last_omit_group[1].omissible)] and pass[tostring(param.omissible)]) then + insert(last_omit_group, param) + else + last_omit_group = {group = true} + insert(last_omit_group, param) + insert(collected_params, last_omit_group) + end + + ::next:: + end + + local param_buf = {} + for index, grp in ipairs(collected_params) do + if grp.group then + insertfmt(param_buf, '[') + for _, param in ipairs(grp) do + insertfmt(param_buf, '%s%s', cond(index > 1, ', '), param.name) + end + insert(param_buf, ']') + else + insertfmt(param_buf, '%s%s', cond(index > 1, ', '), grp.name) + -- insertfmt(param_buf, '%s%s', grp.name, cond(index < last_required_group, ', ')) + end + end + + local params = concat(param_buf) + local prefix = is_method and '> method form ' or '###' + self:write('%s `%s(%s)`\n\n', prefix, name, params) +end +--[[ +-- test usage +writeFuncSignature('', { + name = '', + params = { + { + name = 'foo', + optional = false, + }, + { + name = 'start', + optional = true, + omissible = 1, + }, + { + name = 'stop', + optional = true, + omissible = 1, + }, + { + name = 'bar', + optional = true, + omissible = false, + }, + }, +}) +os.exit() +]] + +---@param params params +function Context:writeParameters(params) + self:write('**Parameters:**\n') + local collected_params = {} + for _, param in pairs(params) do + local res_type, collected_fields = self:resolveAlias(param.type) + local fields = formatBulletList(collected_fields, '\t') + + local optional, default, description = '', '', '' + if param.optional then + optional = ' or `nil`' + end + if param.default then + default = fmt('(default: `%s`)', param.default) + end + if param.description and #param.description > 0 then + description = '— ' .. param.description + end + + local type = fmt('`%s`%s', + res_type, + optional + ) + + insertfmt(collected_params, '- `%s`: %s %s%s%s', + param.name, + type, + description, + default, + cond(#fields > 0, '\n' .. fields) + ) + end + self:write(concat(collected_params, '\n')) + self:write('\n\n') +end + +--- groups returns that are not in a group, and are not separated by a group, together. +local function groupReturnsLevel1(returns) + local groups = {} + local new_group = {} + local function flush() + if #new_group > 0 then + insert(groups, new_group) + new_group = {} + end + end + for _, group in ipairs(returns) do + if not group[1] then + insert(new_group, group) + else + flush() + insert(groups, group) + end + end + flush() + return groups +end + +-- the luv-style small/simple returns. +---@param returns returns +function Context:writeReturns(returns) + if #returns == 0 then + return self:write('**Returns**: Nothing.\n') + end + local collected_groups = {} -- the returns such as `a or b or c` + local collected_fields = {} -- the fields of any returned tables values + + local grouped_returns = groupReturnsLevel1(returns) + for _, group in ipairs(grouped_returns) do + local group_res = {} + for _, rtn in ipairs(group) do + local value, fields = self:resolveAlias(rtn.types) + if next(fields) then + insert(collected_fields, fields) + end + insertfmt(group_res, '%s%s', value, cond(rtn.nilable,'?')) + end + insertfmt(collected_groups, '`%s`', concat(group_res, ', ')) + end + + self:write('**Returns**: %s', concat(collected_groups, ' or ')) + + if next(collected_fields) then + for _, fields in ipairs(collected_fields) do + self:write('\n') + self:write(formatBulletList(fields)) + end + end + self:write('\n\n') +end + +-- much more expanded and detailed returns. +---@param returns returns +function Context:writeReturns2(returns) + if #returns == 0 then + return self:write('**Returns**: Nothing.\n') + end + local collected_groups = {} + local collected_fields = {} + + ---@type returns[] + local grouped_returns = groupReturnsLevel1(returns) + + -- simple one-group returns + if #grouped_returns == 1 then + local res = {} + local fields = {} + for _, rtn in ipairs(grouped_returns[1]) do + local value, field = self:resolveAlias(rtn.types) + insertfmt(res, '%s%s', + value, + cond(rtn.nilable, '?') + ) + if next(field) then + fields[rtn.types] = field + end + end + self:write('**Returns:** `%s`\n', concat(res, ', ')) + for name, field in pairs(fields) do + self:write('\nFor the fields of `%s`:\n%s\n', name, formatBulletList(field)) + end + return + end + + -- grouped returns + for _, group in ipairs(grouped_returns) do + local group_res = {} + local group_fields = {} + for i, rtn in ipairs(group) do + local value, fields = self:resolveAlias(rtn.types) + if next(fields) then + group_fields[rtn.types] = fields + end + insertfmt(group_res, '%d. %s%s', + i, + cond(rtn.name, '`%s`: `%s%s`%s', + rtn.name, + value, + cond(rtn.nilable, '?'), + cond(next(fields), '(`%s`)', rtn.types) + ), + rtn.description and (' — ' .. rtn.description) or '.' + ) + end + insert(collected_groups, '\n' .. concat(group_res, '\n')) + insert(collected_fields, group_fields) + end + + self:write('**Returns**:\n') + + for i, group in ipairs(collected_groups) do + self:write(group .. '\n') + if next(collected_fields[i]) then + for name, fields in pairs(collected_fields[i]) do + self:write('\n\tFor the fields of `%s`:\n%s\n', name, formatBulletList(fields, '\t')) + end + end + if next(collected_groups, i) then + self:write('\nOR\n') + end + end + self:write('\n') +end +-- TODO: decide which return format to use. +Context.writeReturns = Context.writeReturns2 + +---@param interface string +---@param func method +function Context:writeFunction(interface, func) + assert(func.name, 'a function must have name') + assert(func.params, 'a function must define params') + + -- the function definition + self:writeFuncSignature(interface, func) + -- method form + if func.method_form then + local method_interface = type(func.method_form) == 'string' and func.method_form or interface + self:writeFuncSignature(method_interface, func, true) + end + -- parameters + if #func.params > 0 then + self:writeParameters(func.params) + end + -- description + if func.description and #func.description > 0 then + self:write(func.description) + self:write('\n\n') + end + -- returns + self:writeReturns(func.returns) + + self:write('\n') +end + +function Context:writeModule(module) + self:write('# %s\n\n', module.title) + self:write('%s\n\n', module.description) +end + + +---@param docs_meta meta +local function generate(docs_meta) + local context = Context.initiate(docs_meta) + + context:writeModule(docs_meta) + for _, section in ipairs(docs_meta) do + context:write('## %s\n\n', section.title) + context:write('%s\n\n', section.description) + + for _, func in pairs(section.methods) do + context:writeFunction(section.name or docs_meta.name, func) + end + end + return context:finalize() +end + +return { + generate = generate, + extension = '.md' +} diff --git a/docs/modules/miniz.lua b/docs/modules/miniz.lua new file mode 100644 index 00000000..4445ac20 --- /dev/null +++ b/docs/modules/miniz.lua @@ -0,0 +1,907 @@ +---@alias types +---Indicates that this section represents a module. +---|'module' +---A section that consists of text only. +---|'text' +---Indicates that this section represents a class structure. +---|'class' +---Indicates that this section is a list of static functions. +---|'functions' +---Indicates that this section is a list of constant values. +---|'constants' + +---@alias alias_types {type: string, value?: string, default?: boolean, description?: string}[] +---@alias alias {name: string, types: alias_types} +---@alias aliases alias[] + +---@alias param {name: string, description: string, type: string, optional: boolean, omissible?: any, default?: string} +---@alias params param[] +---@alias returns {name: string, description: string, types: string, nilable?: boolean}[] +---@alias method {name: string, description: string, method_form?: string, params: params, returns: returns} +---@alias methods method[] + + +---@class section: table +---@field type string +---@field title string +---@field description string +---@field aliases aliases + +---A section that consists of text only. +---@class text_section: section +---@field type 'text' + +---A section that represents a class-like structure. +---@class class_section: section +---@field type 'class' +---@field name string +---@field parents string[] +---@field methods methods + +---A section containing a list of static functions. +---@class functions_section: section +---@field type 'functions' +---An array of methods definitions. +---@field methods methods +---@field source? string + +---A section containing a list of constant values. +---@class constants_section: section +---@field type 'constants' +---@field constants {name: string, type: string, value: string}[] + + +---@class meta: table +---The name of this docs/meta file. +---@field name string +---A title for the main section. +---@field title string +---A description for this module. +---@field description string +---The version of the module documented in this table. +---@field version string +---The version of this *documentation*. +---This is potentially useful when introducing what may be considered breaking change for tools using this file to generate docs. +---@field def_version string +---The type of the upper level section, most often a module. +---@field type types +---An array of sub-sections under module. +---@field [number] text_section | class_section | functions_section | constants_section + + +local fail_indicator = { + name = 'fail', + types = 'nil', +} +local fail_msg = { + name = 'failure_msg', + types = 'string', +} +local fail_group = { + { + fail_indicator, + fail_msg, + }, +} + +---@type meta +local meta = { + version = '10.1.0', -- the bindings version (currently the same as miniz version) + def_version = '1', -- an internal versioning of the definitions + + type = 'module', + name = 'miniz', + title = 'Miniz Bindings - lminiz', + description = [[ +Lua bindings for [miniz](https://github.com/richgel999/miniz), a minimal C library for zlib. + +Using miniz you are able to create and read zlib ZIP archives, luvi uses it internally to create executable bundles. +Note this bindings depends on `luvi` and `luv` and currently can't be used outside Luvi. + +For the purposes of writing and reading ZIP files take a look at `miniz.new_writer` and `miniz.new_reader`, +the rest of functions are either helpers or intended for deflate/inflate streams. + +**Version:** 10.1.0. + +**Available on Luvi:** `regular`, `tiny`. + +**Available on platform:** All. + +**Imported with:** `require('miniz')`. +]], + + { + type = 'functions', + title = '`miniz` — Base Module Functions', + description = 'functions to initiate operations.', + aliases = { + { + name = 'miniz.alias.mz_zip_flags', + types = { + { + type = 'integer', + value = '0x0100', + description = 'MZ_ZIP_FLAG_CASE_SENSITIVE', + }, + { + type = 'integer', + value = '0x0200', + description = 'MZ_ZIP_FLAG_IGNORE_PATH', + }, + { + type = 'integer', + value = '0x0400', + description = 'MZ_ZIP_FLAG_COMPRESSED_DATA', + }, + { + type = 'integer', + value = '0x0800', + description = 'MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY', + }, + { + type = 'integer', + value = '0x1000', + description = 'MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG', + }, + { + type = 'integer', + value = '0x2000', + description = 'MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY', + }, + { + type = 'integer', + value = '0x4000', + description = 'MZ_ZIP_FLAG_WRITE_ZIP64', + }, + { + type = 'integer', + value = '0x8000', + description = 'MZ_ZIP_FLAG_WRITE_ALLOW_READING', + }, + { + type = 'integer', + value = '0x10000', + description = 'MZ_ZIP_FLAG_ASCII_FILENAME', + }, + }, + }, + { + name = 'miniz.alias.tinfl_decompression_flags', + types = { + { + type = 'integer', + value = '1', + description = 'TINFL_FLAG_PARSE_ZLIB_HEADER - If set, the input has a valid zlib header and ends with an adler32 checksum (it\'s a valid zlib stream). Otherwise, the input is a raw deflate stream.', + }, + { + type = 'integer', + value = '2', + description = 'TINFL_FLAG_HAS_MORE_INPUT - If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input.', + }, + { + type = 'integer', + value = '4', + description = 'TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF - If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB).', + }, + { + type = 'integer', + value = '8', + description = 'TINFL_FLAG_COMPUTE_ADLER32 - Force adler-32 checksum computation of the decompressed bytes.', + }, + }, + }, + }, + methods = { + { + name = 'new_reader', + description = 'Creates a new miniz reader.', + params = { + { + name = 'path', + description = 'The path to the archive file the reader will read from.', + type = 'string', + optional = false, + }, + { + name = 'flags', + description = 'miniz initialization flags.', + type = '@miniz.alias.mz_zip_flags', + optional = true, + default = '0', + }, + }, + returns = { + { + name = 'zip_reader', + types = 'miniz_reader', + }, + unpack(fail_group), + }, + }, + { + name = 'new_writer', + description = 'Creates a new miniz writer.', + params = { + { + name = 'reserved_size', + description = 'The size (in bytes) at the archive beginning for miniz to reserve. Effectively offsets the actual beginning of the archive.', + type = 'integer', + optional = true, + default = '0', + }, + { + name = 'initial_allocation_size', + description = [[ +The archive size (in bytes) to allocate at initialization. +This is not the final size of the archive just the initial allocation, +set this if you have a good estimation about the archive size and you want to avoid unnecessary allocations. +]], + type = 'integer', + optional = true, + default = '131072', + }, + }, + returns = { + { + name = 'zip_writer', + types = 'miniz_writer', + nilable = false, + }, + }, + errors = true, + }, + { + name = 'inflate', + description = 'Inflates (decompresses) the input string into memory.\nThis operates on raw deflated data and not on a zlib format / ZIP archives, for that use `miniz.uncompress` / `miniz.new_reader` respectively.', + params = { + { + name = 'data', + description = 'The input buffer to inflate.', + type = 'string', + optional = false, + }, + { + name = 'flags', + description = 'miniz decompression flags.', + type = '@miniz.alias.tinfl_decompression_flags', + optional = true, + default = '0', + }, + }, + returns = { + { + name = 'inflated_data', + description = 'The inflated/decompressed data as a Lua string.', + types = 'string', + nilable = false, + } + }, + }, + { + name = 'deflate', + description = 'Deflates (compresses) the input data into memory.\nThe output of this is the deflated binary and not a valid zlib/ZIP on its own, for that use `miniz.compress` / `miniz.new_writer` respectively.', + params = { + { + name = 'data', + description = 'The input buffer to deflate.', + type = 'string', + optional = false, + }, + { + name = 'flags', + description = 'Miniz compression flags.', + type = '@miniz.alias.mz_zip_flags', + optional = true, + }, + }, + returns = { + { + name = 'deflated_data', + description = 'The deflated/compressed data as a Lua string.', + types = 'string', + nilable = false, + }, + }, + }, + { + name = 'adler32', + description = 'Calculates the Adler32 checksum of the provided string.', + params = { + { + name = 'adler', + description = 'The initial Adler32 checksum. More specifically this is first 16-bit A-portion of the checksum.', + type = 'integer', + optional = true, + default = '1', + }, + { + name = 'data', + description = 'The data to calculate the checksum for.', + type = 'string', + optional = true, + }, + }, + returns = { + { + name = 'checksum', + description = 'The calculated Adler32 checksum.', + types = 'integer', + nilable = false, + }, + }, + }, + { + name = 'crc32', + description = 'Calculates the CRC32 checksum of the provided string.', + params = { + { + name = 'crc32', + description = 'An initial CRC32 checksum.', + type = 'integer', + optional = true, + default = '0', + }, + { + name = 'data', + description = 'The data to calculate the checksum for.', + type = 'string', + optional = true, + }, + }, + returns = { + { + name = 'checksum', + description = 'The calculated CRC32 checksum.', + types = 'integer', + nilable = false, + }, + }, + }, + { + name = 'compress', + description = 'Compress the input string in zlib format.\nUnlike deflate, this will compress the data in a single call and output zlib-format, this is still not a ZIP archive, for that use `miniz.new_writer`.', + params = { + { + name = 'data', + description = 'The input data to compress.', + type = 'string', + optional = false, + }, + { + name = 'compression_level', + description = 'Determines the speed to compression ratio, the higher this value is the better compression and the slower it is.\nAllowed values are between 1-9.', + type = 'integer', + optional = true, + }, + }, + returns = { + { + name = 'output', + description = 'The zlib compressed data.', + types = 'string', + nilable = true, + }, + unpack(fail_group), + }, + errors = true, + }, + { + name = 'uncompress', + description = 'Decompress zlib compressed data.\nUnlike inflate, this will decompress the data in a single call assuming the input is in zlib-format. For unzipping files use `miniz.new_reader` instead.', + params = { + { + name = 'data', + description = 'The input data to decompress.', + type = 'string', + optional = false, + }, + { + name = 'initial_allocation', + description = [[ +The initial size (in bytes) to allocate for the output buffer. +This is not the final size of the output just the initial allocation, +set this if you have a good estimation and you want to avoid unnecessary allocations.]], + type = 'integer', + default = '#data * 2', + }, + }, + returns = { + { + name = 'output', + description = 'The uncompressed data.', + types = 'string', + nilable = true, + }, + unpack(fail_group), + errors = true, + }, + }, + { + name = 'version', + description = 'Returns the miniz version.', + params = {}, + returns = { + { + name = 'version', + description = 'The miniz version.', + types = 'string', + nilable = false, + }, + }, + }, + { + name = 'new_deflator', + description = 'Creates a new miniz_deflator stream.', + params = { + { + name = 'compression_level', + description = 'Determines the speed to compression ratio, the higher this value is the better compression and the slower it is.\nAllowed values are between 1-9.', + type = 'integer', + optional = true, + }, + }, + returns = { + { + name = 'stream', + description = 'The miniz deflator stream.', + types = 'miniz_deflator', + nilable = false, + }, + }, + errors = true, + }, + { + name = 'new_inflator', + description = 'Creates a new miniz_inflator stream.', + params = {}, + returns = { + { + name = 'stream', + description = 'The miniz deflator stream.', + types = 'miniz_deflator', + nilable = false, + }, + }, + errors = true, + }, + } + }, + + { + type = 'class', + name = 'miniz_reader', + title = '`miniz_reader` — Read archive from a file', + description = 'Initialize a reader for reading ZIP files and archives from a path.', + parents = {}, + aliases = { + { + name = 'miniz.alias.mz_zip_archive_file_stat', + types = { + { + type = 'table', + value = '{index: integer, version_made_by: integer, version_needed: integer, bit_flag: integer, method: integer, time: integer, crc32: integer, comp_size: integer, uncom_size: integer, internal_attr: integer, external_attr: integer, filename: string, comment: string}', + }, + }, + }, + }, + methods = { + { + name = 'get_num_files', + description = 'Returns the number of archived files.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + }, + returns = { + { + name = 'files', + description = 'the count of available files inside the archive.', + types = 'integer', + nilable = false, + }, + }, + }, + { + name = 'stat', + description = 'Returns the stats of a file/directory inside the archive.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + { + name = 'file_index', + description = 'A 1-based index of the desired entry.', + optional = false, + type = 'integer', + }, + }, + returns = { + { + name = 'stats', + description = 'The file stats.', + types = '@miniz.alias.mz_zip_archive_file_stat', + nilable = false, + }, + unpack(fail_group), + }, + }, + { + name = 'get_filename', + description = 'Returns the file/directory name archived at a specific index.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + { + name = 'file_index', + description = 'A 1-based index of the desired entry.', + optional = false, + type = 'integer', + }, + }, + returns = { + { + name = 'filename', + description = 'The name of the file at file_index.', + types = 'string', + nilable = true, + }, + unpack(fail_group), + }, + }, + { + name = 'is_directory', + description = 'Returns whether or not the entry at a specified index is a directory.\nNote: Unlike other methods, this will return `false` if the index provided does not exists.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + { + name = 'file_index', + description = 'A 1-based index of the desired entry.', + optional = false, + type = 'integer', + }, + }, + returns = { + { + name = 'is_directory', + description = 'whether or not the entry is a directory.', + types = 'boolean', + nilable = false, + }, + }, + }, + { + name = 'extract', + description = 'Extracts an entry into a Lua string.\nNote: Unlike other methods, if the index does not exists this will return an empty string.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + { + name = 'file_index', + description = 'A 1-based index of the desired entry.', + optional = false, + type = 'integer', + }, + { + name = 'flags', + description = 'Extraction flags.', + type = '@miniz.alias.mz_zip_flags', + optional = true, + }, + }, + returns = { + { + name = 'extracted', + description = 'The extracted entry as a Lua string, empty string if the entry is a directory or doesn\'t exists', + types = 'string', + nilable = false, + }, + }, + }, + { + name = 'locate_file', + description = 'Given the path of a file, return its index.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + { + name = 'path', + description = 'The file path to locate.', + type = 'string', + optional = false, + }, + { + name = 'flags', + description = 'Locate flags. Available flags are `MZ_ZIP_FLAG_IGNORE_PATH, MZ_ZIP_FLAG_CASE_SENSITIVE`.', + type = '@miniz.alias.mz_zip_flags', + optional = true, + }, + }, + returns = { + { + name = 'index', + description = 'The located index if found.', + types = 'integer', + nilable = true, + }, + unpack(fail_group), + }, + }, + { + name = 'get_offset', + description = 'If the archive does not start at the beginning of the ZIP, returns the offset (in bytes) at which the archive starts.', + method_form = 'reader', + params = { + { + name = 'reader', + type = 'miniz_reader', + optional = false, + }, + }, + returns = { + { + name = 'offset', + description = 'The offset at which the archive start in bytes.', + types = 'integer', + nilable = false, + }, + }, + }, + }, + }, + + { + type = 'class', + name = 'miniz_writer', + title = '`miniz_writer` — Write archives to a file', + description = 'Initialize a writer to create a new zlib archive.', + parents = {}, + aliases = {}, + methods = { + { + name = 'add_from_zip', + description = 'Copy a file from miniz_reader `source`.', + method_form = 'writer', + params = { + { + name = 'writer', + type = 'miniz_writer', + optional = false, + }, + { + name = 'source', + description = 'The archive from which to copy the file.', + optional = false, + type = 'miniz_reader', + }, + { + name = 'file_index', + description = 'A 1-based index of the desired entry.', + optional = false, + type = 'integer', + }, + }, + returns = {}, + errors = true, + }, + { + name = 'add', + description = 'Add a new entry at the specified path.\nNote: By default the compression level is set to 0.', + method_form = 'writer', + params = { + { + name = 'writer', + type = 'miniz_writer', + optional = false, + }, + { + name = 'path', + description = 'The path in the central directory (the archive) to add the data to.', + type = 'string', + optional = false, + }, + { + name = 'data', + description = 'The data that will be compressed and added into the archive', + type = 'string', + optional = false, + }, + { + name = 'level_and_flags', + description = 'The compression level, this is a number between 0-10, you may OR this with one of the `mz_zip_flags` flag values.', + type = 'integer', + optional = true, + default = '0', + }, + }, + returns = {}, + errors = true, + }, + { + name = 'finalize', + description = 'ZLIB encode and compress all of the added entries and output it into a string.', + method_form = 'writer', + params = { + { + name = 'writer', + type = 'miniz_writer', + optional = false, + }, + }, + returns = { + { + name = 'zip', + description = 'The archive binary data.', + types = 'string', + nilable = false, + }, + }, + errors = true, + }, + }, + }, + + { + type = 'class', + name = 'miniz_deflator', + title = '`miniz_deflator` — Deflate a stream of data', + description = 'Apply deflate on a stream of data.\nIn order to finalize the deflated data set `flush` to `"finish"`.\nNote: In case of an error, this will return a `fail`, and the deflated buffer.', + parents = {}, + aliases = { + { + name = 'miniz.alias.flush_values', + types = { + { + type = 'string', + value = '"no"', + description = 'Do no flushing on this call.', + default = true, + }, + { + type = 'string', + value = '"partial"', + }, + { + type = 'string', + value = '"sync"', + }, + { + type = 'string', + value = '"full"', + }, + { + type = 'string', + value = '"finish"', + description = 'Finalize the data and flush it.', + }, + { + type = 'string', + value = '"block"', + }, + } + }, + }, + methods = { + { + name = 'deflate', + description = 'Apply deflate on provided data chunk.', + method_form = 'deflator', + params = { + { + name = 'deflator', + type = 'miniz_deflator', + optional = false, + }, + { + name = 'data', + description = 'The data to deflate.', + type = 'string', + optional = false, + }, + { + name = 'flush', + description = 'Whether or not to flush, and the type of flushing.', + type = '@miniz.alias.flush_values', + optional = true, + }, + }, + returns = { + { + name = 'deflated', + description = 'The flushed deflated data.', + types = 'string', + nilable = true, + }, + { + fail_indicator, + fail_msg, + { + name = 'error_deflated', + description = 'When an error occurs, this will be the remainder of deflated data buffer before the failed deflate.', + types = 'string', + nilable = false, + }, + }, + }, + }, + }, + }, + + { + type = 'class', + name = 'miniz_inflator', + title = 'miniz_inflator` — Inflate a stream of data', + description = 'Apply inflate on a stream of data.\nIn order to finalize the inflated data set `flush` to `"finish"`.\nNote: In case of an error, this will return a `fail`, and the inflate buffer.', + parents = {}, + aliases = {}, + methods = { + { + name = 'inflate', + description = 'Apply inflate on provided data chunk.', + method_form = 'inflator', + params = { + { + name = 'inflator', + type = 'miniz_inflator', + optional = false, + }, + { + name = 'data', + description = 'The data to inflate.', + type = 'string', + optional = false, + }, + { + name = 'flush', + description = 'Whether or not to flush, and the type of flushing.', + type = '@miniz.alias.flush_values', + optional = true, + }, + }, + returns = { + { + name = 'inflated', + description = 'The flushed inflated data.', + types = 'string', + nilable = true, + }, + { + fail_indicator, + fail_msg, + { + name = 'error_inflated', + description = 'When an error occurs, this will be the remainder of inflated data buffer before the failed inflate.', + types = 'string', + nilable = false, + }, + }, + }, + }, + }, + }, +} + +return meta diff --git a/docs/output/markdown/miniz.md b/docs/output/markdown/miniz.md new file mode 100644 index 00000000..bbd1d368 --- /dev/null +++ b/docs/output/markdown/miniz.md @@ -0,0 +1,392 @@ +# Miniz Bindings - lminiz + +Lua bindings for [miniz](https://github.com/richgel999/miniz), a minimal C library for zlib. + +Using miniz you are able to create and read zlib ZIP archives, luvi uses it internally to create executable bundles. +Note this bindings depends on `luvi` and `luv` and currently can't be used outside Luvi. + +For the purposes of writing and reading ZIP files take a look at `miniz.new_writer` and `miniz.new_reader`, +the rest of functions are either helpers or intended for deflate/inflate streams. + +**Version:** 10.1.0. + +**Available on Luvi:** `regular`, `tiny`. + +**Available on platform:** All. + +**Imported with:** `require('miniz')`. + + +## `miniz` — Base Module Functions + +functions to initiate operations. + +### `miniz.new_reader(path[, flags])` + +**Parameters:** +- `path`: `string` — The path to the archive file the reader will read from. +- `flags`: `integer` or `nil` — miniz initialization flags.(default: `0`) + - `0x0100` — MZ_ZIP_FLAG_CASE_SENSITIVE + - `0x0200` — MZ_ZIP_FLAG_IGNORE_PATH + - `0x0400` — MZ_ZIP_FLAG_COMPRESSED_DATA + - `0x0800` — MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY + - `0x1000` — MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG + - `0x2000` — MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY + - `0x4000` — MZ_ZIP_FLAG_WRITE_ZIP64 + - `0x8000` — MZ_ZIP_FLAG_WRITE_ALLOW_READING + - `0x10000` — MZ_ZIP_FLAG_ASCII_FILENAME + +Creates a new miniz reader. + +**Returns**: `miniz_reader` or `nil, string` + + +### `miniz.new_writer([reserved_sizeinitial_allocation_size])` + +**Parameters:** +- `reserved_size`: `integer` or `nil` — The size (in bytes) at the archive beginning for miniz to reserve. Effectively offsets the actual beginning of the archive.(default: `0`) +- `initial_allocation_size`: `integer` or `nil` — The archive size (in bytes) to allocate at initialization. +This is not the final size of the archive just the initial allocation, +set this if you have a good estimation about the archive size and you want to avoid unnecessary allocations. +(default: `131072`) + +Creates a new miniz writer. + +**Returns**: `miniz_writer` + + +### `miniz.inflate(data[, flags])` + +**Parameters:** +- `data`: `string` — The input buffer to inflate. +- `flags`: `integer` or `nil` — miniz decompression flags.(default: `0`) + - `1` — TINFL_FLAG_PARSE_ZLIB_HEADER - If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. + - `2` — TINFL_FLAG_HAS_MORE_INPUT - If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. + - `4` — TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF - If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). + - `8` — TINFL_FLAG_COMPUTE_ADLER32 - Force adler-32 checksum computation of the decompressed bytes. + +Inflates (decompresses) the input string into memory. +This operates on raw deflated data and not on a zlib format / ZIP archives, for that use `miniz.uncompress` / `miniz.new_reader` respectively. + +**Returns**: `string` + + +### `miniz.deflate(data[, flags])` + +**Parameters:** +- `data`: `string` — The input buffer to deflate. +- `flags`: `integer` or `nil` — Miniz compression flags. + - `0x0100` — MZ_ZIP_FLAG_CASE_SENSITIVE + - `0x0200` — MZ_ZIP_FLAG_IGNORE_PATH + - `0x0400` — MZ_ZIP_FLAG_COMPRESSED_DATA + - `0x0800` — MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY + - `0x1000` — MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG + - `0x2000` — MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY + - `0x4000` — MZ_ZIP_FLAG_WRITE_ZIP64 + - `0x8000` — MZ_ZIP_FLAG_WRITE_ALLOW_READING + - `0x10000` — MZ_ZIP_FLAG_ASCII_FILENAME + +Deflates (compresses) the input data into memory. +The output of this is the deflated binary and not a valid zlib/ZIP on its own, for that use `miniz.compress` / `miniz.new_writer` respectively. + +**Returns**: `string` + + +### `miniz.adler32([adlerdata])` + +**Parameters:** +- `adler`: `integer` or `nil` — The initial Adler32 checksum. More specifically this is first 16-bit A-portion of the checksum.(default: `1`) +- `data`: `string` or `nil` — The data to calculate the checksum for. + +Calculates the Adler32 checksum of the provided string. + +**Returns**: `integer` + + +### `miniz.crc32([crc32data])` + +**Parameters:** +- `crc32`: `integer` or `nil` — An initial CRC32 checksum.(default: `0`) +- `data`: `string` or `nil` — The data to calculate the checksum for. + +Calculates the CRC32 checksum of the provided string. + +**Returns**: `integer` + + +### `miniz.compress(data[, compression_level])` + +**Parameters:** +- `data`: `string` — The input data to compress. +- `compression_level`: `integer` or `nil` — Determines the speed to compression ratio, the higher this value is the better compression and the slower it is. +Allowed values are between 1-9. + +Compress the input string in zlib format. +Unlike deflate, this will compress the data in a single call and output zlib-format, this is still not a ZIP archive, for that use `miniz.new_writer`. + +**Returns**: `string?` or `nil, string` + + +### `miniz.uncompress(data, initial_allocation)` + +**Parameters:** +- `data`: `string` — The input data to decompress. +- `initial_allocation`: `integer` — The initial size (in bytes) to allocate for the output buffer. +This is not the final size of the output just the initial allocation, +set this if you have a good estimation and you want to avoid unnecessary allocations.(default: `#data * 2`) + +Decompress zlib compressed data. +Unlike inflate, this will decompress the data in a single call assuming the input is in zlib-format. For unzipping files use `miniz.new_reader` instead. + +**Returns**: `string?` or `nil, string` + + +### `miniz.version()` + +Returns the miniz version. + +**Returns**: `string` + + +### `miniz.new_deflator([compression_level])` + +**Parameters:** +- `compression_level`: `integer` or `nil` — Determines the speed to compression ratio, the higher this value is the better compression and the slower it is. +Allowed values are between 1-9. + +Creates a new miniz_deflator stream. + +**Returns**: `miniz_deflator` + + +### `miniz.new_inflator()` + +Creates a new miniz_inflator stream. + +**Returns**: `miniz_deflator` + + +## `miniz_reader` — Read archive from a file + +Initialize a reader for reading ZIP files and archives from a path. + +### `miniz_reader.get_num_files(reader)` + +> method form `reader:get_num_files(reader)` + +**Parameters:** +- `reader`: `miniz_reader` + +Returns the number of archived files. + +**Returns**: `integer` + + +### `miniz_reader.stat(reader, file_index)` + +> method form `reader:stat(reader, file_index)` + +**Parameters:** +- `reader`: `miniz_reader` +- `file_index`: `integer` — A 1-based index of the desired entry. + +Returns the stats of a file/directory inside the archive. + +**Returns**: `table` or `nil, string` +- `comp_size`: `integer` +- `uncom_size`: `integer` +- `index`: `integer` +- `external_attr`: `integer` +- `comment`: `string` +- `crc32`: `integer` +- `filename`: `string` +- `time`: `integer` +- `internal_attr`: `integer` +- `version_made_by`: `integer` +- `version_needed`: `integer` +- `bit_flag`: `integer` +- `method`: `integer` + + +### `miniz_reader.get_filename(reader, file_index)` + +> method form `reader:get_filename(reader, file_index)` + +**Parameters:** +- `reader`: `miniz_reader` +- `file_index`: `integer` — A 1-based index of the desired entry. + +Returns the file/directory name archived at a specific index. + +**Returns**: `string?` or `nil, string` + + +### `miniz_reader.is_directory(reader, file_index)` + +> method form `reader:is_directory(reader, file_index)` + +**Parameters:** +- `reader`: `miniz_reader` +- `file_index`: `integer` — A 1-based index of the desired entry. + +Returns whether or not the entry at a specified index is a directory. +Note: Unlike other methods, this will return `false` if the index provided does not exists. + +**Returns**: `boolean` + + +### `miniz_reader.extract(reader, file_index[, flags])` + +> method form `reader:extract(reader, file_index[, flags])` + +**Parameters:** +- `reader`: `miniz_reader` +- `file_index`: `integer` — A 1-based index of the desired entry. +- `flags`: `integer` or `nil` — Extraction flags. + - `0x0100` — MZ_ZIP_FLAG_CASE_SENSITIVE + - `0x0200` — MZ_ZIP_FLAG_IGNORE_PATH + - `0x0400` — MZ_ZIP_FLAG_COMPRESSED_DATA + - `0x0800` — MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY + - `0x1000` — MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG + - `0x2000` — MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY + - `0x4000` — MZ_ZIP_FLAG_WRITE_ZIP64 + - `0x8000` — MZ_ZIP_FLAG_WRITE_ALLOW_READING + - `0x10000` — MZ_ZIP_FLAG_ASCII_FILENAME + +Extracts an entry into a Lua string. +Note: Unlike other methods, if the index does not exists this will return an empty string. + +**Returns**: `string` + + +### `miniz_reader.locate_file(reader, path[, flags])` + +> method form `reader:locate_file(reader, path[, flags])` + +**Parameters:** +- `reader`: `miniz_reader` +- `path`: `string` — The file path to locate. +- `flags`: `integer` or `nil` — Locate flags. Available flags are `MZ_ZIP_FLAG_IGNORE_PATH, MZ_ZIP_FLAG_CASE_SENSITIVE`. + - `0x0100` — MZ_ZIP_FLAG_CASE_SENSITIVE + - `0x0200` — MZ_ZIP_FLAG_IGNORE_PATH + - `0x0400` — MZ_ZIP_FLAG_COMPRESSED_DATA + - `0x0800` — MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY + - `0x1000` — MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG + - `0x2000` — MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY + - `0x4000` — MZ_ZIP_FLAG_WRITE_ZIP64 + - `0x8000` — MZ_ZIP_FLAG_WRITE_ALLOW_READING + - `0x10000` — MZ_ZIP_FLAG_ASCII_FILENAME + +Given the path of a file, return its index. + +**Returns**: `integer?` or `nil, string` + + +### `miniz_reader.get_offset(reader)` + +> method form `reader:get_offset(reader)` + +**Parameters:** +- `reader`: `miniz_reader` + +If the archive does not start at the beginning of the ZIP, returns the offset (in bytes) at which the archive starts. + +**Returns**: `integer` + + +## `miniz_writer` — Write archives to a file + +Initialize a writer to create a new zlib archive. + +### `miniz_writer.add_from_zip(writer, source, file_index)` + +> method form `writer:add_from_zip(writer, source, file_index)` + +**Parameters:** +- `writer`: `miniz_writer` +- `source`: `miniz_reader` — The archive from which to copy the file. +- `file_index`: `integer` — A 1-based index of the desired entry. + +Copy a file from miniz_reader `source`. + +**Returns**: Nothing. + +### `miniz_writer.add(writer, path, data[, level_and_flags])` + +> method form `writer:add(writer, path, data[, level_and_flags])` + +**Parameters:** +- `writer`: `miniz_writer` +- `path`: `string` — The path in the central directory (the archive) to add the data to. +- `data`: `string` — The data that will be compressed and added into the archive +- `level_and_flags`: `integer` or `nil` — The compression level, this is a number between 0-10, you may OR this with one of the `mz_zip_flags` flag values.(default: `0`) + +Add a new entry at the specified path. +Note: By default the compression level is set to 0. + +**Returns**: Nothing. + +### `miniz_writer.finalize(writer)` + +> method form `writer:finalize(writer)` + +**Parameters:** +- `writer`: `miniz_writer` + +ZLIB encode and compress all of the added entries and output it into a string. + +**Returns**: `string` + + +## `miniz_deflator` — Deflate a stream of data + +Apply deflate on a stream of data. +In order to finalize the deflated data set `flush` to `"finish"`. +Note: In case of an error, this will return a `fail`, and the deflated buffer. + +### `miniz_deflator.deflate(deflator, data[, flush])` + +> method form `deflator:deflate(deflator, data[, flush])` + +**Parameters:** +- `deflator`: `miniz_deflator` +- `data`: `string` — The data to deflate. +- `flush`: `string` or `nil` — Whether or not to flush, and the type of flushing. + - `"no"` (default) — Do no flushing on this call. + - `"partial"` + - `"sync"` + - `"full"` + - `"finish"` — Finalize the data and flush it. + - `"block"` + +Apply deflate on provided data chunk. + +**Returns**: `string?` or `nil, string, string` + + +## miniz_inflator` — Inflate a stream of data + +Apply inflate on a stream of data. +In order to finalize the inflated data set `flush` to `"finish"`. +Note: In case of an error, this will return a `fail`, and the inflate buffer. + +### `miniz_inflator.inflate(inflator, data[, flush])` + +> method form `inflator:inflate(inflator, data[, flush])` + +**Parameters:** +- `inflator`: `miniz_inflator` +- `data`: `string` — The data to inflate. +- `flush`: `string` or `nil` — Whether or not to flush, and the type of flushing. + - `"no"` (default) — Do no flushing on this call. + - `"partial"` + - `"sync"` + - `"full"` + - `"finish"` — Finalize the data and flush it. + - `"block"` + +Apply inflate on provided data chunk. + +**Returns**: `string?` or `nil, string, string` + +