perf: Lua encode + cache + scanner + validation stack optimizations #135
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [master, main] | |
| pull_request: | |
| env: | |
| CARGO_TERM_COLOR: always | |
| jobs: | |
| rust: | |
| name: Rust tests (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-14] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Rust (stable) | |
| run: | | |
| rustup toolchain install stable --profile minimal --no-self-update | |
| rustup default stable | |
| - name: Cache cargo registry & target | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: cargo-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.toml') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}-${{ runner.arch }}- | |
| - name: Build (release) | |
| run: cargo build --release | |
| - name: Test (release) | |
| run: cargo test --release | |
| - name: Test scalar-only (no AVX2/NEON feature) | |
| run: cargo test --release --no-default-features | |
| - name: Test with test-panic feature | |
| run: cargo test --features test-panic --release | |
| lua: | |
| name: Lua integration tests | |
| runs-on: ubuntu-latest | |
| needs: rust | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Rust (stable) | |
| run: | | |
| rustup toolchain install stable --profile minimal --no-self-update | |
| rustup default stable | |
| - name: Cache cargo registry & target | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| target | |
| key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.toml') }} | |
| restore-keys: | | |
| cargo-${{ runner.os }}- | |
| - name: Build cdylib | |
| run: cargo build --release | |
| - name: Install LuaJIT, LuaRocks and dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y luajit lua5.1 liblua5.1-0-dev luarocks | |
| # luarocks on Ubuntu targets lua5.1 by default; LuaJIT is ABI-compatible | |
| # with 5.1 so rocks built for 5.1 load fine under luajit. | |
| sudo luarocks install busted | |
| sudo luarocks install lua-cjson | |
| - name: Run busted tests (under LuaJIT) | |
| run: | | |
| # ffi.load("qjson") uses dlopen which respects LD_LIBRARY_PATH, | |
| # not LuaJIT's package.cpath. Point dlopen at the release build dir. | |
| LD_LIBRARY_PATH="$PWD/target/release" \ | |
| busted --lua=$(which luajit) tests/lua \ | |
| --lpath='./lua/?.lua' | |
| - name: Validate LuaRocks package | |
| run: | | |
| rm -rf /tmp/lua-qjson-rock | |
| ROCKSPEC="$(python3 - <<'PY' | |
| import re | |
| from pathlib import Path | |
| pattern = re.compile(r"^lua-qjson-(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?-(\d+)\.rockspec$") | |
| def prerelease_key(value): | |
| if value is None: | |
| return () | |
| key = [] | |
| for part in value.split("."): | |
| if part.isdigit(): | |
| key.append((0, int(part))) | |
| else: | |
| key.append((1, part)) | |
| return tuple(key) | |
| matches = [] | |
| for path in Path("rockspec").glob("lua-qjson-*.rockspec"): | |
| match = pattern.match(path.name) | |
| if match: | |
| major, minor, patch, prerelease, revision = match.groups() | |
| matches.append((int(major), int(minor), int(patch), prerelease is None, prerelease_key(prerelease), int(revision), str(path))) | |
| if not matches: | |
| raise SystemExit("no lua-qjson rockspec found") | |
| print(max(matches)[-1]) | |
| PY | |
| )" | |
| luarocks make "$ROCKSPEC" --tree /tmp/lua-qjson-rock | |
| eval "$(luarocks path --tree /tmp/lua-qjson-rock)" | |
| unset LD_LIBRARY_PATH | |
| luajit -e 'local qjson = require("qjson"); local doc = qjson.parse("{\"a\":42}"); assert(doc:get_i64("a") == 42)' | |
| busted --lua=$(which luajit) tests/lua | |
| package: | |
| name: LuaRocks package (${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| needs: rust | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-14] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Install Rust (stable) | |
| run: | | |
| rustup toolchain install stable --profile minimal --no-self-update | |
| rustup default stable | |
| - name: Install LuaJIT | |
| uses: leafo/gh-actions-lua@v13 | |
| with: | |
| luaVersion: "luajit-2.1.0-beta3" | |
| - name: Install LuaRocks | |
| uses: leafo/gh-actions-luarocks@v4 | |
| with: | |
| luarocksVersion: "3.11.1" | |
| - name: Validate LuaRocks package | |
| run: | | |
| rm -rf /tmp/lua-qjson-rock | |
| ROCKSPEC="$(python3 - <<'PY' | |
| import re | |
| from pathlib import Path | |
| pattern = re.compile(r"^lua-qjson-(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?-(\d+)\.rockspec$") | |
| def prerelease_key(value): | |
| if value is None: | |
| return () | |
| key = [] | |
| for part in value.split("."): | |
| if part.isdigit(): | |
| key.append((0, int(part))) | |
| else: | |
| key.append((1, part)) | |
| return tuple(key) | |
| matches = [] | |
| for path in Path("rockspec").glob("lua-qjson-*.rockspec"): | |
| match = pattern.match(path.name) | |
| if match: | |
| major, minor, patch, prerelease, revision = match.groups() | |
| matches.append((int(major), int(minor), int(patch), prerelease is None, prerelease_key(prerelease), int(revision), str(path))) | |
| if not matches: | |
| raise SystemExit("no lua-qjson rockspec found") | |
| print(max(matches)[-1]) | |
| PY | |
| )" | |
| luarocks make "$ROCKSPEC" --tree /tmp/lua-qjson-rock | |
| eval "$(luarocks path --tree /tmp/lua-qjson-rock)" | |
| unset LD_LIBRARY_PATH | |
| unset DYLD_LIBRARY_PATH | |
| lua -e 'assert(jit, "LuaJIT required"); local qjson = require("qjson"); local doc = qjson.parse("{\"a\":42}"); assert(doc:get_i64("a") == 42)' |