Initial commit

This commit is contained in:
lumi 2025-06-06 05:29:52 +10:00
commit 7ae25ab368
19 changed files with 2709 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
*.generated.lua

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "luajit"]
path = luajit
url = https://luajit.org/git/luajit.git

5
.luarc.json Normal file
View File

@ -0,0 +1,5 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "LuaJIT",
"diagnostics.disable": ["redefined-local"]
}

757
Cargo.lock generated Normal file
View File

@ -0,0 +1,757 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "cbindgen"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "975982cdb7ad6a142be15bdf84aea7ec6a9e5d4d797c004d43185b24cfe4e684"
dependencies = [
"clap",
"heck",
"indexmap",
"log",
"proc-macro2",
"quote",
"serde",
"serde_json",
"syn",
"tempfile",
"toml",
]
[[package]]
name = "cc"
version = "1.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
name = "either"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
[[package]]
name = "env_home"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "generic-array"
version = "0.14.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "glob"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
[[package]]
name = "hashbrown"
version = "0.15.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "libc"
version = "0.2.172"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
[[package]]
name = "libmimalloc-sys"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "luabi"
version = "0.1.0"
dependencies = [
"blake2",
"bstr",
"cbindgen",
"cc",
"clap",
"digest",
"glob",
"libmimalloc-sys",
"lz4_flex",
"md-5",
"mimalloc",
"owo-colors",
"sha2",
"thiserror",
"which",
]
[[package]]
name = "lz4_flex"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5"
dependencies = [
"twox-hash",
]
[[package]]
name = "md-5"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mimalloc"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af"
dependencies = [
"libmimalloc-sys",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "once_cell_polyfill"
version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad"
[[package]]
name = "owo-colors"
version = "4.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26995317201fa17f3656c36716aed4a7c81743a9634ac4c99c0eeda495db0cec"
[[package]]
name = "proc-macro2"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
[[package]]
name = "rustix"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "sha2"
version = "0.10.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "static_assertions"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "tempfile"
version = "3.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
dependencies = [
"fastrand",
"getrandom",
"once_cell",
"rustix",
"windows-sys",
]
[[package]]
name = "thiserror"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076"
[[package]]
name = "twox-hash"
version = "1.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675"
dependencies = [
"cfg-if",
"static_assertions",
]
[[package]]
name = "typenum"
version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "version_check"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "which"
version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762"
dependencies = [
"either",
"env_home",
"rustix",
"winsafe",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "winnow"
version = "0.7.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec"
dependencies = [
"memchr",
]
[[package]]
name = "winsafe"
version = "0.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]

8
Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[workspace]
members = ["luabi"]
resolver = "3"
[profile.release]
panic = "abort"
lto = "fat"
strip = "debuginfo"

24
luabi/Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "luabi"
version = "0.1.0"
edition = "2024"
[dependencies]
blake2 = "0.10.6"
bstr = "1.12.0"
clap = { version = "4.5.39", features = ["derive"] }
digest = "0.10.7"
libmimalloc-sys = "0.1.42"
lz4_flex = "0.11.3"
md-5 = "0.10.6"
mimalloc = "0.1.46"
owo-colors = "4.2.1"
sha2 = "0.10.9"
thiserror = "2.0.12"
[build-dependencies]
cbindgen = "0.29.0"
cc = "1.2.25"
glob = "0.3.2"
lz4_flex = "0.11.3"
which = "7.0.3"

189
luabi/build.rs Normal file
View File

@ -0,0 +1,189 @@
use cbindgen::{EnumConfig, Language, Style};
use glob::glob;
use lz4_flex::compress_prepend_size;
use std::{
env::{var, var_os},
fs::{File, copy, create_dir_all, read_dir, write},
io::{self, Write},
path::{Path, PathBuf},
process::Command,
thread::available_parallelism,
};
use which::which;
fn main() {
let target = var("TARGET").unwrap();
let host = var("HOST").unwrap();
let src_path: PathBuf = var("CARGO_MANIFEST_DIR").unwrap().into();
let out_path: PathBuf = var("OUT_DIR").unwrap().into();
let luajit_path = out_path.join("luajit/src");
let lib_rt_path = out_path.join("runtime.lua.lz4");
copy_recursive(src_path.join("../luajit"), luajit_path.parent().unwrap())
.expect("failed to copy luajit source");
build_luajit(&target, &host, &luajit_path);
build_lua(&src_path, &lib_rt_path);
println!("cargo::rerun-if-changed={}/src", src_path.display());
println!("cargo::rustc-link-arg=-rdynamic");
}
fn build_luajit(target: &str, host: &str, src_path: &Path) {
let mut make = match host {
"x86_64-unknown-dragonfly" => Command::new("gmake"),
"x86_64-unknown-freebsd" => Command::new("gmake"),
_ => Command::new("make"),
};
make.current_dir(&src_path)
.arg("-e")
.arg(format!(
"-j{}",
available_parallelism().map_or(1, |n| n.into())
))
.env("BUILDMODE", "static");
let mut xcflags = vec![
"-fPIC",
"-DLUAJIT_ENABLE_LUA52COMPAT", // enable lua 5.2 compatibility
"-DLUAJIT_USE_SYSMALLOC", // disable the built-in allocator (we provide our own)
];
if cfg!(debug_assertions) {
xcflags.extend_from_slice(&[
"-DLUA_USE_ASSERT",
"-DLUA_USE_APICHECK",
"-DLUAJIT_USE_GDBJIT", // gdb support
]);
}
if target.ends_with("-apple-darwin") && var_os("MACOSX_DEPLOYMENT_TARGET").is_none() {
make.env(
"MACOSX_DEPLOYMENT_TARGET",
match target.split_once("-").unwrap() {
("aarch64", _) | ("arm64e", _) => "11.0",
("x86_64", _) | ("x86_64h", _) => "10.11",
(arch, _) => panic!("unknown macos target architecture '{arch}'"),
},
);
} else if target.contains("-windows-") || target.ends_with("-windows") {
make.env("TARGET_SYS", "Windows");
} else if target.contains("-linux-") || target.ends_with("-linux") {
make.env("TARGET_SYS", "Linux");
} else {
make.env("TARGET_SYS", "Other");
}
let host_cc = cc::Build::new().target(host).get_compiler();
let host_cc_path = which(host_cc.path()).expect("failed to find host cc");
if var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap() == "32" {
make.env("HOST_CC", format!("{} -m32", host_cc_path.display())); // cross-compile to 32-bit on 64-bit host
} else {
make.env("HOST_CC", &host_cc_path);
}
let target_cc = cc::Build::new().target(target).get_compiler();
let target_cc_path = which(target_cc.path()).expect("failed to find target cc");
let target_bin_path = target_cc_path.parent().unwrap();
if let Some(prefix) = target_cc_path.to_str().unwrap().strip_suffix("gcc") {
make.env("DEFAULT_CC", "gcc").env("CROSS", prefix);
} else if let Some(prefix) = target_cc_path.to_str().unwrap().strip_suffix("clang") {
make.env("DEFAULT_CC", "clang").env("CROSS", prefix);
} else {
make.env("DEFAULT_CC", target_cc_path.file_name().unwrap())
.env("CROSS", format!("{}/", target_bin_path.display()));
}
make.env("XCFLAGS", xcflags.join(" "));
println!("running luajit make: {make:?}");
let status = make.status().expect("failed to execute make");
if !status.success() {
panic!("failed to compile luajit: {status}: {make:?}");
}
println!("cargo::rerun-if-env-changed=MACOSX_DEPLOYMENT_TARGET");
println!("cargo::rustc-link-search=native={}", src_path.display());
println!("cargo::rustc-link-lib=static=luajit");
}
fn build_lua(src_path: &Path, out_path: &Path) {
let mut buf = Vec::new();
writeln!(&mut buf, "require(\"ffi\").cdef [[").unwrap();
cbindgen::generate_with_config(
src_path,
cbindgen::Config {
language: Language::C,
no_includes: true,
documentation: false,
line_length: 1000,
style: Style::Type,
enumeration: EnumConfig {
prefix_with_name: true,
..Default::default()
},
..Default::default()
},
)
.expect("failed to generate bindings")
.write(&mut buf);
writeln!(&mut buf, "]]").unwrap();
let mut paths = glob(&format!("{}/src/**/*.lua", src_path.display()))
.unwrap()
.collect::<Result<Vec<PathBuf>, _>>()
.unwrap();
paths.sort();
for path in paths {
let name = path
.strip_prefix(src_path)
.unwrap()
.strip_prefix("src")
.unwrap()
.with_extension("")
.to_string_lossy()
.replace("/", ".");
writeln!(&mut buf, "package.preload[\"lb.{name}\"] = function(...)").unwrap();
std::io::copy(
&mut File::open(path).expect("failed to open file"),
&mut buf,
)
.unwrap();
writeln!(&mut buf, "end").unwrap();
}
// debugging:
write(
PathBuf::from(var("CARGO_MANIFEST_DIR").unwrap()).join("runtime.generated.lua"),
&buf,
)
.unwrap();
write(out_path, compress_prepend_size(&buf)).unwrap();
}
fn copy_recursive(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
create_dir_all(&dst)?;
for entry in read_dir(src)? {
let entry = entry?;
let ftype = entry.file_type()?;
if entry.file_name().to_str() == Some(".git") {
continue;
}
let path = dst.as_ref().join(entry.file_name());
if ftype.is_dir() {
copy_recursive(entry.path(), path)?;
} else {
copy(entry.path(), path)?;
}
}
Ok(())
}

112
luabi/src/core.lua Normal file
View File

@ -0,0 +1,112 @@
local ffi = require("ffi")
local C = ffi.C
local mm_names = {
-- known luajit metamethods (see lj_obj.h)
__index = true,
__newindex = true,
__gc = true,
__eq = true,
__len = true,
__lt = true,
__le = true,
__concat = true,
__call = true,
__add = true,
__sub = true,
__mul = true,
__div = true,
__mod = true,
__pow = true,
__unm = true,
__metatable = true,
__tostring = true,
__pairs = true,
__ipairs = true,
-- luabi metamethods
__init = true,
}
local function Type(name, def)
local obj_mt = {
__tostring = function(self)
return string.format("%s %p", name, self)
end,
}
local type = {}
local type_mt = {
__tostring = function(self)
return string.format("lb.core::Type<%s> %p", name, self)
end,
__call = function(_, ...)
local self = setmetatable({}, obj_mt)
local __init = obj_mt.__init
if __init ~= nil then __init(self, ...) end
return self
end,
}
for key, value in pairs(def) do
if mm_names[key] == true then
obj_mt[key] = value
else
type[key] = value
end
end
type_mt.__index = obj_mt.__index
obj_mt.__index = type
return setmetatable(type, type_mt)
end
local Module = Type("lb.core::Module", {
__init = function(self, name, def)
self.__name = name
for key, value in pairs(def) do
self[key] = value
end
end,
__tostring = function(self)
return string.format("lb.core::Module<%s> %p", self.__name, self)
end,
__call = function(self, ...)
local call = self.__call
if call ~= nil then return call(...) end
local default = self.__default
if default ~= nil then return default end
error(string.format("module '%s' has no default export", self.__name))
end,
})
local function unwrap_opt(res)
-- read value out and forget option cdata
if res.has == true then return res.value end
return nil
end
local function unwrap_res(res)
if res.err ~= nil then
-- read message out, drop error cdata and throw
local buf = C.lb_error_msg(res.err)
local msg = ffi.string(buf.ptr, buf.len)
C.lb_error_drop(buf)
error(msg)
else
-- error is nil so value must have been initialised
-- read value out and forget result cdata
return res.value
end
end
return Module("lb.core", {
Module = Module,
Type = Type,
unwrap_opt = unwrap_opt,
unwrap_res = unwrap_res,
})

275
luabi/src/core.rs Normal file
View File

@ -0,0 +1,275 @@
use crate::{
hash::LbHashError,
str::{LbStr, LbStrError},
};
use std::{
collections::HashMap,
mem::{ManuallyDrop, MaybeUninit},
ops::{Deref, DerefMut},
ptr::{self, NonNull},
slice,
};
use thiserror::Error;
#[repr(C)]
pub struct LbOpt<T> {
value: MaybeUninit<T>,
has: bool,
}
impl<T> LbOpt<T> {
pub fn some(value: T) -> Self {
Self {
value: MaybeUninit::new(value),
has: true,
}
}
pub fn none() -> Self {
Self {
value: MaybeUninit::uninit(),
has: false,
}
}
}
impl<T> From<Option<T>> for LbOpt<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(value) => Self::some(value),
None => Self::none(),
}
}
}
impl<T> Drop for LbOpt<T> {
fn drop(&mut self) {
if self.has {
unsafe {
// SAFETY: since option has a value, value must have been initialised
self.value.assume_init_drop();
}
}
}
}
#[repr(C)]
pub struct LbRes<T> {
value: MaybeUninit<T>,
err: Option<Box<LbError>>,
}
impl<T> LbRes<T> {
pub fn ok(value: T) -> Self {
Self {
value: MaybeUninit::new(value),
err: None,
}
}
pub fn err(error: LbError) -> Self {
Self {
value: MaybeUninit::uninit(),
err: Some(Box::new(error)),
}
}
}
impl<T> From<Result<T, LbError>> for LbRes<T> {
fn from(value: Result<T, LbError>) -> Self {
match value {
Ok(value) => Self::ok(value),
Err(error) => Self::err(error),
}
}
}
impl<T> Drop for LbRes<T> {
fn drop(&mut self) {
if self.err.is_none() {
unsafe {
// SAFETY: since result is not an error, value must have been initialised
self.value.assume_init_drop();
}
}
}
}
#[derive(Debug, Error)]
pub enum LbError {
#[error("{0}")]
String(#[from] LbStrError),
#[error("{0}")]
Hash(#[from] LbHashError),
}
impl LbError {
pub fn message(&self) -> String {
format!("{self}")
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_error_drop(&mut self) {
unsafe { ptr::drop_in_place(self) }
}
#[unsafe(no_mangle)]
pub extern "C" fn lb_error_msg(&self) -> LbStr {
self.message().into()
}
}
#[repr(C)]
pub struct LbVec<T> {
ptr: NonNull<T>,
len: usize,
cap: usize,
}
impl<T> LbVec<T> {
pub fn new() -> Self {
Vec::new().into()
}
}
impl<T> Deref for LbVec<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
}
impl<T> DerefMut for LbVec<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
}
impl<T> From<Vec<T>> for LbVec<T> {
fn from(value: Vec<T>) -> Self {
let len = value.len();
let cap = value.capacity();
let ptr = ManuallyDrop::new(value).as_mut_ptr();
Self {
ptr: unsafe { NonNull::new_unchecked(ptr) }.into(),
len,
cap,
}
}
}
impl<T> From<LbVec<T>> for Vec<T> {
fn from(value: LbVec<T>) -> Self {
let len = value.len;
let cap = value.cap;
let ptr = ManuallyDrop::new(value).ptr.as_ptr();
unsafe { Vec::from_raw_parts(ptr, len, cap) }
}
}
#[repr(C)]
pub struct LbMap<K, V> {
inner: Box<LbMapImpl<K, V>>,
}
#[derive(Debug)]
struct LbMapImpl<K, V>(HashMap<K, V>);
impl<K, V> LbMap<K, V> {
pub fn new() -> Self {
Self {
inner: Box::new(LbMapImpl(HashMap::new())),
}
}
}
impl<K, V> Deref for LbMap<K, V> {
type Target = HashMap<K, V>;
fn deref(&self) -> &Self::Target {
&self.inner.0
}
}
impl<K, V> DerefMut for LbMap<K, V> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner.0
}
}
impl<K, V> From<HashMap<K, V>> for LbMap<K, V> {
fn from(value: HashMap<K, V>) -> Self {
Self {
inner: Box::new(LbMapImpl(value)),
}
}
}
impl<K, V> From<LbMap<K, V>> for HashMap<K, V> {
fn from(value: LbMap<K, V>) -> Self {
value.inner.0
}
}
pub type LbBuf = LbVec<u8>;
impl LbBuf {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_buf_new(ptr: *const u8, len: usize) -> LbBuf {
unsafe { slice::from_raw_parts(ptr, len).to_vec().into() }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_buf_drop(&mut self) {
unsafe { ptr::drop_in_place(self) }
}
}
pub type LbParams = LbMap<String, String>;
impl LbParams {
#[unsafe(no_mangle)]
pub extern "C" fn lb_params_new() -> Self {
Self::new()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_params_drop(&mut self) {
unsafe { ptr::drop_in_place(self) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_params_get(&self, key: *const u8, key_len: usize) -> LbOpt<LbStr> {
std::str::from_utf8(unsafe { slice::from_raw_parts(key, key_len) })
.ok()
.and_then(|key| self.get(key).map(|s| s.to_owned().into()))
.into()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_params_set(
&mut self,
key: *const u8,
key_len: usize,
value: *const u8,
value_len: usize,
) -> LbRes<bool> {
std::str::from_utf8(unsafe { slice::from_raw_parts(key, key_len) })
.and_then(|key| {
std::str::from_utf8(unsafe { slice::from_raw_parts(value, value_len) })
.map(|value| (key, value))
})
.map_err(|err| LbStrError::InvalidUtf8(err).into())
.map(|(key, value)| self.insert(key.into(), value.into()))
.map(|_| true)
.into()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_params_del(&mut self, key: *const u8, key_len: usize) -> bool {
std::str::from_utf8(unsafe { slice::from_raw_parts(key, key_len) })
.map(|key| self.remove(key).is_some())
.unwrap_or(false)
}
}

14
luabi/src/fs.rs Normal file
View File

@ -0,0 +1,14 @@
// use std::{
// ffi::{CStr, OsStr, OsString, c_char, c_int},
// slice,
// };
// #[unsafe(no_mangle)]
// pub unsafe extern "C" fn lb_fs_touch(path: *const u8, path_len: usize) -> c_int {
// let path = match std::str::from_utf8(unsafe { slice::from_raw_parts(path, path_len) }) {
// Ok(path) => path,
// Err(_) => return -1, // invalid utf8 string
// };
// 0
// }

56
luabi/src/hash.lua Normal file
View File

@ -0,0 +1,56 @@
local core = require("lb.core")
local ffi = require("ffi")
local C = ffi.C
local algs = {
md5 = C.LbHasherAlgorithm_Md5,
sha224 = C.LbHasherAlgorithm_Sha224,
sha256 = C.LbHasherAlgorithm_Sha256,
sha384 = C.LbHasherAlgorithm_Sha384,
sha512 = C.LbHasherAlgorithm_Sha512,
blake2b = C.LbHasherAlgorithm_Blake2b,
blake2s = C.LbHasherAlgorithm_Blake2s,
}
local alg_names = {}
for name, _ in pairs(algs) do
alg_names[#alg_names + 1] = name
end
table.sort(alg_names)
local Hasher = core.Type("lb.hash::Hasher", {
__init = function(self, name, params)
if name == nil then
error("algorithm must be specified: one of " .. table.concat(alg_names, ", "))
end
local name = tostring(name)
local alg = algs[name]
if alg == nil then error(string.format("unknown hash algorithm '%s'", name)) end
self.__name = name
self.__ptr = ffi.gc(core.unwrap_res(C.lb_hasher_new(alg, nil)), C.lb_hasher_drop)
end,
name = function(self)
return self.__name
end,
update = function(self, data)
local data = tostring(data)
C.lb_hasher_update(self.__ptr, data, data:len())
return self
end,
done = function(self, format)
local buf = C.lb_hasher_digest(self.__ptr)
local digest = ffi.string(buf.ptr, buf.len)
C.lb_buf_drop(buf)
return digest
end,
})
return core.Module("lb.hash", {
__call = Hasher,
Hasher = Hasher,
})

157
luabi/src/hash.rs Normal file
View File

@ -0,0 +1,157 @@
use crate::core::{LbBuf, LbError, LbParams, LbRes};
use blake2::{Blake2b512, Blake2bVar, Blake2s256, Blake2sVar};
use digest::InvalidOutputSize;
use md5::Md5;
use sha2::{Sha224, Sha256, Sha384, Sha512, Sha512_224, Sha512_256};
use std::{mem, num::ParseIntError, ptr, slice};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum LbHashError {
#[error("invalid parameter '{0}': {1}")]
InvalidIntParam(&'static str, ParseIntError),
#[error("invalid parameter '{PARAM_OUTPUT_SIZE}': {0}")]
InvalidOutputSize(#[from] InvalidOutputSize),
}
#[repr(C)]
#[derive(Debug)]
pub struct LbHasher {
alg: LbHasherAlgorithm,
hasher: Box<LbHasherImpl>,
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum LbHasherAlgorithm {
Md5,
Sha224,
Sha256,
Sha384,
Sha512,
Blake2b,
Blake2s,
}
#[derive(Debug)]
enum LbHasherImpl {
Md5(Md5),
Sha224(Sha224),
Sha256(Sha256),
Sha384(Sha384),
Sha512(Sha512),
Sha512_224(Sha512_224),
Sha512_256(Sha512_256),
Blake2b(Blake2bVar),
Blake2b512(Blake2b512),
Blake2s(Blake2sVar),
Blake2s256(Blake2s256),
}
const PARAM_OUTPUT_SIZE: &'static str = "output_size";
fn param_output_size(params: Option<&LbParams>) -> Result<Option<usize>, LbHashError> {
params
.and_then(|p| p.get(PARAM_OUTPUT_SIZE))
.map(|s| s.parse())
.transpose()
.map_err(|err| LbHashError::InvalidIntParam(PARAM_OUTPUT_SIZE, err))
}
impl LbHasher {
pub fn new(alg: LbHasherAlgorithm, params: Option<&LbParams>) -> Result<Self, LbHashError> {
use digest::{Digest, VariableOutput};
let hasher = Box::new(match alg {
LbHasherAlgorithm::Md5 => LbHasherImpl::Md5(Digest::new()),
LbHasherAlgorithm::Sha224 => LbHasherImpl::Sha224(Digest::new()),
LbHasherAlgorithm::Sha256 => LbHasherImpl::Sha256(Digest::new()),
LbHasherAlgorithm::Sha384 => LbHasherImpl::Sha384(Digest::new()),
LbHasherAlgorithm::Sha512 => param_output_size(params).and_then(|size| match size {
Some(224) => Ok(LbHasherImpl::Sha512_224(Digest::new())),
Some(256) => Ok(LbHasherImpl::Sha512_256(Digest::new())),
Some(512) | None => Ok(LbHasherImpl::Sha512(Digest::new())),
_ => Err(LbHashError::InvalidOutputSize(InvalidOutputSize)),
})?,
LbHasherAlgorithm::Blake2b => {
param_output_size(params).and_then(|size| match size {
Some(512) | None => Ok(LbHasherImpl::Blake2b512(Blake2b512::new())),
size => VariableOutput::new(size.unwrap())
.map(LbHasherImpl::Blake2b)
.map_err(LbHashError::InvalidOutputSize),
})?
}
LbHasherAlgorithm::Blake2s => {
param_output_size(params).and_then(|size| match size {
Some(256) | None => Ok(LbHasherImpl::Blake2s256(Blake2s256::new())),
size => VariableOutput::new(size.unwrap())
.map(LbHasherImpl::Blake2s)
.map_err(LbHashError::InvalidOutputSize),
})?
}
});
Ok(Self { alg, hasher })
}
pub fn update(&mut self, data: impl AsRef<[u8]>) {
use digest::Update;
match *self.hasher {
LbHasherImpl::Md5(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha224(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha256(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha384(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha512(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha512_224(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Sha512_256(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Blake2b(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Blake2b512(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Blake2s(ref mut s) => s.update(data.as_ref()),
LbHasherImpl::Blake2s256(ref mut s) => s.update(data.as_ref()),
}
}
pub fn digest(&mut self) -> Vec<u8> {
use digest::{DynDigest, VariableOutput};
match *self.hasher {
LbHasherImpl::Md5(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha224(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha256(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha384(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha512(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha512_224(ref mut s) => s.finalize_reset(),
LbHasherImpl::Sha512_256(ref mut s) => s.finalize_reset(),
LbHasherImpl::Blake2b(ref mut s) => {
mem::replace(s, Blake2bVar::new(s.output_size()).unwrap()).finalize_boxed()
}
LbHasherImpl::Blake2b512(ref mut s) => s.finalize_reset(),
LbHasherImpl::Blake2s(ref mut s) => {
mem::replace(s, Blake2sVar::new(s.output_size()).unwrap()).finalize_boxed()
}
LbHasherImpl::Blake2s256(ref mut s) => s.finalize_reset(),
}
.to_vec()
}
#[unsafe(no_mangle)]
pub extern "C" fn lb_hasher_new(
alg: LbHasherAlgorithm,
params: Option<&LbParams>,
) -> LbRes<Self> {
Self::new(alg, params).map_err(LbError::Hash).into()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_hasher_drop(&mut self) {
unsafe { ptr::drop_in_place(self) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_hasher_update(&mut self, data: *const u8, data_len: usize) {
self.update(unsafe { slice::from_raw_parts(data, data_len) })
}
#[unsafe(no_mangle)]
pub extern "C" fn lb_hasher_digest(&mut self) -> LbBuf {
self.digest().into()
}
}

4
luabi/src/lib.rs Normal file
View File

@ -0,0 +1,4 @@
pub mod core;
pub mod fs;
pub mod hash;
pub mod str;

924
luabi/src/lua.rs Normal file
View File

@ -0,0 +1,924 @@
#![allow(non_camel_case_types, non_snake_case, unused)]
use bstr::{BStr, BString, ByteSlice};
use libmimalloc_sys::{mi_free, mi_realloc};
use std::{
backtrace::Backtrace,
ffi::{CStr, CString, NulError},
fmt::{self, Display},
marker::{PhantomData, PhantomPinned},
os::raw::{c_char, c_double, c_int, c_void},
ptr::{self, NonNull},
rc::Rc,
slice,
thread::{self, Thread},
};
use thiserror::Error;
/***** lua.h *****/
/* mark for precompiled code (`<esc>Lua') */
pub const LUA_SIGNATURE: &[u8] = b"\x1bLJ";
/* option for multiple returns in `lua_pcall' and `lua_call' */
pub const LUA_MULTRET: c_int = -1;
/*
** pseudo-indices
*/
pub const LUA_REGISTRYINDEX: c_int = -10000;
pub const LUA_ENVIRONINDEX: c_int = -10001;
pub const LUA_GLOBALSINDEX: c_int = -10002;
pub const fn lua_upvalueindex(i: c_int) -> c_int {
LUA_GLOBALSINDEX - i
}
/* thread status */
pub const LUA_OK: c_int = 0;
pub const LUA_YIELD: c_int = 1;
pub const LUA_ERRRUN: c_int = 2;
pub const LUA_ERRSYNTAX: c_int = 3;
pub const LUA_ERRMEM: c_int = 4;
pub const LUA_ERRERR: c_int = 5;
#[repr(C)]
pub struct lua_State {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
pub type lua_CFunction = unsafe extern "C-unwind" fn(L: *mut lua_State) -> c_int;
/*
** functions that read/write blocks when loading/dumping Lua chunks
*/
pub type lua_Reader = unsafe extern "C-unwind" fn(
L: *mut lua_State,
ud: *mut c_void,
sz: *mut usize,
) -> *const c_char;
/*
** prototype for memory-allocation functions
*/
pub type lua_Writer = unsafe extern "C-unwind" fn(
L: *mut lua_State,
p: *const c_void,
sz: usize,
ud: *mut c_void,
) -> c_int;
/*
** prototype for memory-allocation functions
*/
pub type lua_Alloc = unsafe extern "C" fn(
ud: *mut c_void,
ptr: *mut c_void,
osize: usize,
nsize: usize,
) -> *mut c_void;
/*
** basic types
*/
pub const LUA_TNONE: c_int = -1;
pub const LUA_TNIL: c_int = 0;
pub const LUA_TBOOLEAN: c_int = 1;
pub const LUA_TLIGHTUSERDATA: c_int = 2;
pub const LUA_TNUMBER: c_int = 3;
pub const LUA_TSTRING: c_int = 4;
pub const LUA_TTABLE: c_int = 5;
pub const LUA_TFUNCTION: c_int = 6;
pub const LUA_TUSERDATA: c_int = 7;
pub const LUA_TTHREAD: c_int = 8;
pub const LUA_TCDATA: c_int = 10;
/* minimum Lua stack available to a C function */
pub const LUA_MINSTACK: c_int = 20;
/* type of numbers in Lua */
pub type lua_Number = c_double;
/* type for integer functions */
#[cfg(target_pointer_width = "32")]
pub type lua_Integer = i32;
#[cfg(target_pointer_width = "64")]
pub type lua_Integer = i64;
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
/*
** state manipulation
*/
pub fn lua_newstate(f: lua_Alloc, ud: *mut c_void) -> *mut lua_State;
pub fn lua_close(L: *mut lua_State);
pub fn lua_newthread(L: *mut lua_State) -> *mut lua_State;
pub fn lua_atpanic(L: *mut lua_State, panicf: lua_CFunction) -> lua_CFunction;
/*
** basic stack manipulation
*/
pub fn lua_gettop(L: *mut lua_State) -> c_int;
pub fn lua_settop(L: *mut lua_State, idx: c_int);
pub fn lua_pushvalue(L: *mut lua_State, idx: c_int);
pub fn lua_remove(L: *mut lua_State, idx: c_int);
pub fn lua_insert(L: *mut lua_State, idx: c_int);
pub fn lua_replace(L: *mut lua_State, idx: c_int);
pub fn lua_checkstack(L: *mut lua_State, sz: c_int) -> c_int;
pub fn lua_xmove(from: *mut lua_State, to: *mut lua_State, n: c_int);
pub fn lua_isnumber(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isstring(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_iscfunction(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_isuserdata(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_type(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_typename(L: *mut lua_State, tp: c_int) -> *const c_char;
pub fn lua_equal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_rawequal(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_lessthan(L: *mut lua_State, idx1: c_int, idx2: c_int) -> c_int;
pub fn lua_tonumber(L: *mut lua_State, idx: c_int) -> lua_Number;
pub fn lua_tointeger(L: *mut lua_State, idx: c_int) -> lua_Integer;
pub fn lua_toboolean(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_tolstring(L: *mut lua_State, idx: c_int, len: *mut usize) -> *const c_char;
pub fn lua_objlen(L: *mut lua_State, idx: c_int) -> usize;
pub fn lua_tocfunction(L: *mut lua_State, idx: c_int) -> Option<lua_CFunction>;
pub fn lua_touserdata(L: *mut lua_State, idx: c_int) -> *mut c_void;
pub fn lua_tothread(L: *mut lua_State, idx: c_int) -> *mut lua_State;
pub fn lua_topointer(L: *mut lua_State, idx: c_int) -> *const c_void;
/*
** push functions (C -> stack)
*/
pub fn lua_pushnil(L: *mut lua_State);
pub fn lua_pushnumber(L: *mut lua_State, n: lua_Number);
pub fn lua_pushinteger(L: *mut lua_State, n: lua_Integer);
pub fn lua_pushlstring(L: *mut lua_State, s: *const c_char, l: usize);
pub fn lua_pushstring(L: *mut lua_State, s: *const c_char);
pub fn lua_pushfstring(L: *mut lua_State, fmt: *const c_char, ...) -> *const c_char;
pub fn lua_pushcclosure(L: *mut lua_State, f: lua_CFunction, n: c_int);
pub fn lua_pushboolean(L: *mut lua_State, b: c_int);
pub fn lua_pushlightuserdata(L: *mut lua_State, p: *mut c_void);
pub fn lua_pushthread(L: *mut lua_State) -> c_int;
/*
** get functions (Lua -> stack)
*/
pub fn lua_gettable(L: *mut lua_State, idx: c_int);
pub fn lua_getfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawget(L: *mut lua_State, idx: c_int);
pub fn lua_rawgeti(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_createtable(L: *mut lua_State, narr: c_int, nrec: c_int);
pub fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void;
pub fn lua_getmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_getfenv(L: *mut lua_State, idx: c_int);
/*
** set functions (stack -> Lua)
*/
pub fn lua_settable(L: *mut lua_State, idx: c_int);
pub fn lua_setfield(L: *mut lua_State, idx: c_int, k: *const c_char);
pub fn lua_rawset(L: *mut lua_State, idx: c_int);
pub fn lua_rawseti(L: *mut lua_State, idx: c_int, n: c_int);
pub fn lua_setmetatable(L: *mut lua_State, objindex: c_int) -> c_int;
pub fn lua_setfenv(L: *mut lua_State, idx: c_int) -> c_int;
/*
** `load' and `call' functions (load and run Lua code)
*/
pub fn lua_call(L: *mut lua_State, nargs: c_int, nresults: c_int);
pub fn lua_pcall(L: *mut lua_State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int;
pub fn lua_cpcall(L: *mut lua_State, func: lua_CFunction, ud: *mut c_void) -> c_int;
pub fn lua_load(
L: *mut lua_State,
reader: lua_Reader,
dt: *mut c_void,
chunkname: *const c_char,
) -> c_int;
pub fn lua_dump(L: *mut lua_State, writer: lua_Writer, data: *mut c_void) -> c_int;
/*
** coroutine functions
*/
pub fn lua_yield(L: *mut lua_State, nresults: c_int) -> c_int;
pub fn lua_resume(L: *mut lua_State, narg: c_int) -> c_int;
pub fn lua_status(L: *mut lua_State) -> c_int;
}
/*
** garbage-collection function and options
*/
pub const LUA_GCSTOP: c_int = 0;
pub const LUA_GCRESTART: c_int = 1;
pub const LUA_GCCOLLECT: c_int = 2;
pub const LUA_GCCOUNT: c_int = 3;
pub const LUA_GCCOUNTB: c_int = 4;
pub const LUA_GCSTEP: c_int = 5;
pub const LUA_GCSETPAUSE: c_int = 6;
pub const LUA_GCSETSTEPMUL: c_int = 7;
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn lua_gc(L: *mut lua_State, what: c_int, data: c_int) -> c_int;
/*
** miscellaneous functions
*/
pub fn lua_error(L: *mut lua_State) -> !;
pub fn lua_next(L: *mut lua_State, idx: c_int) -> c_int;
pub fn lua_concat(L: *mut lua_State, n: c_int);
pub fn lua_getallocf(L: *mut lua_State, ud: *mut *mut c_void) -> lua_Alloc;
pub fn lua_setallocf(L: *mut lua_State, f: lua_Alloc, ud: *mut c_void);
}
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
pub unsafe fn lua_pop(L: *mut lua_State, n: c_int) {
unsafe { lua_settop(L, -n - 1) }
}
pub unsafe fn lua_newtable(L: *mut lua_State) {
unsafe { lua_createtable(L, 0, 0) }
}
pub unsafe fn lua_register(L: *mut lua_State, n: *const c_char, f: lua_CFunction) {
unsafe { (lua_pushcfunction(L, f), lua_setglobal(L, n)) };
}
pub unsafe fn lua_pushcfunction(L: *mut lua_State, f: lua_CFunction) {
unsafe { lua_pushcclosure(L, f, 0) }
}
pub unsafe fn lua_strlen(L: *mut lua_State, i: c_int) -> usize {
unsafe { lua_objlen(L, i) }
}
pub unsafe fn lua_isfunction(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TFUNCTION) as c_int }
}
pub unsafe fn lua_istable(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TTABLE) as c_int }
}
pub unsafe fn lua_islightuserdata(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TLIGHTUSERDATA) as c_int }
}
pub unsafe fn lua_isnil(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TNIL) as c_int }
}
pub unsafe fn lua_isboolean(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TBOOLEAN) as c_int }
}
pub unsafe fn lua_isthread(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TTHREAD) as c_int }
}
pub unsafe fn lua_isnone(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) == LUA_TNONE) as c_int }
}
pub unsafe fn lua_isnoneornil(L: *mut lua_State, n: c_int) -> c_int {
unsafe { (lua_type(L, n) <= LUA_TNIL) as c_int }
}
pub unsafe fn lua_pushliteral(L: *mut lua_State, s: impl AsRef<[u8]>) {
unsafe { luab_pushstring(L, s) }
}
pub unsafe fn lua_setglobal(L: *mut lua_State, s: *const c_char) {
unsafe { lua_setfield(L, LUA_GLOBALSINDEX, s) }
}
pub unsafe fn lua_getglobal(L: *mut lua_State, s: *const c_char) {
unsafe { lua_getfield(L, LUA_GLOBALSINDEX, s) }
}
pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
unsafe { lua_tolstring(L, i, ptr::null_mut()) }
}
/*
** compatibility macros and functions
*/
pub unsafe fn lua_getregistry(L: *mut lua_State) {
unsafe { lua_pushvalue(L, LUA_REGISTRYINDEX) }
}
pub unsafe fn lua_getgccount(L: *mut lua_State) -> c_int {
unsafe { lua_gc(L, LUA_GCCOUNT, 0) }
}
pub type lua_Chunkreader = lua_Reader;
pub type lua_Chunkwriter = lua_Writer;
/* hack */
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn lua_setlevel(from: *mut lua_State, to: *mut lua_State);
}
/*
** {======================================================================
** Debug API
** =======================================================================
*/
/*
** Event codes
*/
pub const LUA_HOOKCALL: c_int = 0;
pub const LUA_HOOKRET: c_int = 1;
pub const LUA_HOOKLINE: c_int = 2;
pub const LUA_HOOKCOUNT: c_int = 3;
pub const LUA_HOOKTAILCALL: c_int = 4;
/*
** Event masks
*/
pub const LUA_MASKCALL: c_int = 1 << (LUA_HOOKCALL as usize);
pub const LUA_MASKRET: c_int = 1 << (LUA_HOOKRET as usize);
pub const LUA_MASKLINE: c_int = 1 << (LUA_HOOKLINE as usize);
pub const LUA_MASKCOUNT: c_int = 1 << (LUA_HOOKCOUNT as usize);
/* Functions to be called by the debuger in specific events */
pub type lua_Hook = unsafe extern "C-unwind" fn(L: *mut lua_State, ar: *mut lua_Debug);
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn lua_getstack(L: *mut lua_State, level: c_int, ar: *mut lua_Debug) -> c_int;
pub fn lua_getinfo(L: *mut lua_State, what: *const c_char, ar: *mut lua_Debug) -> c_int;
pub fn lua_getlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_setlocal(L: *mut lua_State, ar: *const lua_Debug, n: c_int) -> *const c_char;
pub fn lua_getupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_sethook(
L: *mut lua_State,
func: Option<lua_Hook>,
mask: c_int,
count: c_int,
) -> c_int;
pub fn lua_gethook(L: *mut lua_State) -> Option<lua_Hook>;
pub fn lua_gethookmask(L: *mut lua_State) -> c_int;
pub fn lua_gethookcount(L: *mut lua_State) -> c_int;
}
/* From Lua 5.2. */
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn lua_upvalueid(L: *mut lua_State, idx: c_int, n: c_int) -> *mut c_void;
pub fn lua_upvaluejoin(L: *mut lua_State, idx1: c_int, n1: c_int, idx2: c_int, n2: c_int);
pub fn lua_loadx(
L: *mut lua_State,
reader: lua_Reader,
dt: *mut c_void,
chunkname: *const c_char,
mode: *const c_char,
) -> c_int;
pub fn lua_version(L: *mut lua_State) -> *const lua_Number;
pub fn lua_copy(L: *mut lua_State, fromidx: c_int, toidx: c_int);
pub fn lua_tonumberx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Number;
pub fn lua_tointegerx(L: *mut lua_State, idx: c_int, isnum: *mut c_int) -> lua_Integer;
}
/* From Lua 5.3. */
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn lua_isyieldable(L: *mut lua_State) -> c_int;
}
/* activation record */
#[repr(C)]
pub struct lua_Debug {
pub event: c_int,
pub name: *const c_char, /* (n) */
pub namewhat: *const c_char, /* (n) `global', `local', `field', `method' */
pub what: *const c_char, /* (S) `Lua', `C', `main', `tail' */
pub source: *const c_char, /* (S) */
pub currentline: c_int, /* (l) */
pub nups: c_int, /* (u) number of upvalues */
pub linedefined: c_int, /* (S) */
pub lastlinedefined: c_int, /* (S) */
pub short_src: [c_char; LUA_IDSIZE as usize], /* (S) */
/* private part */
i_ci: c_int,
}
pub const LUA_IDSIZE: c_int = 60; /* Size of lua_Debug.short_src. */
/***** lualib.h *****/
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn luaopen_base(L: *mut lua_State) -> c_int;
pub fn luaopen_math(L: *mut lua_State) -> c_int;
pub fn luaopen_string(L: *mut lua_State) -> c_int;
pub fn luaopen_table(L: *mut lua_State) -> c_int;
pub fn luaopen_io(L: *mut lua_State) -> c_int;
pub fn luaopen_os(L: *mut lua_State) -> c_int;
pub fn luaopen_package(L: *mut lua_State) -> c_int;
pub fn luaopen_debug(L: *mut lua_State) -> c_int;
pub fn luaopen_bit(L: *mut lua_State) -> c_int;
pub fn luaopen_jit(L: *mut lua_State) -> c_int;
pub fn luaopen_ffi(L: *mut lua_State) -> c_int;
pub fn luaopen_string_buffer(L: *mut lua_State) -> c_int;
}
/***** lauxlib.h *****/
#[link(name = "luajit", kind = "static")]
unsafe extern "C-unwind" {
pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);
}
#[derive(Debug, Clone, Copy)]
pub enum Library {
Base,
Math,
String,
Table,
Io,
Os,
Package,
Debug,
Bit,
Jit,
Ffi,
StringBuffer,
}
impl Library {
pub const ALL: &[Library] = &[
Library::Base,
Library::Math,
Library::String,
Library::Table,
Library::Io,
Library::Os,
Library::Package,
Library::Debug,
Library::Bit,
Library::Jit,
Library::Ffi,
Library::StringBuffer,
];
pub fn name(&self) -> &'static str {
match self {
Self::Base => "base",
Self::Math => "math",
Self::String => "string",
Self::Table => "table",
Self::Io => "io",
Self::Os => "os",
Self::Package => "package",
Self::Debug => "debug",
Self::Bit => "bit",
Self::Jit => "jit",
Self::Ffi => "ffi",
Self::StringBuffer => "string_buffer",
}
}
fn get_open_fn(&self) -> lua_CFunction {
match self {
Self::Base => luaopen_base,
Self::Math => luaopen_math,
Self::String => luaopen_string,
Self::Table => luaopen_table,
Self::Io => luaopen_io,
Self::Os => luaopen_os,
Self::Package => luaopen_package,
Self::Debug => luaopen_debug,
Self::Bit => luaopen_bit,
Self::Jit => luaopen_jit,
Self::Ffi => luaopen_ffi,
Self::StringBuffer => luaopen_string_buffer,
}
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("out of memory")]
OutOfMemory,
#[error("syntax error in chunk '{chunk}': {msg}")]
Syntax { chunk: BString, msg: BString },
#[error("bad chunkname: {0}")]
BadChunkName(NulError),
#[error("{msg}")]
Call { msg: BString },
#[error("{msg}")]
Resume { msg: BString, trace: BString },
}
#[derive(Debug, Clone, Copy, Default)]
pub enum LoadMode {
#[default]
Auto,
Text,
Binary,
}
impl LoadMode {
fn mode_str(&self) -> &'static CStr {
match self {
Self::Auto => c"bt",
Self::Text => c"t",
Self::Binary => c"b",
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub enum ResumeStatus {
#[default]
Ok,
Suspended,
}
unsafe fn luab_pushstring(L: *mut lua_State, s: impl AsRef<[u8]>) {
unsafe { lua_pushlstring(L, s.as_ref().as_ptr().cast(), s.as_ref().len()) }
}
// SAFETY: returned reference is valid only as long as it is present in the stack
unsafe fn luab_tostring(L: *mut lua_State, idx: c_int) -> Option<&'static BStr> {
unsafe {
let mut len = 0;
let ptr = lua_tolstring(L, idx, &mut len);
(!ptr.is_null()).then(|| slice::from_raw_parts(ptr.cast(), len).into())
}
}
unsafe fn luab_load(
L: *mut lua_State,
name: Option<&CStr>,
chunk: impl AsRef<[u8]>,
mode: LoadMode,
) -> Result<(), Error> {
unsafe {
type ReaderState<'s> = Option<&'s [u8]>;
let name = name.unwrap_or(c"?");
let mut state: ReaderState = Some(chunk.as_ref());
unsafe extern "C-unwind" fn read_cb(
L: *mut lua_State,
state: *mut c_void,
size: *mut usize,
) -> *const c_char {
unsafe {
if let Some(chunk) = (*(state as *mut ReaderState)).take() {
*size = chunk.len();
chunk.as_ptr() as *const c_char
} else {
*size = 0;
ptr::null()
}
}
}
lua_checkstack(L, 1);
match lua_loadx(
L,
read_cb,
&raw mut state as *mut c_void,
name.as_ptr(),
mode.mode_str().as_ptr(),
) {
LUA_OK => Ok(()),
LUA_ERRMEM => {
lua_pop(L, 1);
Err(Error::OutOfMemory)
}
LUA_ERRSYNTAX => {
let chunk = name.to_bytes().as_bstr().to_owned();
let msg = luab_tostring(L, -1)
.filter(|s| !s.is_empty())
.unwrap_or(b"unknown error".into())
.to_owned();
lua_pop(L, 1);
Err(Error::Syntax { chunk, msg })
}
_ => unreachable!(),
}
}
}
unsafe fn luab_call(L: *mut lua_State, narg: c_int, nret: c_int) -> Result<c_int, Error> {
unsafe {
assert!(
matches!(lua_status(L), LUA_OK | LUA_ERRERR), // see lua_pcall in lj_api.c
"thread {L:?} called in wrong state"
);
let base = lua_gettop(L) - (narg + 1);
match lua_pcall(L, narg, nret, 0) {
LUA_OK => {
let n = lua_gettop(L) - base;
assert!(n == nret || nret == LUA_MULTRET);
Ok(n)
}
LUA_ERRMEM => {
lua_pop(L, 1);
Err(Error::OutOfMemory)
}
LUA_ERRRUN | LUA_ERRERR => {
let msg = luab_tostring(L, -1)
.filter(|s| !s.is_empty())
.unwrap_or(b"unknown error".into())
.to_owned();
lua_pop(L, 1);
Err(Error::Call { msg })
}
_ => unreachable!(),
}
}
}
unsafe fn luab_resume(L: *mut lua_State, narg: c_int) -> Result<ResumeStatus, Error> {
unsafe {
assert!(
matches!(lua_status(L), LUA_OK | LUA_YIELD), // see lua_resume in lj_api.c
"cannot resume dead thread {L:?}"
);
match lua_resume(L, narg) {
LUA_OK => Ok(ResumeStatus::Ok),
LUA_YIELD => Ok(ResumeStatus::Suspended),
LUA_ERRMEM => {
lua_pop(L, 1);
Err(Error::OutOfMemory)
}
LUA_ERRRUN | LUA_ERRERR => {
luaL_traceback(L, L, ptr::null(), 0);
let msg = luab_tostring(L, -2)
.filter(|s| !s.is_empty())
.unwrap_or(b"unknown error".into())
.to_owned();
let trace = luab_tostring(L, -1)
.map(|s| s.strip_prefix(b"stack traceback:\n").unwrap_or(s).as_bstr())
.filter(|s| !s.is_empty())
.unwrap_or(b"<stack trace unavailable>".into())
.to_owned();
lua_pop(L, 2);
Err(Error::Resume { msg, trace })
}
_ => unreachable!(),
}
}
}
#[derive(Debug)]
struct StateInner(NonNull<lua_State>);
impl Drop for StateInner {
fn drop(&mut self) {
unsafe { lua_close(self.0.as_ptr()) }
}
}
#[derive(Debug)]
pub struct State {
inner: Rc<StateInner>,
ptr: NonNull<lua_State>,
}
impl State {
pub fn new() -> Result<Self, Error> {
unsafe {
let ptr = NonNull::new(lua_newstate(Self::alloc_cb, ptr::null_mut()))
.ok_or(Error::OutOfMemory)?;
let state = Self {
inner: Rc::new(StateInner(ptr)),
ptr,
};
lua_atpanic(state.as_ptr(), Self::panic_cb);
// lua_pushcfunction(state.as_ptr(), lua_handle_panic);
// lua_replace(state.as_ptr(), lua_upvalueindex(1));
Ok(state)
}
}
unsafe extern "C" fn alloc_cb(
_ud: *mut c_void,
ptr: *mut c_void,
_osize: usize,
nsize: usize,
) -> *mut c_void {
unsafe {
// NOTE: same as the reference implementation used by luaL_newstate
// https://www.lua.org/manual/5.1/manual.html#lua_Alloc
if nsize == 0 {
mi_free(ptr);
ptr::null_mut()
} else {
mi_realloc(ptr, nsize)
}
}
}
unsafe extern "C-unwind" fn panic_cb(L: *mut lua_State) -> c_int {
unsafe {
let msg = luab_tostring(L, -1)
.filter(|s| !s.is_empty())
.unwrap_or(b"unknown error".into());
panic!("{msg}")
}
}
pub fn open_lib(&self, lib: Library) -> Result<(), Error> {
unsafe {
lua_checkstack(self.as_ptr(), 2);
lua_pushcfunction(self.as_ptr(), lib.get_open_fn());
lua_pushliteral(self.as_ptr(), lib.name());
luab_call(self.as_ptr(), 1, 0).map(|_| ())
}
}
pub fn open_libs<'a>(&self, libs: impl IntoIterator<Item = &'a Library>) -> Result<(), Error> {
for lib in libs {
self.open_lib(*lib)?;
}
Ok(())
}
pub fn as_ptr(&self) -> *mut lua_State {
self.ptr.as_ptr()
}
pub fn top(&self) -> c_int {
unsafe { lua_gettop(self.as_ptr()) }
}
pub fn absindex(&self, idx: c_int) -> c_int {
// SAFETY: refer to index2adr in lj_api.c
unsafe {
if idx > 0 {
let top = lua_gettop(self.as_ptr());
assert!(idx <= top, "invalid stack index {idx} (size={top})");
idx
} else if idx > LUA_REGISTRYINDEX {
let top = lua_gettop(self.as_ptr());
let i = top + idx + 1;
assert!(0 < i && i <= top, "invalid stack index {idx} (size={top})");
i
} else {
idx
}
}
}
pub fn push(&self, idx: c_int) {
unsafe {
lua_checkstack(self.as_ptr(), 1);
lua_pushvalue(self.as_ptr(), self.absindex(idx));
}
}
pub fn push_string(&self, s: impl AsRef<[u8]>) {
unsafe {
lua_checkstack(self.as_ptr(), 1);
luab_pushstring(self.as_ptr(), s);
}
}
pub fn push_pointer<T>(&self, p: *const T) {
unsafe {
lua_checkstack(self.as_ptr(), 1);
lua_pushlightuserdata(self.as_ptr(), p as *mut c_void);
}
}
pub fn push_require(&self, module: impl AsRef<[u8]>) -> Result<(), Error> {
unsafe {
lua_checkstack(self.as_ptr(), 2);
luab_pushstring(self.as_ptr(), "require");
lua_rawget(self.as_ptr(), LUA_GLOBALSINDEX);
luab_pushstring(self.as_ptr(), module);
Ok(())
}
}
pub fn to_string(&self, idx: c_int) -> Option<BString> {
// SAFETY: we copy the lua-owned string immediately so no reference to it is kept
unsafe { luab_tostring(self.as_ptr(), self.absindex(idx)).map(|s| s.to_owned()) }
}
pub fn pop(&self, n: c_int) {
unsafe {
let top = lua_gettop(self.as_ptr());
assert!(
0 < n && n <= top,
"cannot pop {n} value(s) from the stack (size={top})"
);
lua_pop(self.as_ptr(), n);
}
}
pub fn get_table(&self, idx: c_int) {
unsafe {
let top = lua_gettop(self.as_ptr());
assert!(top >= 1, "expected 1 value on the stack (size={top})");
lua_rawget(self.as_ptr(), self.absindex(idx));
}
}
pub fn set_table(&self, idx: c_int) {
unsafe {
let top = lua_gettop(self.as_ptr());
assert!(top >= 2, "expected 2 values on the stack (size={top})");
lua_rawset(self.as_ptr(), self.absindex(idx));
}
}
pub fn load(
&self,
name: Option<impl AsRef<[u8]>>,
chunk: impl AsRef<[u8]>,
mode: LoadMode,
) -> Result<(), Error> {
unsafe {
luab_load(
self.as_ptr(),
name.map(|s| CString::new(s.as_ref()))
.transpose()
.map_err(Error::BadChunkName)?
.as_deref(),
chunk,
mode,
)
}
}
pub fn call(&self, narg: c_int, nret: c_int) -> Result<c_int, Error> {
unsafe {
let top = lua_gettop(self.as_ptr());
let need = narg + 1; // need function on the stack too
assert!(
0 <= need && need <= top,
"expected {need} value(s) on the stack (size={top})"
);
assert!(0 <= nret || nret == LUA_MULTRET);
luab_call(self.as_ptr(), narg, nret)
}
}
pub fn new_thread(&self) -> State {
unsafe {
lua_checkstack(self.as_ptr(), 2);
// SAFETY: lua_newthread never returns null, but may throw on oom
let state = State {
ptr: NonNull::new_unchecked(lua_newthread(self.as_ptr())),
inner: Rc::clone(&self.inner),
};
lua_pushlightuserdata(self.as_ptr(), state.as_ptr() as *mut c_void);
lua_insert(self.as_ptr(), -2); // map thread pointer to thread
lua_rawset(self.as_ptr(), LUA_REGISTRYINDEX);
state
}
}
pub fn resume(&self, narg: c_int) -> Result<ResumeStatus, Error> {
unsafe {
let top = lua_gettop(self.as_ptr());
let need = if lua_status(self.as_ptr()) == LUA_OK {
narg + 1 // need function on the stack too if not already suspended
} else {
narg
};
assert!(
0 <= need && need <= top,
"expected {need} value(s) on the stack (size={top})"
);
luab_resume(self.as_ptr(), narg)
}
}
}
impl fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let version = unsafe { *lua_version(self.as_ptr()) };
write!(f, "lua {version} {:p}", self.as_ptr())
}
}

100
luabi/src/main.rs Normal file
View File

@ -0,0 +1,100 @@
use clap::Parser;
use lz4_flex::decompress_size_prepended;
use mimalloc::MiMalloc;
use owo_colors::OwoColorize;
use std::{
backtrace::Backtrace,
fmt, fs, panic,
thread::{self, Thread},
};
pub mod core;
pub mod hash;
pub mod lua;
pub mod str;
#[global_allocator]
static GLOBAL: MiMalloc = MiMalloc;
static LIB_RT: &'static [u8] = include_bytes!(concat!(env!("OUT_DIR"), "/runtime.lua.lz4"));
#[derive(Debug, Parser)]
struct Args {
/// Path to the main script.
#[clap(value_name = "SCRIPT")]
path: Option<String>,
/// Execute string 'chunk'.
#[clap(long, short = 'e', value_name = "CHUNK")]
evals: Vec<String>,
/// Require library 'name'.
#[clap(long, short = 'l', value_name = "NAME")]
libs: Vec<String>,
}
fn panic_cb(panic: &panic::PanicHookInfo) {
struct Message<'a> {
thread: Thread,
panic: &'a panic::PanicHookInfo<'a>,
}
impl<'s> fmt::Display for Message<'s> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let thread = self.thread.name().unwrap_or("<unnamed>");
let location = self.panic.location().unwrap();
let payload = self.panic.payload();
let msg = if let Some(s) = payload.downcast_ref::<&'static str>() {
s
} else if let Some(s) = payload.downcast_ref::<String>() {
s.as_str()
} else {
"unknown error"
};
write!(f, "thread '{thread}' panicked at {location}:\n{msg}")
}
}
let trace = Backtrace::force_capture();
let thread = thread::current();
print!("{}\n{trace}", Message { thread, panic }.red());
}
fn main() {
panic::set_hook(Box::new(panic_cb));
let args = Args::parse();
let vm = new_vm().unwrap_or_else(|err| panic!("failed to initialise runtime: {err}"));
let main = vm.new_thread();
if let Some(ref path) = args.path {
match main
.load(
Some(format!("@{path}")),
fs::read(path).unwrap_or_else(|err| panic!("{err}")),
lua::LoadMode::Auto,
)
.and_then(|()| main.resume(0))
{
Ok(status) => println!("ok: {status:?}"),
Err(err) => match err {
lua::Error::Resume { msg, trace } => println!("{}\n{trace}", msg.red()),
err => println!("{}", err.red()),
},
}
}
}
fn new_vm() -> Result<lua::State, lua::Error> {
let vm = lua::State::new()?;
vm.open_libs(lua::Library::ALL)?;
vm.load(
Some("@[luabi]"),
decompress_size_prepended(LIB_RT).unwrap(),
lua::LoadMode::Text,
)?;
vm.call(0, 0)?;
Ok(vm)
}

70
luabi/src/str.rs Normal file
View File

@ -0,0 +1,70 @@
use crate::core::{LbBuf, LbRes};
use std::{
ops::{Deref, DerefMut},
ptr, slice,
str::Utf8Error,
};
use thiserror::Error;
#[repr(C)]
#[derive(Debug, Error)]
pub enum LbStrError {
#[error("{0}")]
InvalidUtf8(Utf8Error),
}
#[repr(C)]
pub struct LbStr {
buf: LbBuf,
}
impl LbStr {
pub fn new() -> Self {
String::new().into()
}
}
impl Deref for LbStr {
type Target = str;
fn deref(&self) -> &Self::Target {
unsafe { std::str::from_utf8_unchecked(&self.buf) }
}
}
impl DerefMut for LbStr {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { std::str::from_utf8_unchecked_mut(&mut self.buf) }
}
}
impl From<String> for LbStr {
fn from(value: String) -> Self {
Self {
buf: value.into_bytes().into(),
}
}
}
impl From<LbStr> for String {
fn from(value: LbStr) -> Self {
unsafe { String::from_utf8_unchecked(value.buf.into()) }
}
}
impl LbStr {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_str_new(ptr: *const u8, len: usize) -> LbRes<LbStr> {
std::str::from_utf8(unsafe { slice::from_raw_parts(ptr, len) })
.map_err(|err| LbStrError::InvalidUtf8(err).into())
.map(|str| LbStr {
buf: str.as_bytes().to_vec().into(),
})
.into()
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn lb_str_drop(&mut self) {
unsafe { ptr::drop_in_place(self) }
}
}

1
luajit Submodule

@ -0,0 +1 @@
Subproject commit f9140a622a0c44a99efb391cc1c2358bc8098ab7

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 100

7
stylua.toml Normal file
View File

@ -0,0 +1,7 @@
syntax = "LuaJIT"
indent_type = "Spaces"
indent_width = 2
column_width = 100
quote_style = "ForceDouble"
call_parentheses = "NoSingleTable"
collapse_simple_statement = "ConditionalOnly"