initial commit

This commit is contained in:
2025-03-08 23:22:10 +02:00
commit 7571cfbca7
24 changed files with 2612 additions and 0 deletions

50
lib/build.lua Normal file
View File

@@ -0,0 +1,50 @@
local function unresolve_require(paths, f)
for _, pattern in ipairs(paths) do
local path = f:match(pattern);
if path then
return path:gsub("%/", ".");
end
end
error("path " .. f .. " couldn't be resolved to any valid module");
end
local function all_loader(paths, ...)
local files = { ... };
local function coro_entry()
for _, f in ipairs(files) do
local name = unresolve_require(paths, f);
local bytecode = string.dump(assert(load(io.lines(f, 1024), "@" .. f, "t", nil)));
coroutine.yield(("package.preload[%q] = load(%q, %q, 'b');"):format(name, bytecode, f));
end
coroutine.yield("require 'init' (...);");
end
return coroutine.wrap(coro_entry);
end
local function main(root, out, ...)
local paths = { root .. "/(.+).lua", root .. "/(.+)/init.lua" };
for el in package.path:gmatch "[^;]+" do
paths[#paths + 1] = el:gsub("?", "(.+)");
end
local res = string.dump(assert(load(all_loader(paths, ...), "@<entry>", "t", nil)));
local f = assert(io.open(out, "w"));
f:write "#include <stdint.h>\n";
f:write "#define BYTECODE entry_bytecode\n";
f:write "static const uint8_t entry_bytecode[] = { ";
for i = 1, #res do
f:write(string.byte(res, i), ", ");
end
f:write "};";
assert(f:close());
end
main(...);

771
lib/fmt.c Normal file
View File

@@ -0,0 +1,771 @@
// Similar in concept to Lua's pack and unpack
// Main differences are:
// - Some formats are reinterpreted to mean the same thing across architectures
// - Default endianness is big-endian (but that can easily be changed, of course)
// - Formats are parsed once and used many times, to reduce overhead
// - Polyfills will store packers and unpackers in weak tables
// - Default alignment is no alignment
// - The maximum size of an integer is 64 bits (instead of 128)
#include <lauxlib.h>
#include <lua.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "lib.h"
#define FMT_UDATA_NAME "fmt.meta"
typedef enum {
FMT_INT8,
FMT_UINT8,
FMT_INT16,
FMT_UINT16,
FMT_INT32,
FMT_UINT32,
FMT_INT64,
FMT_UINT64,
FMT_FLOAT32,
FMT_FLOAT64,
FMT_STR_FIXED,
FMT_STR8,
FMT_STR16,
FMT_STR32,
FMT_STR64,
FMT_STR_ZERO,
FMT_PADDING,
} fmt_type_t;
typedef enum {
FMT_OK,
FMT_BAD_ARGS,
FMT_INCOMPLETE_OP,
FMT_BAD_OP_ARG,
FMT_BAD_OP,
} fmt_code_t;
typedef struct {
fmt_type_t type;
size_t size, align;
} fmt_segment_t;
typedef struct {
bool little_endian;
size_t max_align;
size_t segments_n;
fmt_segment_t segments[];
} fmt_t;
static int fmt_parse_width(size_t *i, const char *raw, size_t raw_size) {
if (*i < raw_size) {
switch (raw[(*i)++]) {
case '1': return 1;
case '2': return 2;
case '4': return 4;
case '8': return 8;
default:
(*i)--;
return 8;
}
}
else return 8;
}
static fmt_code_t fmt_parse_fmt(lua_State *ctx, const char *raw, size_t raw_size) {
if (raw == NULL || *raw == 0) return FMT_BAD_ARGS;
if (raw_size == -1) raw_size = strlen(raw);
fmt_segment_t segments[raw_size];
size_t segments_n = 0;
bool little_endian;
bool max_align;
size_t i = 0;
bool curr_padding = false;
while (true) {
if (i >= raw_size) break;
fmt_segment_t segment;
switch (raw[i++]) {
case '<': little_endian = true; continue;
case '>':
case '=': little_endian = false; continue;
case '!':
if (i < raw_size && (raw[i] < '0' || raw[i] > '9')) {
max_align = raw[i] - '0';
i++;
}
else max_align = 1;
continue;
case 'b': segment = (fmt_segment_t){ .type = FMT_INT8, .align = 1 }; break;
case 'B': segment = (fmt_segment_t){ .type = FMT_UINT8, .align = 1 }; break;
case 'h': segment = (fmt_segment_t){ .type = FMT_INT16, .align = 2 }; break;
case 'H': segment = (fmt_segment_t){ .type = FMT_UINT16, .align = 2 }; break;
case 'j': segment = (fmt_segment_t){ .type = FMT_INT32, .align = 4 }; break;
case 'J': segment = (fmt_segment_t){ .type = FMT_UINT32, .align = 4 }; break;
case 'l': segment = (fmt_segment_t){ .type = FMT_INT64, .align = 8 }; break;
case 'L':
case 'T': segment = (fmt_segment_t){ .type = FMT_UINT64, .align = 8 }; break;
case 'f': segment = (fmt_segment_t){ .type = FMT_FLOAT32, .align = 4 }; break;
case 'd':
case 'n': segment = (fmt_segment_t){ .type = FMT_FLOAT64, .align = 8 }; break;
case 'i':
switch (fmt_parse_width(&i, raw, raw_size)) {
case 1: segment = (fmt_segment_t){ .type = FMT_INT8, .align = 1 }; break;
case 2: segment = (fmt_segment_t){ .type = FMT_INT16, .align = 2 }; break;
case 4: segment = (fmt_segment_t){ .type = FMT_INT32, .align = 4 }; break;
case 8: segment = (fmt_segment_t){ .type = FMT_INT64, .align = 8 }; break;
}
break;
case 'I':
switch (fmt_parse_width(&i, raw, raw_size)) {
case 1: segment = (fmt_segment_t){ .type = FMT_UINT8, .align = 1 }; break;
case 2: segment = (fmt_segment_t){ .type = FMT_UINT16, .align = 2 }; break;
case 4: segment = (fmt_segment_t){ .type = FMT_UINT32, .align = 4 }; break;
case 8: segment = (fmt_segment_t){ .type = FMT_UINT64, .align = 8 }; break;
}
break;
case 'z': segment = (fmt_segment_t){ .type = FMT_STR_ZERO, .align = 1 }; break;
case 'c':
bool has_size;
size_t str_size;
while (i >= raw_size && raw[i] >= '0' && raw[i] <= '9') {
str_size *= 10;
str_size += raw[i] - '0';
has_size = true;
i++;
}
if (!has_size) return FMT_INCOMPLETE_OP;
segment = (fmt_segment_t){ .type = FMT_STR_FIXED, .size = str_size, .align = 1 };
break;
case 's':
switch (fmt_parse_width(&i, raw, raw_size)) {
case 1: segment = (fmt_segment_t){ .type = FMT_STR8, .align = 1 }; break;
case 2: segment = (fmt_segment_t){ .type = FMT_STR16, .align = 2 }; break;
case 4: segment = (fmt_segment_t){ .type = FMT_STR32, .align = 4 }; break;
case 8: segment = (fmt_segment_t){ .type = FMT_STR64, .align = 8 }; break;
}
break;
case 'x': segment = (fmt_segment_t){ .type = FMT_PADDING, .size = 1, .align = 1 }; break;
case 'X':
curr_padding = true;
continue;
case ' ':
break;
default:
return FMT_BAD_OP;
}
if (curr_padding) {
// TODO: is this correct?
switch (segment.type) {
case FMT_INT8:
case FMT_UINT8:
case FMT_STR8:
segment = (fmt_segment_t){ .type = FMT_PADDING, .size = 1, .align = 1 };
break;
case FMT_INT16:
case FMT_UINT16:
case FMT_STR16:
segment = (fmt_segment_t){ .type = FMT_PADDING, .size = 2, .align = 2 };
break;
case FMT_INT32:
case FMT_UINT32:
case FMT_FLOAT32:
case FMT_STR32:
segment = (fmt_segment_t){ .type = FMT_PADDING, .size = 4, .align = 4 };
break;
case FMT_INT64:
case FMT_UINT64:
case FMT_FLOAT64:
case FMT_STR64:
segment = (fmt_segment_t){ .type = FMT_PADDING, .size = 8, .align = 8 };
break;
case FMT_STR_FIXED:
segment = (fmt_segment_t){ .type = FMT_PADDING, .size = segment.size, .align = segment.size };
break;
default:
return FMT_BAD_OP_ARG;
}
}
segments[segments_n++] = segment;
continue;
}
if (curr_padding) return FMT_INCOMPLETE_OP;
for (size_t i = 0; i < segments_n; i++) {
// TODO: this might not be correct...
if (segments[i].align > max_align) segments[i].align = 8;
if (segments[i].align > max_align) segments[i].align = 4;
if (segments[i].align > max_align) segments[i].align = 2;
if (segments[i].align > max_align) segments[i].align = 1;
}
fmt_t *res = lua_newuserdata(ctx, sizeof *res + sizeof *segments * segments_n);
luaL_getmetatable(ctx, FMT_UDATA_NAME);
lua_setmetatable(ctx, -2);
res->little_endian = little_endian;
res->max_align = max_align;
res->segments_n = segments_n;
memcpy(res->segments, segments, sizeof *segments * segments_n);
return FMT_OK;
}
typedef struct {
char *arr;
size_t cap, n;
} write_buff_t;
static void write_buff_append(write_buff_t *buff, const char *data, size_t size) {
size_t new_cap = buff->cap;
while (new_cap < buff->n + size) {
if (new_cap == 0) new_cap = 16;
else new_cap *= 2;
}
if (new_cap != buff->cap) {
if (buff->cap == 0) {
buff->arr = malloc(new_cap);
}
else {
buff->arr = realloc(buff->arr, new_cap);
}
buff->cap = new_cap;
}
if (data == NULL) {
memset(buff->arr + buff->n, 0, size);
}
else {
memcpy(buff->arr + buff->n, data, size);
}
buff->n += size;
}
static void write_buff_fit(write_buff_t *buff) {
if (buff->arr == NULL) return;
if (buff->n == 0) {
free(buff->arr);
buff->cap = 0;
}
else {
buff->cap = buff->n;
buff->arr = realloc(buff->arr, buff->n);
}
}
static bool fmt_read_uint8(const uint8_t *raw, size_t *i, size_t size, uint8_t *res) {
if ((*i) + 1 > size || *i < 0) return false;
*res = raw[(*i)++];
return true;
}
static bool fmt_read_int8(const uint8_t *raw, size_t *i, size_t size, int8_t *res) {
return fmt_read_uint8(raw, i, size, (uint8_t*)res);
}
static bool fmt_read_uint16(const uint8_t *raw, size_t *i, size_t size, uint16_t *res, bool little_endian) {
if ((*i) + 2 > size || *i < 0) return false;
uint16_t a = raw[(*i)++];
uint16_t b = raw[(*i)++];
if (little_endian) *res = a | b << 8;
else *res = a << 8 | b;
return true;
}
static bool fmt_read_int16(const uint8_t *raw, size_t *i, size_t size, int16_t *res, bool little_endian) {
return fmt_read_uint16(raw, i, size, (uint16_t*)res, little_endian);
}
static bool fmt_read_uint32(const uint8_t *raw, size_t *i, size_t size, uint32_t *res, bool little_endian) {
if ((*i) + 4 > size || *i < 0) return false;
uint8_t a = raw[(*i)++];
uint8_t b = raw[(*i)++];
uint8_t c = raw[(*i)++];
uint8_t d = raw[(*i)++];
if (little_endian) *res = a | b << 8 | c << 16 | d << 24;
else *res = a << 24 | b << 16 | c << 8 | d;
return true;
}
static bool fmt_read_int32(const uint8_t *raw, size_t *i, size_t size, int32_t *res, bool little_endian) {
return fmt_read_uint32(raw, i, size, (uint32_t*)res, little_endian);
}
static bool fmt_read_uint64(const uint8_t *raw, size_t *i, size_t size, uint64_t *res, bool little_endian) {
if ((*i) + 8 > size || *i < 0) return false;
uint64_t a = raw[(*i)++];
uint64_t b = raw[(*i)++];
uint64_t c = raw[(*i)++];
uint64_t d = raw[(*i)++];
uint64_t e = raw[(*i)++];
uint64_t f = raw[(*i)++];
uint64_t g = raw[(*i)++];
uint64_t h = raw[(*i)++];
if (little_endian) *res = a | b << 8 | c << 16 | d << 24 | e << 32 | f << 40 | g << 48 | h << 56;
else *res = a << 56 | b << 48 | c << 40 | d << 32 | e << 24 | f << 16 | g << 8 | h;
return true;
}
static bool fmt_read_int64(const uint8_t *raw, size_t *i, size_t size, int64_t *res, bool little_endian) {
return fmt_read_uint64(raw, i, size, (uint64_t*)res, little_endian);
}
static bool fmt_read_float32(const uint8_t *raw, size_t *i, size_t size, float *res, bool little_endian) {
if ((*i) + 4 > size || *i < 0) return false;
uint8_t a = raw[(*i)++];
uint8_t b = raw[(*i)++];
uint8_t c = raw[(*i)++];
uint8_t d = raw[(*i)++];
// TODO: is this portable enough?
if (little_endian) *res = *(float*)(uint8_t[]) { a, b, c, d };
else *res = *(float*)(uint8_t[]) { d, c, b, a };
return true;
}
static bool fmt_read_float64(const uint8_t *raw, size_t *i, size_t size, double *res, bool little_endian) {
if ((*i) + 8 > size || *i < 0) return false;
uint8_t a = raw[(*i)++];
uint8_t b = raw[(*i)++];
uint8_t c = raw[(*i)++];
uint8_t d = raw[(*i)++];
uint8_t e = raw[(*i)++];
uint8_t f = raw[(*i)++];
uint8_t g = raw[(*i)++];
uint8_t h = raw[(*i)++];
// TODO: is this portable enough?
if (little_endian) *res = *(float*)(uint8_t[]) { a, b, c, d, e, f, g, h };
else *res = *(float*)(uint8_t[]) { h, g, f, e, d, c, b, a };
return true;
}
static bool fmt_read_string(lua_State *ctx, fmt_segment_t segment, const uint8_t *raw, size_t *i, size_t size, bool little_endian) {
uint64_t len;
switch (segment.type) {
case FMT_STR8: {
uint8_t len8;
if (!fmt_read_uint8(raw, i, size, &len8)) return false;
break;
}
case FMT_STR16: {
uint16_t len16;
if (!fmt_read_uint16(raw, i, size, &len16, little_endian)) return false;
len = len16;
break;
}
case FMT_STR32: {
uint32_t len32;
if (!fmt_read_uint32(raw, i, size, &len32, little_endian)) return false;
len = len32;
break;
}
case FMT_STR64:
if (!fmt_read_uint64(raw, i, size, &len, little_endian)) return false;
break;
case FMT_STR_FIXED:
len = segment.size;
break;
case FMT_STR_ZERO:
len = strnlen((const char*)(raw + *i), size - *i);
if (len >= size - *i) return false;
break;
default:
return false;
}
fprintf(stderr, "%lu %lu", len, *i);
if ((*i) + len > size) return false;
if (*i < 0) return false;
char data[len];
memcpy(data, raw + (*i), len);
lua_pushlstring(ctx, data, len);
return true;
}
static void fmt_write_uint8(write_buff_t *buff, uint8_t val) {
write_buff_append(buff, (char[]){ val }, 1);
}
static void fmt_write_int8(write_buff_t *buff, int8_t val) {
return fmt_write_uint8(buff, val);
}
static void fmt_write_uint16(write_buff_t *buff, uint16_t val, bool little_endian) {
uint8_t a = val & 0xFF;
uint8_t b = val >> 8 & 0xFF;
if (little_endian) write_buff_append(buff, (char[]){ a, b }, 2);
else write_buff_append(buff, (char[]){ b, a }, 2);
}
static void fmt_write_int16(write_buff_t *buff, uint16_t val, bool little_endian) {
fmt_write_uint16(buff, val, little_endian);
}
static void fmt_write_uint32(write_buff_t *buff, uint32_t val, bool little_endian) {
uint8_t a = val & 0xFF;
uint8_t b = val >> 8 & 0xFF;
uint8_t c = val >> 16 & 0xFF;
uint8_t d = val >> 24 & 0xFF;
if (little_endian) write_buff_append(buff, (char[]){ a, b, c, d }, 4);
else write_buff_append(buff, (char[]){ d, c, b, a }, 4);
}
static void fmt_write_int32(write_buff_t *buff, uint32_t val, bool little_endian) {
fmt_write_uint32(buff, val, little_endian);
}
static void fmt_write_uint64(write_buff_t *buff, uint64_t val, bool little_endian) {
uint8_t a = val & 0xFF;
uint8_t b = val >> 8 & 0xFF;
uint8_t c = val >> 16 & 0xFF;
uint8_t d = val >> 24 & 0xFF;
uint8_t e = val >> 32 & 0xFF;
uint8_t f = val >> 40 & 0xFF;
uint8_t g = val >> 48 & 0xFF;
uint8_t h = val >> 56 & 0xFF;
if (little_endian) write_buff_append(buff, (char[]){ a, b, c, d, e, f, g, h }, 8);
else write_buff_append(buff, (char[]){ h, g, f, e, d, c, b, a }, 8);
}
static void fmt_write_int64(write_buff_t *buff, uint64_t val, bool little_endian) {
fmt_write_uint64(buff, val, little_endian);
}
static void fmt_write_float32(write_buff_t *buff, float val, bool little_endian) {
uint32_t ival = *(uint32_t*)&val;
uint8_t a = ival & 0xFF;
uint8_t b = ival >> 8 & 0xFF;
uint8_t c = ival >> 16 & 0xFF;
uint8_t d = ival >> 24 & 0xFF;
if (little_endian) write_buff_append(buff, (char[]){ a, b, c, d }, 4);
else write_buff_append(buff, (char[]){ d, c, b, a }, 4);
}
static void fmt_write_float64(write_buff_t *buff, double val, bool little_endian) {
uint64_t ival = *(uint64_t*)&val;
uint8_t a = ival & 0xFF;
uint8_t b = ival >> 8 & 0xFF;
uint8_t c = ival >> 16 & 0xFF;
uint8_t d = ival >> 24 & 0xFF;
uint8_t e = ival >> 32 & 0xFF;
uint8_t f = ival >> 40 & 0xFF;
uint8_t g = ival >> 48 & 0xFF;
uint8_t h = ival >> 56 & 0xFF;
if (little_endian) write_buff_append(buff, (char[]){ a, b, c, d, e, f, g, h }, 8);
else write_buff_append(buff, (char[]){ h, g, f, e, d, c, b, a }, 8);
}
static bool fmt_write_string(write_buff_t *buff, fmt_segment_t segment, const char *str, size_t len, bool little_endian) {
switch (segment.type) {
case FMT_STR8:
if (len > 256) return false;
fmt_write_uint8(buff, (uint8_t)len);
write_buff_append(buff, str, len);
return true;
case FMT_STR16:
if (len > 0x10000) return false;
fmt_write_uint16(buff, (uint16_t)len, little_endian);
write_buff_append(buff, str, len);
return true;
case FMT_STR32:
if (len > 0x100000000) return false;
fmt_write_uint32(buff, (uint32_t)len, little_endian);
write_buff_append(buff, str, len);
return true;
case FMT_STR64:
fmt_write_uint64(buff, len, little_endian);
write_buff_append(buff, str, len);
return true;
case FMT_STR_FIXED:
if (len > segment.size) return false;
write_buff_append(buff, str, len);
write_buff_append(buff, NULL, segment.size - len);
return true;
case FMT_STR_ZERO:
if (strlen(str) != len) return false;
write_buff_append(buff, str, len);
write_buff_append(buff, (char[]) { 0x00 }, 1);
return true;
default:
return false;
}
}
static int lib_fmt_pack(lua_State *ctx) {
fmt_t *fmt = luaL_checkudata(ctx, 1, FMT_UDATA_NAME);
size_t arg_i = 2;
write_buff_t buff = { .arr = NULL, .n = 0, .cap = 0 };
for (size_t i = 0; i < fmt->segments_n; i++) {
fmt_segment_t segment = fmt->segments[i];
size_t n_aligned = -((-buff.n / segment.align) * segment.align);
size_t align_padding = n_aligned - buff.n;
write_buff_append(&buff, NULL, align_padding);
switch (segment.type) {
case FMT_INT8:
fmt_write_int8(&buff, luaL_checkinteger(ctx, arg_i++));
break;
case FMT_UINT8:
fmt_write_uint8(&buff, luaL_checkinteger(ctx, arg_i++));
break;
case FMT_INT16:
fmt_write_int16(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_UINT16:
fmt_write_uint16(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_INT32:
fmt_write_int32(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_UINT32:
fmt_write_uint32(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_INT64:
fmt_write_int64(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_UINT64:
fmt_write_uint64(&buff, luaL_checkinteger(ctx, arg_i++), fmt->little_endian);
break;
case FMT_FLOAT32:
fmt_write_float32(&buff, (float)luaL_checknumber(ctx, arg_i++), fmt->little_endian);
break;
case FMT_FLOAT64:
fmt_write_float64(&buff, (double)luaL_checknumber(ctx, arg_i++), fmt->little_endian);
break;
case FMT_STR_FIXED:
case FMT_STR8:
case FMT_STR16:
case FMT_STR32:
case FMT_STR64:
case FMT_STR_ZERO: {
size_t size;
const char *str = luaL_checklstring(ctx, arg_i++, &size);
if (!fmt_write_string(&buff, segment, str, size, fmt->little_endian)) {
luaL_error(ctx, "invalid string at %d", arg_i);
}
break;
}
case FMT_PADDING:
// TODO: might need to remove this later...
write_buff_append(&buff, NULL, segment.size);
}
}
write_buff_fit(&buff);
lua_pushlstring(ctx, buff.arr, buff.n);
free(buff.arr);
return 1;
}
static int lib_fmt_unpack(lua_State *ctx) {
fmt_t *fmt = luaL_checkudata(ctx, 1, FMT_UDATA_NAME);
size_t raw_size;
const uint8_t *raw = (const uint8_t*)luaL_checklstring(ctx, 2, &raw_size);
size_t read_i = 0;
size_t res_n = 0;
if (lua_isinteger(ctx, 3)) {
read_i = lua_tointeger(ctx, 3) - 1;
}
write_buff_t buff = { .arr = NULL, .n = 0, .cap = 0 };
for (size_t i = 0; i < fmt->segments_n; i++) {
fmt_segment_t segment = fmt->segments[i];
size_t n_aligned = -((-buff.n / segment.align) * segment.align);
size_t align_padding = n_aligned - buff.n;
read_i += align_padding;
switch (segment.type) {
case FMT_INT8: {
int8_t res;
if (!fmt_read_int8(raw, &read_i, raw_size, &res)) luaL_error(ctx, "couldn't read int8");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_UINT8: {
uint8_t res;
if (!fmt_read_uint8(raw, &read_i, raw_size, &res)) luaL_error(ctx, "couldn't read uint8");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_INT16: {
int16_t res;
if (!fmt_read_int16(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read int16");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_UINT16: {
uint16_t res;
if (!fmt_read_uint16(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read uint16");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_INT32: {
int32_t res;
if (!fmt_read_int32(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read int32");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_UINT32: {
uint32_t res;
if (!fmt_read_uint32(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read uint32");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_INT64: {
int64_t res;
if (!fmt_read_int64(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read int64");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_UINT64: {
uint64_t res;
if (!fmt_read_uint64(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read uint64");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_FLOAT32: {
float res;
if (!fmt_read_float32(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read float32");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_FLOAT64: {
double res;
if (!fmt_read_float64(raw, &read_i, raw_size, &res, fmt->little_endian)) luaL_error(ctx, "couldn't read float64");
lua_pushinteger(ctx, res);
res_n++;
break;
}
case FMT_STR_FIXED:
case FMT_STR8:
case FMT_STR16:
case FMT_STR32:
case FMT_STR64:
case FMT_STR_ZERO: {
if (!fmt_read_string(ctx, segment, raw, &read_i, raw_size, fmt->little_endian)) luaL_error(ctx, "couldn't read string");
res_n++;
break;
}
case FMT_PADDING:
read_i += segment.size;
break;
}
}
lua_pushinteger(ctx, read_i + 1);
return res_n + 1;
}
static int lib_fmt_new(lua_State *ctx) {
size_t raw_size;
const char *raw = luaL_checklstring(ctx, 1, &raw_size);
fmt_code_t code = fmt_parse_fmt(ctx, raw, raw_size);
if (code != FMT_OK) {
switch (code) {
case FMT_BAD_ARGS: return luaL_error(ctx, "illegal C arguments");
case FMT_INCOMPLETE_OP: return luaL_error(ctx, "incomplete operand in format string");
case FMT_BAD_OP_ARG: return luaL_error(ctx, "bad operand argument in format string");
case FMT_BAD_OP: return luaL_error(ctx, "bad operand in format string");
default: return luaL_error(ctx, "unknown error while parsing format string");
}
}
return 1;
}
static void fmt_init_meta(lua_State *ctx) {
luaL_newmetatable(ctx, FMT_UDATA_NAME);
luaL_newlib(ctx, ((luaL_Reg[]) {
{ "pack", lib_fmt_pack },
{ "unpack", lib_fmt_unpack },
{ NULL, NULL }
}));
lua_setfield(ctx, -2, "__index");
lua_pushboolean(ctx, false);
lua_setfield(ctx, -2, "__meta");
lua_pop(ctx, 1);
}
int fmt_open_lib(lua_State *ctx) {
fmt_init_meta(ctx);
luaL_newlib(ctx, ((luaL_Reg[]) {
{ "new", lib_fmt_new },
{ "pack", lib_fmt_pack },
{ "unpack", lib_fmt_unpack },
{ NULL, NULL },
}));
return 1;
}

101
lib/http.c Normal file
View File

@@ -0,0 +1,101 @@
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#include "lib.h"
typedef struct {
char **segments;
size_t *sizes;
size_t n, cap;
} http_body_buff_t;
static size_t body_writer(char *ptr, size_t _, size_t size, void *pbuff) {
http_body_buff_t *buff = pbuff;
size_t new_cap = buff->cap;
while (buff->n >= new_cap) {
new_cap *= 2;
}
if (new_cap > buff->cap) {
buff->segments = realloc(buff->segments, sizeof(*buff->segments) * new_cap);
buff->sizes = realloc(buff->sizes, sizeof(*buff->sizes) * new_cap);
buff->cap = new_cap;
}
buff->segments[buff->n] = malloc(size);
memcpy(buff->segments[buff->n], ptr, size);
buff->sizes[buff->n] = size;
buff->n++;
return size;
}
static char *body_compress(http_body_buff_t buff, size_t *psize) {
size_t size = 0;
for (size_t i = 0; i < buff.n; i++) {
size += buff.sizes[i];
}
char *res = malloc(size + 1);
res[size] = 0;
size_t offset = 0;
for (size_t i = 0; i < buff.n; i++) {
memcpy(res + offset, buff.segments[i], buff.sizes[i]);
offset += buff.sizes[i];
free(buff.segments[i]);
}
free(buff.segments);
free(buff.sizes);
*psize = size;
return res;
}
static int lib_http_get(lua_State *ctx) {
const char *url = luaL_checkstring(ctx, 1);
http_body_buff_t buff = {
.segments = malloc(sizeof *buff.segments * 16),
.sizes = malloc(sizeof *buff.sizes * 16),
.n = 0,
.cap = 16,
};
CURL *curl = curl_easy_init();
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, body_writer);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buff);
CURLcode code = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (code != CURLE_OK) {
return luaL_error(ctx, "HTTP GET failed: %s", curl_easy_strerror(code));
}
size_t size;
char *body = body_compress(buff, &size);
lua_pushlstring(ctx, body, size);
free(body);
return 1;
}
int http_open_lib(lua_State *ctx) {
luaL_newlib(ctx, ((luaL_Reg[]) {
{ "get", lib_http_get },
{ NULL, NULL },
}));
return 1;
}

5
lib/lib.h Normal file
View File

@@ -0,0 +1,5 @@
#include <lua.h>
int http_open_lib(lua_State *ctx);
int fmt_open_lib(lua_State *ctx);
int zlib_open_lib(lua_State *ctx);

126
lib/main.c Normal file
View File

@@ -0,0 +1,126 @@
#include <lua.h>
#include <lualib.h>
#include <lauxlib.h>
#include <curl/curl.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "lib.h"
#ifndef BYTECODE
static char entry_bytecode[] = {
0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93,
0x0d, 0x0a, 0x1a, 0x0a, 0x04, 0x08, 0x08, 0x78,
0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x77, 0x40, 0x01,
0x80, 0x80, 0x80, 0x00, 0x01, 0x02, 0x85, 0x51,
0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x83,
0x80, 0x00, 0x00, 0x44, 0x00, 0x02, 0x01, 0x46,
0x00, 0x01, 0x01, 0x82, 0x04, 0x86, 0x65, 0x72,
0x72, 0x6f, 0x72, 0x04, 0xa4, 0x70, 0x6c, 0x65,
0x61, 0x73, 0x65, 0x2c, 0x20, 0x63, 0x6f, 0x6d,
0x70, 0x69, 0x6c, 0x65, 0x20, 0x77, 0x69, 0x74,
0x68, 0x20, 0x2d, 0x44, 0x42, 0x59, 0x54, 0x45,
0x43, 0x4f, 0x44, 0x45, 0x3d, 0x2e, 0x2e, 0x2e,
0x81, 0x01, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80,
0x80,
};
#define BYTECODE entry_bytecode
#endif
static char err_bytecode[] = {
0x1b, 0x4c, 0x75, 0x61, 0x54, 0x00, 0x19, 0x93,
0x0d, 0x0a, 0x1a, 0x0a, 0x04, 0x08, 0x08, 0x78,
0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x28, 0x77, 0x40, 0x01,
0x80, 0x80, 0x80, 0x00, 0x01, 0x0c, 0xc4, 0x51,
0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x02, 0x81,
0x80, 0x00, 0x80, 0x0b, 0x01, 0x00, 0x00, 0x0e,
0x01, 0x02, 0x01, 0x14, 0x81, 0x02, 0x02, 0x03,
0x82, 0x01, 0x00, 0x8b, 0x02, 0x00, 0x04, 0x00,
0x03, 0x00, 0x00, 0xc4, 0x02, 0x02, 0x02, 0x03,
0x83, 0x02, 0x00, 0x44, 0x01, 0x05, 0x01, 0x0b,
0x01, 0x00, 0x06, 0x0e, 0x01, 0x02, 0x07, 0x80,
0x01, 0x01, 0x00, 0x03, 0x02, 0x04, 0x00, 0x44,
0x01, 0x03, 0x02, 0x3c, 0x81, 0x09, 0x00, 0x38,
0x17, 0x00, 0x80, 0x8e, 0x01, 0x02, 0x0a, 0x0e,
0x02, 0x02, 0x0b, 0x3c, 0x02, 0x0c, 0x00, 0xb8,
0x00, 0x00, 0x80, 0x03, 0x82, 0x06, 0x00, 0x38,
0x05, 0x00, 0x80, 0x94, 0x82, 0x04, 0x0e, 0x83,
0x83, 0x07, 0x00, 0xc4, 0x02, 0x03, 0x02, 0xc2,
0x02, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x80, 0x03,
0x02, 0x08, 0x00, 0xb8, 0x01, 0x00, 0x80, 0x83,
0x82, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0xb5,
0x02, 0x02, 0x00, 0x00, 0x02, 0x05, 0x00, 0x8e,
0x02, 0x02, 0x12, 0xc0, 0x02, 0x7f, 0x00, 0x38,
0x02, 0x00, 0x80, 0x80, 0x02, 0x04, 0x00, 0x03,
0x83, 0x09, 0x00, 0x8e, 0x03, 0x02, 0x12, 0xb5,
0x02, 0x03, 0x00, 0x00, 0x02, 0x05, 0x00, 0xbc,
0x81, 0x09, 0x00, 0xb8, 0x04, 0x00, 0x80, 0x8b,
0x02, 0x00, 0x00, 0x8e, 0x02, 0x05, 0x01, 0x94,
0x82, 0x05, 0x02, 0x83, 0x03, 0x0a, 0x00, 0x00,
0x04, 0x04, 0x00, 0x83, 0x84, 0x0a, 0x00, 0x00,
0x05, 0x03, 0x00, 0x83, 0x85, 0x02, 0x00, 0xc4,
0x02, 0x07, 0x01, 0x38, 0x03, 0x00, 0x80, 0x8b,
0x02, 0x00, 0x00, 0x8e, 0x02, 0x05, 0x01, 0x94,
0x82, 0x05, 0x02, 0x83, 0x03, 0x0a, 0x00, 0x00,
0x04, 0x04, 0x00, 0x83, 0x84, 0x02, 0x00, 0xc4,
0x02, 0x05, 0x01, 0x95, 0x00, 0x01, 0x80, 0xaf,
0x00, 0x80, 0x06, 0xb8, 0xe4, 0xff, 0x7f, 0x46,
0x00, 0x02, 0x01, 0x46, 0x01, 0x01, 0x01, 0x96,
0x04, 0x83, 0x69, 0x6f, 0x04, 0x87, 0x73, 0x74,
0x64, 0x65, 0x72, 0x72, 0x04, 0x86, 0x77, 0x72,
0x69, 0x74, 0x65, 0x04, 0x92, 0x75, 0x6e, 0x68,
0x61, 0x6e, 0x64, 0x6c, 0x65, 0x64, 0x20, 0x65,
0x72, 0x72, 0x6f, 0x72, 0x3a, 0x20, 0x04, 0x89,
0x74, 0x6f, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67,
0x04, 0x82, 0x0a, 0x04, 0x86, 0x64, 0x65, 0x62,
0x75, 0x67, 0x04, 0x88, 0x67, 0x65, 0x74, 0x69,
0x6e, 0x66, 0x6f, 0x04, 0x84, 0x53, 0x6e, 0x6c,
0x00, 0x04, 0x85, 0x6e, 0x61, 0x6d, 0x65, 0x04,
0x8a, 0x73, 0x68, 0x6f, 0x72, 0x74, 0x5f, 0x73,
0x72, 0x63, 0x04, 0x84, 0x5b, 0x43, 0x5d, 0x04,
0x87, 0x61, 0x74, 0x20, 0x3c, 0x43, 0x3e, 0x04,
0x85, 0x66, 0x69, 0x6e, 0x64, 0x04, 0x89, 0x25,
0x5b, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x04,
0x8c, 0x61, 0x74, 0x20, 0x3c, 0x73, 0x74, 0x72,
0x69, 0x6e, 0x67, 0x3e, 0x04, 0x84, 0x61, 0x74,
0x20, 0x04, 0x8c, 0x63, 0x75, 0x72, 0x72, 0x65,
0x6e, 0x74, 0x6c, 0x69, 0x6e, 0x65, 0x04, 0x82,
0x3a, 0x04, 0x83, 0x20, 0x20, 0x04, 0x85, 0x20,
0x69, 0x6e, 0x20, 0x81, 0x01, 0x00, 0x00, 0x80,
0x80, 0x80, 0x80, 0x80,
};
static void load_modules(lua_State *ctx) {
luaL_openlibs(ctx);
luaL_requiref(ctx, "http", http_open_lib, false);
luaL_requiref(ctx, "fmt", fmt_open_lib, false);
luaL_requiref(ctx, "zlib", zlib_open_lib, false);
}
int main(int argc, const char **argv) {
lua_State *ctx = luaL_newstate();
load_modules(ctx);
if (luaL_loadbufferx(ctx, err_bytecode, sizeof(err_bytecode), "<main>", "b")) {
fprintf(stderr, "error while loading lua bytecode for errfunc: %s", lua_tostring(ctx, -1));
return 1;
}
int errfunc_i = lua_gettop(ctx);
if (luaL_loadbufferx(ctx, (char*)BYTECODE, sizeof(BYTECODE), "<main>", "b")) {
fprintf(stderr, "error while loading lua bytecode for main: %s", lua_tostring(ctx, -1));
return 1;
}
for (size_t i = 1; i < argc; i++) {
lua_pushstring(ctx, argv[i]);
}
if (lua_pcall(ctx, argc - 1, 0, errfunc_i)) return 1;
lua_close(ctx);
return 0;
}

136
lib/zlib.c Normal file
View File

@@ -0,0 +1,136 @@
#include <zlib.h>
#include <lauxlib.h>
#include <lua.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include "lib.h"
#define DECOMPRESS_META "zlib.decompress.meta"
typedef struct {
z_stream stream;
size_t processed;
uint8_t buff[4096];
bool finalized;
} lua_zlib_stream_t;
static int lib_decompress_iter(lua_State *ctx) {
size_t size;
uint8_t *data = (uint8_t*)lua_tolstring(ctx, lua_upvalueindex(1), &size);
lua_zlib_stream_t *stream = lua_touserdata(ctx, lua_upvalueindex(2));
if (stream->finalized) {
lua_pushnil(ctx);
return 1;
}
stream->stream.next_in = data + stream->processed;
stream->stream.avail_in = size - stream->processed;
stream->stream.next_out = stream->buff;
stream->stream.avail_out = sizeof stream->buff;
int res = inflate(&stream->stream, Z_NO_FLUSH);
switch (res) {
case Z_STREAM_END:
inflateEnd(&stream->stream);
stream->finalized = true;
case Z_OK: {
lua_pushlstring(ctx, (const char*)stream->buff, sizeof stream->buff - stream->stream.avail_out);
stream->processed = size - stream->stream.avail_in;
return 1;
}
case Z_BUF_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: incorrect C parameters passed");
case Z_MEM_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: memory ran out");
case Z_STREAM_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: invalid state");
case Z_DATA_ERROR:
inflateEnd(&stream->stream);
stream->finalized = true;
return luaL_error(ctx, "zlib error: %s", stream->stream.msg);
default:
return luaL_error(ctx, "wtf");
}
}
static int lib_decompress_free(lua_State *ctx) {
lua_zlib_stream_t *stream = lua_touserdata(ctx, 1);
if (stream->finalized) return 0;
inflateEnd(&stream->stream);
stream->finalized = true;
return 0;
}
static int lib_decompress_create(lua_State *ctx, int window_size) {
// size_t data_size;
// uint8_t *data = (uint8_t*)luaL_checklstring(ctx, 1, &data_size);
lua_pushvalue(ctx, 1);
lua_zlib_stream_t *stream = lua_newuserdatauv(ctx, sizeof *stream, 0);
memset(stream, 0, sizeof *stream);
luaL_getmetatable(ctx, DECOMPRESS_META);
lua_setmetatable(ctx, -2);
// stream->stream.avail_out = sizeof stream->buff;
// stream->stream.next_out = stream->buff;
// stream->stream.avail_in = data_size;
// stream->stream.next_in = data;
if (inflateInit2(&stream->stream, window_size) != Z_OK) {
return luaL_error(ctx, "failed to initialize inflate stream");
}
lua_pushcclosure(ctx, lib_decompress_iter, 2);
return 1;
}
static int lib_inflate(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS);
}
static int lib_inflate_raw(lua_State *ctx) {
return lib_decompress_create(ctx, -MAX_WBITS);
}
static int lib_gunzip(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS + 16);
}
static int lib_decompress(lua_State *ctx) {
return lib_decompress_create(ctx, MAX_WBITS + 32);
}
int zlib_open_lib(lua_State *ctx) {
luaL_newmetatable(ctx, DECOMPRESS_META);
luaL_setfuncs(ctx, (luaL_Reg[]) {
{ "__gc", lib_decompress_free },
{ NULL, NULL },
}, 0);
lua_pop(ctx, 2);
luaL_newlib(ctx, ((luaL_Reg[]) {
{ "inflate", lib_inflate },
{ "inflate_raw", lib_inflate_raw },
{ "gunzip", lib_gunzip },
{ "decompress", lib_decompress },
{ NULL, NULL },
}));
return 1;
}