mirror of https://github.com/tailix/libclayer.git
Use the code from libkernaux
This commit is contained in:
commit
772db061c5
|
@ -0,0 +1,104 @@
|
|||
# We use Cirrus CI to test on FreeBSD.
|
||||
# For GNU/Linux CI see GitHub Actions.
|
||||
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-1
|
||||
|
||||
main_freebsd_task:
|
||||
name: Main (FreeBSD)
|
||||
only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_BASE_BRANCH == 'master'
|
||||
dependencies_script:
|
||||
- pkg install --yes autoconf automake cppcheck libtool libyaml py39-pip py39-wheel python3
|
||||
- pip install --user Jinja2 PyYAML
|
||||
main_build_script:
|
||||
- ./autogen.sh
|
||||
- ./configure --enable-fixtures --enable-checks-all CFLAGS='-O3'
|
||||
- make
|
||||
- sudo make install
|
||||
main_test_script:
|
||||
- make check
|
||||
|
||||
mruby_freebsd_task:
|
||||
name: mruby (FreeBSD)
|
||||
only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_BASE_BRANCH == 'master'
|
||||
env:
|
||||
CPATH: '/usr/local/include'
|
||||
LIBRARY_PATH: '/usr/local/lib'
|
||||
MRUBY_YAML_USE_SYSTEM_LIBRARY: x
|
||||
dependencies_script:
|
||||
- pkg install --yes autoconf automake git libtool rubygem-rake wget
|
||||
dependencies_mruby_script:
|
||||
- wget https://github.com/mruby/mruby/archive/3.1.0.zip -O mruby-3.1.0.zip
|
||||
- unzip mruby-3.1.0.zip
|
||||
main_build_script:
|
||||
- ./autogen.sh
|
||||
- ./configure CFLAGS='-O3'
|
||||
- make
|
||||
- sudo make install
|
||||
mruby_test_script:
|
||||
- cd mruby-3.1.0
|
||||
- MRUBY_CONFIG=../bindings/mruby/build_config.rb rake test
|
||||
|
||||
ruby_freebsd_task:
|
||||
name: Ruby (FreeBSD)
|
||||
only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_BASE_BRANCH == 'master'
|
||||
env:
|
||||
CPATH: '/usr/local/include'
|
||||
LIBRARY_PATH: '/usr/local/lib'
|
||||
dependencies_script:
|
||||
- pkg install --yes autoconf automake cppcheck git libtool wget
|
||||
dependencies_ruby_script:
|
||||
- wget https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.3.tar.gz
|
||||
- tar -xzf ruby-3.0.3.tar.gz
|
||||
- cd ruby-3.0.3
|
||||
- ./configure --disable-install-doc
|
||||
- make
|
||||
- sudo make install
|
||||
main_build_script:
|
||||
- ./autogen.sh
|
||||
- ./configure CFLAGS='-O3'
|
||||
- make
|
||||
- sudo make install
|
||||
ruby_build_script:
|
||||
- cd bindings/ruby
|
||||
- ./bin/setup
|
||||
- bundle exec rake compile
|
||||
ruby_test_script:
|
||||
- cd bindings/ruby
|
||||
- bundle exec rake
|
||||
|
||||
rust_freebsd_task:
|
||||
name: Rust (FreeBSD)
|
||||
only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_BASE_BRANCH == 'master'
|
||||
env:
|
||||
RUSTFLAGS: '-L /usr/local/lib'
|
||||
dependencies_script:
|
||||
- pkg install --yes autoconf automake libtool
|
||||
dependencies_rust_script:
|
||||
- curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
main_build_script:
|
||||
- ./autogen.sh
|
||||
- ./configure CFLAGS='-O3'
|
||||
- make
|
||||
- sudo make install
|
||||
rust_test_script:
|
||||
- cd bindings/rust
|
||||
- ~/.cargo/bin/cargo test
|
||||
- ~/.cargo/bin/cargo clippy
|
||||
- ~/.cargo/bin/cargo fmt --check
|
||||
|
||||
main_freebsd_port_task:
|
||||
name: Main (FreeBSD port)
|
||||
only_if: "changesInclude('.cirrus.yml', 'pkgs/freebsd/**')"
|
||||
dependencies_script:
|
||||
- pkg install --yes portfmt portlint porttools
|
||||
port_prepare_script:
|
||||
- echo 'DEVELOPER=yes' >> /etc/make.conf
|
||||
- rm -rf /usr/ports/devel/libkernaux/
|
||||
- cp -r $CIRRUS_WORKING_DIR/pkgs/freebsd/devel/libkernaux /usr/ports/devel/
|
||||
port_test_script:
|
||||
- cd /usr/ports/devel/libkernaux/
|
||||
- portfmt -D Makefile
|
||||
- portclippy --strict Makefile
|
||||
- portlint -A
|
||||
- port test .
|
|
@ -0,0 +1,9 @@
|
|||
deb [arch=i386,amd64] http://azure.archive.ubuntu.com/ubuntu jammy main restricted universe multiverse
|
||||
deb [arch=i386,amd64] http://azure.archive.ubuntu.com/ubuntu jammy-updates main restricted universe multiverse
|
||||
deb [arch=i386,amd64] http://azure.archive.ubuntu.com/ubuntu jammy-backports main restricted universe multiverse
|
||||
deb [arch=i386,amd64] http://azure.archive.ubuntu.com/ubuntu jammy-security main restricted universe multiverse
|
||||
|
||||
deb [arch=arm64,riscv64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse
|
||||
deb [arch=arm64,riscv64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse
|
||||
deb [arch=arm64,riscv64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse
|
||||
deb [arch=arm64,riscv64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted universe multiverse
|
|
@ -0,0 +1,148 @@
|
|||
name: Main
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install cppcheck
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --enable-checks-cppcheck
|
||||
- name: check
|
||||
run: make check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
assert: ['--disable-assert', '--enable-assert']
|
||||
cc: ['gcc', 'clang', 'tcc']
|
||||
opt: ['', '-O0', '-O3']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install clang tcc
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{matrix.assert}} --enable-fixtures --enable-checks --enable-checks-pthreads --enable-checks-python CC='${{matrix.cc}}' CFLAGS='${{matrix.opt}}'
|
||||
- name: make
|
||||
run: make
|
||||
- name: check
|
||||
run: make check || (./make/test-suite-log && false)
|
||||
- name: install
|
||||
run: sudo make install
|
||||
|
||||
cross:
|
||||
runs-on: ubuntu-22.04 # jammy, because it's in ".github/sources.list"
|
||||
strategy:
|
||||
matrix:
|
||||
cross:
|
||||
- arch: 'aarch64'
|
||||
apt_arch: 'arm64'
|
||||
apt_pkgs: 'crossbuild-essential-arm64'
|
||||
cprefix: 'aarch64-linux-gnu-'
|
||||
- arch: 'i386'
|
||||
apt_arch: 'i386'
|
||||
apt_pkgs: 'crossbuild-essential-i386'
|
||||
cprefix: 'i686-linux-gnu-'
|
||||
- arch: 'riscv64'
|
||||
apt_arch: 'riscv64'
|
||||
apt_pkgs: 'crossbuild-essential-riscv64'
|
||||
cprefix: 'riscv64-linux-gnu-'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: apt sources
|
||||
run: sudo cp .github/sources.list /etc/apt/sources.list
|
||||
- name: apt upgrade
|
||||
run: sudo apt-get --yes update && (sudo apt-get --yes upgrade || sudo apt-get --yes -f install)
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install binfmt-support qemu qemu-user-static ${{matrix.cross.apt_pkgs}}
|
||||
- name: dpkg arch
|
||||
run: sudo dpkg --add-architecture ${{matrix.cross.apt_arch}}
|
||||
- name: apt update
|
||||
run: sudo apt-get --yes update
|
||||
- name: cross dependencies
|
||||
run: sudo apt-get --yes install libc6:${{matrix.cross.apt_arch}}
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --host='${{matrix.cross.arch}}-unknown-elf' --enable-checks --enable-checks-pthreads --enable-checks-python CC='${{matrix.cross.cprefix}}gcc'
|
||||
- name: make
|
||||
run: make
|
||||
- name: check
|
||||
run: make check || (./make/test-suite-log && false)
|
||||
- name: install
|
||||
run: sudo make install
|
||||
|
||||
cond:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
packages:
|
||||
- without: 'all'
|
||||
- without: 'io'
|
||||
- without: 'ntoa'
|
||||
dependencies: '--without-printf --without-units'
|
||||
- without: 'printf'
|
||||
- without: 'memmap'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --enable-checks --enable-checks-pthreads --enable-checks-python --without-${{matrix.packages.without}} ${{matrix.packages.dependencies}}
|
||||
- name: make
|
||||
run: make
|
||||
- name: check
|
||||
run: make check || (./make/test-suite-log && false)
|
||||
- name: install
|
||||
run: sudo make install
|
||||
|
||||
freestanding:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
assert: ['--disable-assert', '--enable-assert']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: apt update
|
||||
run: sudo apt-get --yes update
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install crossbuild-essential-i386
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --host='i386-elf' ${{matrix.assert}} --enable-freestanding --with-libc CC="$(which i686-linux-gnu-gcc)"
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
dist:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure dist
|
||||
run: ./configure
|
||||
- name: dist
|
||||
run: make dist
|
||||
- name: extract
|
||||
run: tar -xzf "libkernaux-$(cat VERSION).tar.gz"
|
||||
- name: configure
|
||||
run: cd "libkernaux-$(cat VERSION)" && ./configure --enable-checks --enable-checks-pthreads --enable-checks-python
|
||||
- name: make
|
||||
run: cd "libkernaux-$(cat VERSION)" && make
|
||||
- name: check
|
||||
run: cd "libkernaux-$(cat VERSION)" && make check || (./make/test-suite-log && false)
|
||||
- name: install
|
||||
run: cd "libkernaux-$(cat VERSION)" && sudo make install
|
|
@ -0,0 +1,48 @@
|
|||
name: mruby
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MRUBY_YAML_USE_SYSTEM_LIBRARY: x
|
||||
strategy:
|
||||
matrix:
|
||||
assert: ['--disable-assert', '--enable-assert']
|
||||
packages:
|
||||
- configure: ''
|
||||
- configure: '--without-all'
|
||||
- configure: '--without-all --with-ntoa'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.0
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install cppcheck libyaml-dev rake
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{matrix.assert}} ${{matrix.packages.configure}} CFLAGS='-O3'
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: ldconfig
|
||||
run: sudo ldconfig
|
||||
- working-directory: vendor/mruby
|
||||
name: test
|
||||
run: MRUBY_CONFIG=../../bindings/mruby/build_config.rb rake test
|
||||
- working-directory: bindings/mruby
|
||||
name: setup
|
||||
run: ./bin/setup
|
||||
- working-directory: bindings/mruby
|
||||
name: lint
|
||||
run: rake
|
|
@ -0,0 +1,46 @@
|
|||
name: Ruby
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
assert: ['--disable-assert', '--enable-assert']
|
||||
packages:
|
||||
- configure: ''
|
||||
- configure: '--without-all'
|
||||
skip_coverage: 'x'
|
||||
- configure: '--without-all --with-ntoa'
|
||||
skip_coverage: 'x'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: 3.0
|
||||
- name: dependencies
|
||||
run: sudo apt-get --yes install cppcheck
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{matrix.assert}} ${{matrix.packages.configure}} CFLAGS='-O3'
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: ldconfig
|
||||
run: sudo ldconfig
|
||||
- working-directory: bindings/ruby
|
||||
name: setup
|
||||
run: ./bin/setup
|
||||
- working-directory: bindings/ruby
|
||||
name: compile
|
||||
run: rake compile
|
||||
- working-directory: bindings/ruby
|
||||
name: test & lint
|
||||
run: SKIP_COVERAGE='${{matrix.packages.skip_coverage}}' rake
|
|
@ -0,0 +1,45 @@
|
|||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
assert: ['--disable-assert', '--enable-assert']
|
||||
packages:
|
||||
- configure: ''
|
||||
- configure: '--without-all'
|
||||
cargo: '--no-default-features'
|
||||
- configure: '--without-all --with-ntoa'
|
||||
cargo: '--no-default-features --features ntoa'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt, clippy
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{matrix.assert}} ${{matrix.packages.configure}} CFLAGS='-O3'
|
||||
- name: make
|
||||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: ldconfig
|
||||
run: sudo ldconfig
|
||||
- working-directory: bindings/rust
|
||||
name: test
|
||||
run: cargo test ${{matrix.packages.cargo}}
|
||||
- working-directory: bindings/rust
|
||||
name: clippy
|
||||
run: cargo clippy
|
||||
- working-directory: bindings/rust
|
||||
name: fmt
|
||||
run: cargo fmt --check
|
|
@ -0,0 +1,85 @@
|
|||
/build/*
|
||||
!/build/.keep
|
||||
|
||||
##########################
|
||||
# Common generated files #
|
||||
##########################
|
||||
|
||||
*.a
|
||||
*.c.d
|
||||
*.la
|
||||
*.lo
|
||||
*.o
|
||||
|
||||
.deps/
|
||||
.dirstamp
|
||||
.libs/
|
||||
|
||||
############################
|
||||
# Always generated in root #
|
||||
############################
|
||||
|
||||
/INSTALL
|
||||
/aclocal.m4
|
||||
/ar-lib
|
||||
/autom4te.cache/
|
||||
/autoscan.log
|
||||
/compile
|
||||
/config.guess
|
||||
/config.h.in
|
||||
/config.h.in~
|
||||
/config.sub
|
||||
/configure
|
||||
/configure.ac~
|
||||
/configure~
|
||||
/depcomp
|
||||
/install-sh
|
||||
/ltmain.sh
|
||||
/missing
|
||||
/test-driver
|
||||
|
||||
/m4/*
|
||||
!/m4/.keep
|
||||
|
||||
/Makefile.in
|
||||
/examples/Makefile.in
|
||||
/fixtures/Makefile.in
|
||||
/include/Makefile.in
|
||||
/libc/Makefile.in
|
||||
/libc/include/Makefile.in
|
||||
/tests/Makefile.in
|
||||
|
||||
###########################################
|
||||
# Only generated when configuring in root #
|
||||
###########################################
|
||||
|
||||
/config.h
|
||||
/config.log
|
||||
/config.status
|
||||
/libtool
|
||||
/stamp-h1
|
||||
|
||||
/confcache
|
||||
/confdefs.h
|
||||
/confinc.mk
|
||||
/confmf.BSD
|
||||
/confmf.GNU
|
||||
/conftest.c
|
||||
/conftest.err
|
||||
|
||||
/examples/test-suite.log
|
||||
/examples/*.log
|
||||
/examples/*.trs
|
||||
/tests/test-suite.log
|
||||
/tests/test_*.log
|
||||
/tests/test_*.trs
|
||||
|
||||
/Makefile
|
||||
/examples/Makefile
|
||||
/fixtures/Makefile
|
||||
/include/Makefile
|
||||
/libc/Makefile
|
||||
/libc/include/Makefile
|
||||
/tests/Makefile
|
||||
|
||||
/libkernaux.pc
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "vendor/mruby"]
|
||||
path = vendor/mruby
|
||||
url = https://github.com/mruby/mruby.git
|
|
@ -0,0 +1,39 @@
|
|||
# We use sourcehut CI (https://builds.sr.ht) to test on OpenBSD.
|
||||
# For GNU/Linux CI see GitHub Actions.
|
||||
|
||||
image: openbsd/7.2
|
||||
arch: amd64
|
||||
sources:
|
||||
- https://github.com/tailix/libkernaux.git
|
||||
packages:
|
||||
- autoconf-2.71
|
||||
- automake-1.16.5
|
||||
- cppcheck
|
||||
- libyaml
|
||||
- m4
|
||||
- py3-pip
|
||||
- py3-wheel
|
||||
- python3
|
||||
- wget
|
||||
environment:
|
||||
AUTOCONF_VERSION: '2.71'
|
||||
AUTOMAKE_VERSION: '1.16'
|
||||
tasks:
|
||||
- libtool: |
|
||||
wget https://ftpmirror.gnu.org/libtool/libtool-2.4.7.tar.gz
|
||||
tar -xzf libtool-2.4.7.tar.gz
|
||||
cd libtool-2.4.7
|
||||
./configure
|
||||
make
|
||||
doas make install
|
||||
- dependencies: |
|
||||
pip3 install --user Jinja2 PyYAML
|
||||
- build: |
|
||||
cd libkernaux
|
||||
./autogen.sh
|
||||
./configure --enable-fixtures --enable-checks-all CFLAGS='-O3'
|
||||
make
|
||||
doas make install
|
||||
- test: |
|
||||
cd libkernaux
|
||||
make check
|
|
@ -0,0 +1,194 @@
|
|||
Common
|
||||
------
|
||||
|
||||
* Add your name to [COPYING](/COPYING).
|
||||
* Don't add your name to `AUTHORS` - it's for maintainers.
|
||||
* Add copyright notice in the beginning of changed files except the headers.
|
||||
* If you change the behavior (even just fix a bug) of **libkernaux** (stable) or
|
||||
[libc](/libc), add a record to [ChangeLog](/ChangeLog).
|
||||
|
||||
Prohibitions:
|
||||
|
||||
* Don't commit binary files
|
||||
* Don't commit configuration files of your editor or IDE
|
||||
* Don't use encodings other than ASCII and UTF-8
|
||||
* Don't use alphabets other than Latin
|
||||
* Don't use languages other than English
|
||||
* Don't use tabulations (I don't hate tabs, but people can not use them
|
||||
properly)
|
||||
* Don't leave trailing whitespaces
|
||||
* Don't forget the newline character in the end of files
|
||||
|
||||
The following statements are recommendations, but highly encouraged:
|
||||
|
||||
* Write documentation
|
||||
* Write tests
|
||||
* Keep lines less than 80 characters long for better experience on split screen
|
||||
|
||||
### Things to review periodically
|
||||
|
||||
* `git grep -i fixme`
|
||||
* `git grep -i todo`
|
||||
* `git grep -i cppcheck-suppress`
|
||||
* `git grep -i rubocop:disable`
|
||||
|
||||
### Programming mistakes
|
||||
|
||||
* Always check documentation, manuals and specifications
|
||||
|
||||
Avoid stupid errors with:
|
||||
|
||||
* Manual memory management
|
||||
* `malloc` may return `NULL`
|
||||
* Memory leak (forget to `free`)
|
||||
* Use after `free`/`realloc`
|
||||
* Double `free`/`realloc`
|
||||
* `free`ing/`realloc`ating unallocated memory
|
||||
* Changing the original pointer to the allocated memory (use `const`!)
|
||||
* `NULL` pointers and `nil`/`None`/whatever objects
|
||||
* Division by zero
|
||||
* Pointer arithmetic - consider type size
|
||||
* Type sizes (like `long` on 32-bit and 64-bit)
|
||||
* Integer arithmetic overflow
|
||||
* Bit shift
|
||||
* Endianness (byte order)
|
||||
* Data packing
|
||||
* Data alignment
|
||||
* Thread safety
|
||||
* Undefined behavior
|
||||
* Logical expressions (tautology, whatever)
|
||||
* Checking for an error (return value, pointer argument, whatever)
|
||||
* Use of not fully initialized data
|
||||
* Not reading beyond a buffer, array or string
|
||||
* The index of the last item, which is less than the buffer size
|
||||
* Negative indices
|
||||
* The terminating null character in a string
|
||||
* Allowed values of arguments
|
||||
* Possible values of parameters
|
||||
* Operator precedence
|
||||
* Default case in switch statements
|
||||
* Braces (curly brackets) around code blocks
|
||||
|
||||
|
||||
|
||||
C language
|
||||
----------
|
||||
|
||||
Use **cppcheck**.
|
||||
|
||||
* Name regular functions (*not methods*) and variables in lower snake case
|
||||
(example: `foo_bar`).
|
||||
* Name macros in upper snake case (example: `FOO_BAR`).
|
||||
* Name types (*structures, unions, enumerations and type definitions*) in Pascal
|
||||
case (example: `FooBar`).
|
||||
* Name nested types in Pascal case and with the prefix of the surrounding type
|
||||
in Pascal case, separate type names with underscores (example:
|
||||
`FooBar_CarCdr`).
|
||||
* Name methods (*functions that belong to a specific type*) in lower snake case
|
||||
and with the prefix of the type name in Pascal case (example:
|
||||
`FooBar_car_cdr`).
|
||||
|
||||
* Name public (*defined in the headers and exported as symbols*) regular
|
||||
functions (*not methods*) and variables with the prefix `kernaux_` (example:
|
||||
`kernaux_foo_bar`).
|
||||
* Name public (*defined in the headers*) macros with the prefix `KERNAUX_`
|
||||
(example: `KERNAUX_FOO_BAR`).
|
||||
* Name public (*defined in the headers*) types with the prefix `KernAux_`
|
||||
(example: `KernAux_FooBar`).
|
||||
* Name public (*defined in the headers*) with the prefix `KernAux_` and with the
|
||||
prefix of the surrounding type, separate type names with underscore (example:
|
||||
`KernAux_FooBar_CarCdr`).
|
||||
* Name public (*defined in the headers*) methods with the prefix `KernAux_` and
|
||||
with the prefix of the type name (example: `KernAux_FooBar_car_cdr`).
|
||||
|
||||
* Use postfix `size` for a byte size.
|
||||
* Use postfix `slen` for C string length without terminating `\0` character
|
||||
(size - 1).
|
||||
* Use postfix `count` for a number of elements in an array.
|
||||
|
||||
* Create `typedef`s with the names of related `struct`s. Use this name with a
|
||||
prefix `struct` to declare the data itself, withoth the prefix to declare
|
||||
a pointer or an array:
|
||||
|
||||
```c
|
||||
typedef struct FooBar { int car; } *FooBar;
|
||||
|
||||
static struct FooBar FooBar_create();
|
||||
static void FooBar FooBar_init(FooBar foobar);
|
||||
|
||||
static void FooBar_do_something(FooBar foobar);
|
||||
|
||||
// Initialize:
|
||||
struct FooBar foobar = FooBar_create();
|
||||
// or
|
||||
struct FooBar foobar;
|
||||
FooBar_init(&foobar);
|
||||
|
||||
// Use:
|
||||
FooBar foobar_ptr = &foobar;
|
||||
FooBar_do_something(&foobar);
|
||||
```
|
||||
|
||||
```c
|
||||
typedef struct FooBar { int car; } FooBar[1];
|
||||
|
||||
static struct FooBar FooBar_create();
|
||||
static void FooBar FooBar_init(FooBar foobar);
|
||||
|
||||
static void FooBar_do_something(FooBar foobar);
|
||||
|
||||
// Initialize:
|
||||
FooBar foobar = { FooBar_create() };
|
||||
// or
|
||||
FooBar foobar;
|
||||
FooBar_init(foobar);
|
||||
|
||||
// Use:
|
||||
FooBar_do_something(foobar);
|
||||
```
|
||||
|
||||
* Mark variables and parameters with `const` if you don't plan to modify them
|
||||
* Only omit braces (curly brackets) of a block if it's statement is placed on
|
||||
the same line as conditional statement:
|
||||
|
||||
```c
|
||||
// Good:
|
||||
if (foo) return bar;
|
||||
if (foo) {
|
||||
return bar;
|
||||
}
|
||||
|
||||
// Bad:
|
||||
if (foo)
|
||||
return bar;
|
||||
```
|
||||
|
||||
|
||||
|
||||
Python
|
||||
------
|
||||
|
||||
Nothing here yet.
|
||||
|
||||
|
||||
|
||||
Ruby
|
||||
----
|
||||
|
||||
* Freeze objects if you don't plan to modify them
|
||||
|
||||
### Matz's Ruby interpreter
|
||||
|
||||
Use **RuboCop**. See [bindings/ruby/.rubocop.yml](/bindings/ruby/.rubocop.yml)
|
||||
|
||||
### mruby
|
||||
|
||||
Use **RuboCop**. See [bindings/mruby/.rubocop.yml](/bindings/mruby/.rubocop.yml)
|
||||
|
||||
|
||||
|
||||
Rust
|
||||
----
|
||||
|
||||
Use **rustfmt** and **Clippy**.
|
||||
See [bindings/rust/rustfmt.toml](/bindings/rust/rustfmt.toml)
|
|
@ -0,0 +1,27 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 Alex Kotov
|
||||
|
||||
Copyright (c) 2011 Nicholas J. Kain
|
||||
Copyright (c) 2011-2015 Rich Felker
|
||||
Copyright (c) 2014-2019 Marco Paland
|
||||
Copyright (c) 2017-2022 Embedded Artistry LLC
|
||||
Copyright (c) 2022 Alexander Monakov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,3 @@
|
|||
2022-12-23 Alex Kotov <kotovalexarian@gmail.com>
|
||||
|
||||
libkernaux 0.7.0 released
|
|
@ -0,0 +1,125 @@
|
|||
include $(top_srcdir)/make/shared.am
|
||||
include $(top_srcdir)/make/checks.am
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
EXTRA_DIST = autogen.sh CONTRIBUTING.md sha256sums.txt src/assert.h
|
||||
|
||||
SUBDIRS = include
|
||||
|
||||
if WITH_LIBC
|
||||
# FIXME: after "make clean" libc is not rebuiling
|
||||
SUBDIRS += libc
|
||||
endif
|
||||
|
||||
SUBDIRS += .
|
||||
|
||||
if ENABLE_FIXTURES
|
||||
SUBDIRS += fixtures
|
||||
endif
|
||||
|
||||
if ENABLE_CHECKS
|
||||
SUBDIRS += examples tests
|
||||
endif
|
||||
|
||||
libc/libc.la:
|
||||
$(MAKE) $(AM_MAKEFLAGS) -C $(top_builddir)/libc libc.la
|
||||
|
||||
AM_CFLAGS += -DKERNAUX_ACCESS_PRIVATE
|
||||
|
||||
lib_LTLIBRARIES = libkernaux.la
|
||||
|
||||
if ENABLE_PKG_CONFIG
|
||||
pkgconfigdir = @pkgconfdir@
|
||||
pkgconfig_DATA = libkernaux.pc
|
||||
endif
|
||||
|
||||
##################
|
||||
# Required files #
|
||||
##################
|
||||
|
||||
libkernaux_la_LDFLAGS = -version-info @PACKAGE_VERSION_SO@
|
||||
libkernaux_la_LIBADD =
|
||||
libkernaux_la_SOURCES = \
|
||||
src/generic/display.c \
|
||||
src/generic/malloc.c \
|
||||
src/generic/mutex.c \
|
||||
src/runtime.c
|
||||
|
||||
########
|
||||
# libc #
|
||||
########
|
||||
|
||||
if WITH_LIBC
|
||||
libkernaux_la_LIBADD += libc/libc.la
|
||||
endif
|
||||
|
||||
#######
|
||||
# ARCH #
|
||||
#######
|
||||
|
||||
if WITH_ARCH_I386
|
||||
libkernaux_la_SOURCES += src/arch/i386/idt.c
|
||||
endif
|
||||
|
||||
#######
|
||||
# ASM #
|
||||
#######
|
||||
|
||||
if WITH_ASM
|
||||
if ASM_I386
|
||||
libkernaux_la_SOURCES += src/asm/i386.S
|
||||
endif
|
||||
if ASM_RISCV64
|
||||
libkernaux_la_SOURCES += src/asm/riscv64.S
|
||||
endif
|
||||
if ASM_X86_64
|
||||
libkernaux_la_SOURCES += src/asm/x86_64.S
|
||||
endif
|
||||
endif
|
||||
|
||||
####################
|
||||
# Default packages #
|
||||
####################
|
||||
|
||||
if WITH_CMDLINE
|
||||
libkernaux_la_SOURCES += src/cmdline.c
|
||||
endif
|
||||
if WITH_ELF
|
||||
libkernaux_la_SOURCES += src/elf.c
|
||||
endif
|
||||
if WITH_FREE_LIST
|
||||
libkernaux_la_SOURCES += src/free_list.c
|
||||
endif
|
||||
if WITH_MBR
|
||||
libkernaux_la_SOURCES += src/mbr.c
|
||||
endif
|
||||
if WITH_MEMMAP
|
||||
libkernaux_la_SOURCES += src/memmap.c
|
||||
endif
|
||||
if WITH_MULTIBOOT2
|
||||
libkernaux_la_SOURCES += \
|
||||
src/multiboot2/header_enums.c \
|
||||
src/multiboot2/header_helpers.c \
|
||||
src/multiboot2/header_is_valid.c \
|
||||
src/multiboot2/header_print.c \
|
||||
src/multiboot2/info_convert.c \
|
||||
src/multiboot2/info_enums.c \
|
||||
src/multiboot2/info_helpers.c \
|
||||
src/multiboot2/info_is_valid.c \
|
||||
src/multiboot2/info_print.c
|
||||
endif
|
||||
if WITH_NTOA
|
||||
libkernaux_la_SOURCES += src/ntoa.c
|
||||
endif
|
||||
if WITH_PFA
|
||||
libkernaux_la_SOURCES += src/pfa.c
|
||||
endif
|
||||
if WITH_PRINTF
|
||||
libkernaux_la_SOURCES += src/printf.c
|
||||
endif
|
||||
if WITH_PRINTF_FMT
|
||||
libkernaux_la_SOURCES += src/printf_fmt.c
|
||||
endif
|
||||
if WITH_UNITS
|
||||
libkernaux_la_SOURCES += src/units.c
|
||||
endif
|
|
@ -0,0 +1,204 @@
|
|||
Releases
|
||||
========
|
||||
|
||||
|
||||
|
||||
0.7.0 (2022-12-23)
|
||||
------------------
|
||||
|
||||
169 files changed, 6332 insertions(+), 3858 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `./configure` - feature `--(enable|disable)-debug` has been removed.
|
||||
* `<kernaux/*>` - definition `KERNAUX_DEBUG` has been removed.
|
||||
* `<kernaux/assert.h>` - it has been removed.
|
||||
* `<kernaux/memmap.h>` - it has been completely rewritten.
|
||||
|
||||
### New features
|
||||
|
||||
* `./configure` - feature `--(enable|disable)-fixtures` has been added.
|
||||
* `./configure` - feature `--(enable|disable)-pkg-config[=PATH` has been added.
|
||||
It allows you to specify where to install **pkg-config** files.
|
||||
* `./configure` - package `--with[out]-multiboot2` has been added.
|
||||
* `<kernaux/*>` - definition `KERNAUX_BITFIELDS` has been added.
|
||||
* `<kernaux/generic/display.h>` - new header.
|
||||
* `<kernaux/macro.h>` - macro `KERNAUX_STATIC_TEST` has been added.
|
||||
* `<kernaux/macro.h>` - macros `KERNAUX_CAST_(VAR|CONST)` have been added.
|
||||
* `<kernaux/multiboot2.h>` - new header.
|
||||
* `<kernaux/runtime.h>` - new header. Now it contains the variable
|
||||
`kernaux_assert_cb`.
|
||||
|
||||
### Other changes
|
||||
|
||||
* `./configure` - a shared library is built by default.
|
||||
* `<kernaux/*>` - the headers are the same regardless of the selection
|
||||
of features and packages (except `<kernaux/version.h>`).
|
||||
* `examples/Makefile` - the test suite can be built now without **pthreads**.
|
||||
* `tests/Makefile` - the ability to run tests from with any working directory
|
||||
has been added.
|
||||
* `tests/Makefile` - the test suite has been fixed to run on FreeBSD.
|
||||
|
||||
|
||||
|
||||
0.6.1 (2022-12-05)
|
||||
------------------
|
||||
|
||||
12 files changed, 111 insertions(+), 110 deletions(-)
|
||||
|
||||
### Other changes
|
||||
|
||||
* `./configure` - fix a bug with building for a freestanding environment
|
||||
* `Makefile` - fix cppcheck in out-of-root builds
|
||||
* `Makefile` - fix Python tests if out-of-root builds
|
||||
|
||||
|
||||
|
||||
0.6.0 (2022-12-04)
|
||||
------------------
|
||||
|
||||
121 files changed, 1444 insertions(+), 1806 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `./configure` - features `--enable-tests*` have been renamed
|
||||
to `--enable-checks*`.
|
||||
* `<kernaux/macro.h>` - macro `KERNAUX_PACKING_ATTR` has been renamed
|
||||
to `KERNAUX_PACKED`.
|
||||
|
||||
### New features
|
||||
|
||||
* `./configure` - feature `--enable-checks-cppcheck` has been added.
|
||||
* `Makefile` - task `check-cppcheck` has been added.
|
||||
* `<kernaux/macro.h>` - macros `KERNAUX_ALIGNED`, `KERNAUX_ASM`,
|
||||
`KERNAUX_NORETURN`, `KERNAUX_PRINTF`, `KERNAUX_RETURNS_TWICE`,
|
||||
`KERNAUX_SECTION`, `KERNAUX_UNUSED`, `KERNAUX_USED` have been added.
|
||||
* `<kernaux/printf_fmt.h>` - new header. It's printf format parser.
|
||||
|
||||
### Other changes
|
||||
|
||||
* `<kernaux/free_list.h>` - fix a bug that was making the allocator unusable.
|
||||
* `<kernaux/printf.h>` - fix displaying floating-point numbers with big
|
||||
precision.
|
||||
|
||||
|
||||
|
||||
0.5.0 (2022-11-26)
|
||||
------------------
|
||||
|
||||
138 files changed, 2962 insertions(+), 1741 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `./configure` - package `--with[out]-io` has been removed.
|
||||
* `<kernaux/io.h>` - the header has been removed.
|
||||
* `<kernaux/printf.h>` - the API of the functions functions `[v]fprintf` has
|
||||
been changed.
|
||||
|
||||
### New features
|
||||
|
||||
* `./configure` - package `--with[out]-asm` has been added.
|
||||
* `./configure` - packages `--with[out]-arch-(all|i386|riscv64|x86-64)` have
|
||||
been added.
|
||||
* `./configure` - package `--with[out]-drivers` has been added.
|
||||
* `./configure` - package `--with[out]-free-list` has been added.
|
||||
* `<kernaux/free_list.h>` - new header. It's a free list memory allocator.
|
||||
* `<kernaux/macro.h>` - new header. It's a set of macros.
|
||||
* `<kernaux/macro.h>` - macro `KERNAUX_PRIVATE_FIELD` has been added.
|
||||
* `<kernaux/macro.h>` - macro `KERNAUX_PROTECTED_FIELD` has been added.
|
||||
* `<kernaux/macro.h>` - macros `KERNAUX_BITS[8|16|32|64]` have been added.
|
||||
* `<kernaux/macro.h>` - macros `KERNAUX_CONTAINER_OF` have been added.
|
||||
* `<kernaux/generic/malloc.h>` - new header. It's a generic memory allocator
|
||||
interface.
|
||||
* `<kernaux/generic/mutex.h>` - new header. It's a generic mutex interface.
|
||||
* Definition `KERNAUX_ACCESS_PRIVATE` has been added.
|
||||
* Definition `KERNAUX_ACCESS_PROTECTED` has been added.
|
||||
|
||||
|
||||
|
||||
0.4.0 (2022-06-16)
|
||||
------------------
|
||||
|
||||
225 files changed, 8625 insertions(+), 3744 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `./configure` - feature `--(enable|disable)-bloat` has been removed.
|
||||
* `./configure` - feature `--(enable|disable)-pic` has been removed.
|
||||
* `<kernaux/assert.h>` - API has been completely changed.
|
||||
* `<kernaux/libc.h>` - header has been removed.
|
||||
* `<kernaux/ntoa.h>` - API has been completely changed.
|
||||
* `<kernaux/printf.h>` - Easter egg has been removed.
|
||||
* `<kernaux/printf.h>` - functions `[v]printf` have been renamed to `[v]fprintf`,
|
||||
API has been changed.
|
||||
|
||||
### New features
|
||||
|
||||
* `./configure` - new feature `--(enable|disable)-debug`.
|
||||
* `./configure` - new package `--with[out]-io`.
|
||||
* `./configure` - new package `--with[out]-memmap`.
|
||||
* `<kernaux/memmap.h>` - new header. It's a memory map.
|
||||
|
||||
|
||||
|
||||
0.3.0 (2022-05-28)
|
||||
------------------
|
||||
|
||||
134 files changed, 6149 insertions(+), 1770 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `<kernaux/assert.h>` - a semicolon is required after macros.
|
||||
* `<kernaux/assert.h>` - the constant `KERNAUX_ITOA_BUFFER_SIZE` is renamed to
|
||||
`KERNAUX_ITOA10_BUFFER_SIZE`.
|
||||
|
||||
### New features
|
||||
|
||||
* `./configure` - testing may be enabled with `--enable-tests`,
|
||||
`--enable-tests-all`, `--enable-tests-python`.
|
||||
* `./configure` - the heavy binary data may be disabled with `--disable-bloat`.
|
||||
* `./configure` - position-independent code is generated by default, disable
|
||||
with `--disable-pic`.
|
||||
* `./configure` - compiler warnings turn into errors by default, disable with
|
||||
`--disable-werror`.
|
||||
* `<kernaux/libc.h>` - added functions `atoi`, `isdigit`, `isspace`.
|
||||
* `<kernaux/ntoa.h>` - added functions `kernaux_[u|i]toa`, `kernaux_[u|i]toa16`
|
||||
and related constants.
|
||||
|
||||
### Other changes
|
||||
|
||||
* Assertions are used more broadly.
|
||||
* `<kernaux.h>` - the header is now generated from template depending on
|
||||
configuration options, so you may safely include it when some packages are
|
||||
missing, it won't cause compilation error.
|
||||
* `<kernaux/printf.h>` - a potential buffer overflow is fixed.
|
||||
|
||||
|
||||
|
||||
0.2.0 (2022-01-22)
|
||||
------------------
|
||||
|
||||
51 files changed, 1647 insertions(+), 501 deletions(-)
|
||||
|
||||
### Breaking changes
|
||||
|
||||
* `./configure` - removed options to disable panic calls and returns in
|
||||
`<kernaux/assert.h>`.
|
||||
* `<kernaux/assert.h>` - removed preprocessor directives that can disable panic
|
||||
calls and returns.
|
||||
|
||||
### New features
|
||||
|
||||
* `<kernaux/cmdline.h>` is stable now. It's a simple command line parser.
|
||||
|
||||
### Other changes
|
||||
|
||||
* Now `./configure` options also follow semantic versioning.
|
||||
* Bug fixes in `<kernaux/printf.h>`.
|
||||
|
||||
|
||||
|
||||
0.1.0 (2022-01-20)
|
||||
------------------
|
||||
|
||||
Initial release.
|
|
@ -0,0 +1,264 @@
|
|||
libkernaux
|
||||
==========
|
||||
|
||||
[![Build status](https://github.com/tailix/libkernaux/actions/workflows/main.yml/badge.svg)](https://github.com/tailix/libkernaux/actions/workflows/main.yml)
|
||||
[![Build status (FreeBSD)](https://api.cirrus-ci.com/github/tailix/libkernaux.svg?task=Main%20(FreeBSD))](https://cirrus-ci.com/github/tailix/libkernaux)
|
||||
|
||||
Auxiliary library for kernel development.
|
||||
|
||||
[Topic on OSDev.org forum](https://forum.osdev.org/viewtopic.php?f=1&t=37958).
|
||||
|
||||
|
||||
|
||||
Table of contents
|
||||
-----------------
|
||||
|
||||
* [Overview](#libkernaux)
|
||||
* [Table of contents](#table-of-contents)
|
||||
* [API](#api)
|
||||
* [Headers](#headers)
|
||||
* [Definitions](#definitions)
|
||||
* [Global variables](#global-variables)
|
||||
* [Configuration](#configuration)
|
||||
* [Non-default options](#non-default-options)
|
||||
* [Default options](#default-options)
|
||||
* [Tips](#tips)
|
||||
* [Installation](#installation)
|
||||
* [Development](#development)
|
||||
* [Cross](#cross)
|
||||
|
||||
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
### Headers
|
||||
|
||||
We use [semantic versioning](https://semver.org) for stable APIs. Stable APIs
|
||||
may only change when major version number is increased (or minor while major is
|
||||
zero). Work-in-progress APIs can change at any time.
|
||||
|
||||
* Basic features
|
||||
* [Feature macros](/include/kernaux/version.h.in) (*work in progress*)
|
||||
* [Runtime environment](/include/kernaux/runtime.h) (*non-breaking since* **0.7.0**)
|
||||
* [Macros](/include/kernaux/macro.h) (*non-breaking since* **0.6.0**)
|
||||
* [Example: packing](/examples/macro_packing.c)
|
||||
* [Example: BITS](/examples/macro_bits.c)
|
||||
* [Example: CAST\_\*](/examples/macro_cast.c);
|
||||
* [Example: CONTAINER\_OF](/examples/macro_container_of.c)
|
||||
* [Example: STATIC\_TEST\*](/examples/macro_static_test.c)
|
||||
* Stack trace *(planned)*
|
||||
* Generic types
|
||||
* [Display](/include/kernaux/generic/display.h) (*non-breaking since* **0.7.0**)
|
||||
* [Example](/examples/generic_display.c)
|
||||
* [Memory allocator](/include/kernaux/generic/malloc.h) (*non-breaking since* **0.5.0**)
|
||||
* [Example](/examples/generic_malloc.c)
|
||||
* [Mutex](/include/kernaux/generic/mutex.h) (*non-breaking since* **0.5.0**)
|
||||
* [Example](/examples/generic_mutex.c)
|
||||
* Algorithms
|
||||
* [Free list memory allocator](/include/kernaux/free_list.h) (*non-breaking since* **0.5.0**)
|
||||
* [Simple command line parser](/include/kernaux/cmdline.h) (*non-breaking since* **0.2.0**)
|
||||
* [Example](/examples/cmdline.c)
|
||||
* [Page Frame Allocator](/include/kernaux/pfa.h) (*work in progress*)
|
||||
* [Example](/examples/pfa.c)
|
||||
* Data formats
|
||||
* [ELF](/include/kernaux/elf.h) (*work in progress*)
|
||||
* [MBR](/include/kernaux/mbr.h) (*work in progress*)
|
||||
* [Multiboot 2 (GRUB 2)](/include/kernaux/multiboot2.h.in) (*non-breaking since* **0.7.0**)
|
||||
* [Example: header macros](/examples/multiboot2_header_macro.c)
|
||||
* Utilities
|
||||
* [Measurement units utils](/include/kernaux/units.h) (*work in progress*)
|
||||
* [Example: To human](/examples/units_human.c)
|
||||
* [Memory map](/include/kernaux/memmap.h) (*non-breaking since* **0.7.0**)
|
||||
* [Example](/examples/memmap.c)
|
||||
* [printf format parser](/include/kernaux/printf_fmt.h) (*non-breaking since* **0.6.0**)
|
||||
* [Example](/examples/printf_fmt.c)
|
||||
* Usual functions
|
||||
* [itoa/ftoa replacement](/include/kernaux/ntoa.h) (*non-breaking since* **0.4.0**)
|
||||
* [Example](/examples/ntoa.c)
|
||||
* [printf replacement](/include/kernaux/printf.h) (*non-breaking since* **0.5.0**)
|
||||
* [Example: fprintf](/examples/printf_file.c)
|
||||
* [Example: vfprintf](/examples/printf_file_va.c)
|
||||
* [Example: snprintf](/examples/printf_str.c)
|
||||
* [Example: vsnprintf](/examples/printf_str_va.c)
|
||||
* libc replacement (*work in progress*)
|
||||
* [ctype.h](/libc/include/ctype.h)
|
||||
* [errno.h](/libc/include/errno.h)
|
||||
* [inttypes.h](/libc/include/inttypes.h)
|
||||
* [setjmp.h](/libc/include/setjmp.h)
|
||||
* [stdlib.h](/libc/include/stdlib.h)
|
||||
* [string.h](/libc/include/string.h)
|
||||
* [sys/types.h](/libc/include/sys/types.h)
|
||||
* Architecture-specific code (*work in progress*)
|
||||
* [Declarations](/include/kernaux/arch/)
|
||||
* [Functions](/include/kernaux/asm/)
|
||||
|
||||
### Definitions
|
||||
|
||||
`#define` the following C preprocessor macros before including `<kernaux.h>` and
|
||||
`<kernaux/*.h>` files. They have effect on your code, not the library code.
|
||||
|
||||
* `KERNAUX_ACCESS_PRIVATE` - disable access modifier "private". Don't do this!
|
||||
* `KERNAUX_ACCESS_PROTECTED` - disable access modifier "protected". Only do this
|
||||
in a file where you implement an inherited type.
|
||||
* `KERNAUX_BITFIELDS` - enable bitfields in packed structs. It doesn't follow
|
||||
the C standard and may be incompatible with some compilers.
|
||||
|
||||
### Global variables
|
||||
|
||||
```c
|
||||
// in <kernaux/runtime.h>
|
||||
void (*kernaux_assert_cb)(const char *file, int line, const char *msg)
|
||||
```
|
||||
|
||||
Assertion callback. It's better to always set it to some function which always
|
||||
interrupts the execution, even when assertions are disabled. It may for example
|
||||
call `abort()` in hosted environment, raise an exception in Ruby, panic in Rust
|
||||
or power off the machine in freestanding environment. It may also log the error
|
||||
location and message.
|
||||
|
||||
|
||||
|
||||
|
||||
Configuration
|
||||
-------------
|
||||
|
||||
Because this library has no external dependencies, we use **autoconf** features
|
||||
to control behavior of the library, and packages to choose it's components.
|
||||
Configuration options also follow the [semantic versioning](https://semver.org)
|
||||
scheme and are split into stable and work-in-progress ones. Here we cover only
|
||||
stable options.
|
||||
|
||||
### Non-default options
|
||||
|
||||
#### Features
|
||||
|
||||
* `--enable-checks` - enable usual tests and examples
|
||||
* `--enable-checks-all` - enable all checks
|
||||
* `--enable-checks-cppcheck` - enable cppcheck
|
||||
* `--enable-checks-pthreads` - enable tests that require pthreads
|
||||
* `--enable-checks-python` - enable tests that require Python 3 with YAML and
|
||||
Jinja2
|
||||
* `--enable-fixtures` - enable fixtures for tests and bindings
|
||||
* `--enable-pkg-config[=PATH]` - install pkg-config files
|
||||
[PATH='${libdir}/pkgconfig']
|
||||
|
||||
#### Packages
|
||||
|
||||
* `--with-libc` - provides the replacement for some standard C functions.
|
||||
Useful in freestanding environment, where no libc is present.
|
||||
|
||||
### Default options
|
||||
|
||||
#### Features
|
||||
|
||||
* `--(enable|disable)-assert` - assertions
|
||||
* `--(enable|disable)-float` - floating-point arithmetic
|
||||
* `--(enable|disable)-werror` - fail on warning (`CFLAGS+='-Werror'`)
|
||||
|
||||
#### Packages
|
||||
|
||||
All packages are included by default. To exclude all packages except those
|
||||
explicitly included, use `--without-all`.
|
||||
|
||||
* `--with[out]-arch-all` - all architectures
|
||||
* `--with[out]-arch-i386` - architecture i386
|
||||
* `--with[out]-arch-riscv64` - architecture riscv64
|
||||
* `--with[out]-arch-x86-64` - architecture x86-64
|
||||
* `--with[out]-asm` - kernel assembler helpers
|
||||
* `--with[out]-cmdline` - command line parser
|
||||
* `--with[out]-free-list` - free list memory allocator
|
||||
* `--with[out]-memmap` - memory map
|
||||
* `--with[out]-multiboot2` - Multiboot 2 utils
|
||||
* `--with[out]-ntoa` - itoa/ftoa
|
||||
* `--with[out]-printf` - printf
|
||||
|
||||
|
||||
|
||||
Tips
|
||||
----
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
./autogen.sh # if present
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
sudo ldconfig # on GNU/Linux
|
||||
```
|
||||
|
||||
This is just a usual library. You can use most of it's APIs in hosted
|
||||
environment.
|
||||
|
||||
### Development
|
||||
|
||||
```
|
||||
./autogen.sh # if present
|
||||
./configure --enable-fixtures --enable-checks # or --enable-checks-all, but see prerequisites
|
||||
make
|
||||
```
|
||||
|
||||
You can test with `make check`.
|
||||
|
||||
#### See also
|
||||
|
||||
* [GitHub Actions](/.github/) for **GNU/Linux** build environment
|
||||
* [Cirrus CI](/.cirrus.yml) for **FreeBSD** build environment
|
||||
* [sourcehut CI](/.openbsd.yml) for **OpenBSD** build environment
|
||||
|
||||
### Cross
|
||||
|
||||
Create configuration script with `./autogen.sh` (if present).
|
||||
|
||||
Let's assume that your target triplet is `i386-elf`. Configure with
|
||||
[cross-compiler](https://wiki.osdev.org/GCC_Cross-Compiler) in `$PATH` to make
|
||||
without it in `$PATH`:
|
||||
|
||||
```
|
||||
./configure \
|
||||
--host='i386-elf' \
|
||||
--disable-shared \
|
||||
--enable-freestanding \
|
||||
--with-libc \
|
||||
CC="$(which i386-elf-gcc)"
|
||||
```
|
||||
|
||||
The variables include `AR`, `AS`, `CC`, `CCAS`, `LD`, `NM`, `OBJDUMP`, `RANLIB`,
|
||||
`STRIP`. See the generated `config.log` for more information.
|
||||
|
||||
To install into specific directory use full path: `DESTDIR="$(pwd)/dest" make
|
||||
install` instead of `DESTDIR=dest make install`.
|
||||
|
||||
Check if compilation targets i386: `objdump -d src/asm/i386.o`. It should output
|
||||
something like this:
|
||||
|
||||
```
|
||||
src/asm/i386.o: file format elf32-i386
|
||||
|
||||
|
||||
Disassembly of section .text:
|
||||
|
||||
00000000 <kernaux_asm_i386_read_cr0>:
|
||||
0: 0f 20 c0 mov %cr0,%eax
|
||||
3: c3 ret
|
||||
|
||||
00000004 <kernaux_asm_i386_read_cr4>:
|
||||
4: 0f 20 e0 mov %cr4,%eax
|
||||
7: c3 ret
|
||||
|
||||
00000008 <kernaux_asm_i386_write_cr0>:
|
||||
8: 8b 44 24 04 mov 0x4(%esp),%eax
|
||||
c: 0f 22 c0 mov %eax,%cr0
|
||||
f: c3 ret
|
||||
|
||||
00000010 <kernaux_asm_i386_write_cr3>:
|
||||
10: 8b 44 24 04 mov 0x4(%esp),%eax
|
||||
14: 0f 22 d8 mov %eax,%cr3
|
||||
17: c3 ret
|
||||
|
||||
00000018 <kernaux_asm_i386_write_cr4>:
|
||||
18: 8b 44 24 04 mov 0x4(%esp),%eax
|
||||
1c: 0f 22 e0 mov %eax,%cr4
|
||||
1f: c3 ret
|
||||
```
|
|
@ -0,0 +1 @@
|
|||
0:0:0
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
exec autoreconf -isf -Wall
|
|
@ -0,0 +1,7 @@
|
|||
/build_config.rb.lock
|
||||
|
||||
# For a library or gem, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in.
|
||||
/Gemfile.lock
|
||||
/.ruby-version
|
||||
/.ruby-gemset
|
|
@ -0,0 +1,81 @@
|
|||
require:
|
||||
- rubocop-performance
|
||||
- rubocop-rake
|
||||
|
||||
AllCops:
|
||||
# TODO: Explore which exact syntax version mruby is compatible with
|
||||
TargetRubyVersion: 2.7
|
||||
DisplayCopNames: true
|
||||
NewCops: enable
|
||||
|
||||
Layout/AccessModifierIndentation:
|
||||
EnforcedStyle: outdent
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 80
|
||||
|
||||
Lint/AmbiguousOperatorPrecedence:
|
||||
Enabled: false
|
||||
|
||||
Lint/ReturnInVoidContext:
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
- 'Rakefile'
|
||||
- 'test/**/*.rb'
|
||||
|
||||
Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'test/sprintf.rb'
|
||||
|
||||
Performance/CollectionLiteralInLoop:
|
||||
Exclude:
|
||||
- 'test/**/*.rb'
|
||||
|
||||
Security/Eval:
|
||||
Exclude:
|
||||
- 'test/**/*.rb'
|
||||
|
||||
Security/YAMLLoad:
|
||||
Exclude:
|
||||
- 'test/**/*.rb'
|
||||
|
||||
Style/AndOr:
|
||||
EnforcedStyle: conditionals
|
||||
|
||||
Style/Documentation:
|
||||
Exclude:
|
||||
- 'Rakefile'
|
||||
|
||||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
|
||||
Style/ExpandPathArguments:
|
||||
# __dir__ is not available in mruby
|
||||
Enabled: false
|
||||
|
||||
Style/FrozenStringLiteralComment:
|
||||
Enabled: false
|
||||
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
Style/NumericPredicate:
|
||||
# Numeric#negative? is not available in mruby
|
||||
Enabled: false
|
||||
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
Style/TrailingCommaInArguments:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/VariableInterpolation:
|
||||
Enabled: false
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'bundler', '~> 2.2'
|
||||
gem 'rake', '~> 13.0'
|
||||
gem 'rubocop', '~> 1.25'
|
||||
gem 'rubocop-performance', '~> 1.13'
|
||||
gem 'rubocop-rake', '~> 0.6'
|
|
@ -0,0 +1,26 @@
|
|||
::KernAux
|
||||
=========
|
||||
|
||||
[![Build status](https://github.com/tailix/libkernaux/actions/workflows/mruby.yml/badge.svg)](https://github.com/tailix/libkernaux/actions/workflows/mruby.yml)
|
||||
[![Build status (FreeBSD)](https://api.cirrus-ci.com/github/tailix/libkernaux.svg?task=mruby%20(FreeBSD))](https://cirrus-ci.com/github/tailix/libkernaux)
|
||||
|
||||
Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
|
||||
library for kernel development.
|
||||
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Add the following to your `build_config.rb`:
|
||||
|
||||
```ruby
|
||||
MRuby::Build.new do |conf|
|
||||
# ...
|
||||
conf.gem github: 'tailix/libkernaux',
|
||||
path: 'bindings/mruby',
|
||||
branch: 'v0.7.0',
|
||||
checksum_hash: '1cfff3fca3fc3f8990f43f0d6d5c91929c8f359c'
|
||||
# ...
|
||||
end
|
||||
```
|
|
@ -0,0 +1,29 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
desc 'Run default checks'
|
||||
task default: :lint
|
||||
|
||||
desc 'Run code analysis tools'
|
||||
task lint: %i[rubocop cppcheck]
|
||||
|
||||
desc 'Fix code style (rubocop --auto-correct)'
|
||||
task fix: 'rubocop:auto_correct'
|
||||
|
||||
begin
|
||||
require 'rubocop/rake_task'
|
||||
RuboCop::RakeTask.new
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
desc 'Run cppcheck'
|
||||
task :cppcheck do
|
||||
sh(
|
||||
'cppcheck',
|
||||
'--quiet',
|
||||
'--error-exitcode=1',
|
||||
'--std=c99',
|
||||
'--enable=warning,style,performance,portability',
|
||||
__dir__,
|
||||
)
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
bundle install
|
||||
|
||||
# Do any other automated setup that you need to do here
|
|
@ -0,0 +1,6 @@
|
|||
MRuby::Build.new do |conf|
|
||||
conf.toolchain
|
||||
conf.enable_test
|
||||
conf.gem '.'
|
||||
conf.gem core: 'mruby-bin-mirb'
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
MRuby::Gem::Specification.new 'mruby-kernaux' do |spec|
|
||||
spec.version = '0.7.0'
|
||||
spec.license = 'MIT'
|
||||
spec.homepage =
|
||||
'https://github.com/tailix/libkernaux/tree/master/bindings/ruby'
|
||||
spec.author = 'Alex Kotov'
|
||||
|
||||
spec.summary =
|
||||
'Binding to libkernaux - auxiliary library for kernel development'
|
||||
|
||||
spec.description = <<~DESCRIPTION.split("\n").map(&:strip).join ' '
|
||||
Binding to libkernaux - auxiliary library for kernel development.
|
||||
DESCRIPTION
|
||||
|
||||
spec.add_test_dependency 'mruby-eval'
|
||||
spec.add_test_dependency 'mruby-io'
|
||||
spec.add_test_dependency 'mruby-random'
|
||||
spec.add_test_dependency 'mruby-yaml'
|
||||
|
||||
spec.linker.libraries << 'kernaux'
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
##
|
||||
# Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
|
||||
# library for kernel development.
|
||||
#
|
||||
module KernAux
|
||||
DEFAULT_ASSERT_CB = @assert_cb = lambda { |file, line, msg|
|
||||
raise AssertError, "#{file}:#{line}:#{msg}"
|
||||
}
|
||||
|
||||
class Error < RuntimeError; end
|
||||
class AssertError < Error; end
|
||||
class CmdlineError < Error; end
|
||||
class InvalidNtoaBaseError < Error; end
|
||||
class TooLongNtoaPrefixError < Error; end
|
||||
end
|
|
@ -0,0 +1,74 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/string.h>
|
||||
#include <mruby/variable.h>
|
||||
|
||||
static void assert_cb(const char *file, int line, const char *msg);
|
||||
|
||||
static mrb_value rb_KernAux_assert_cb(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_assert_cb_EQ(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_assert_do(mrb_state *mrb, mrb_value self);
|
||||
|
||||
void init_assert(mrb_state *const mrb)
|
||||
{
|
||||
kernaux_assert_cb = assert_cb;
|
||||
|
||||
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "assert_cb",
|
||||
rb_KernAux_assert_cb, MRB_ARGS_NONE());
|
||||
mrb_define_class_method(mrb, rb_KernAux, "assert_cb=",
|
||||
rb_KernAux_assert_cb_EQ, MRB_ARGS_REQ(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "assert_do",
|
||||
rb_KernAux_assert_do, MRB_ARGS_REQ(3));
|
||||
}
|
||||
|
||||
void assert_cb(const char *const file, const int line, const char *const msg)
|
||||
{
|
||||
mrb_state *const mrb = current_mrb_get();
|
||||
if (!mrb) return;
|
||||
|
||||
struct RClass *const self_rbc = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
mrb_value self_rb = mrb_obj_value(self_rbc);
|
||||
|
||||
mrb_value assert_cb_rb = mrb_iv_get(mrb, self_rb, MRB_IVSYM(assert_cb));
|
||||
if (mrb_nil_p(assert_cb_rb)) return;
|
||||
|
||||
mrb_value file_rb = mrb_str_new_lit(mrb, "");
|
||||
file_rb = mrb_str_cat_cstr(mrb, file_rb, file);
|
||||
|
||||
mrb_value line_rb = mrb_fixnum_value(line);
|
||||
|
||||
mrb_value msg_rb = mrb_str_new_lit(mrb, "");
|
||||
msg_rb = mrb_str_cat_cstr(mrb, msg_rb, msg);
|
||||
|
||||
mrb_funcall_id(
|
||||
mrb, assert_cb_rb, MRB_SYM(call), 3, file_rb, line_rb, msg_rb);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_assert_cb(mrb_state *const mrb, const mrb_value self_rb)
|
||||
{
|
||||
return mrb_iv_get(mrb, self_rb, MRB_IVSYM(assert_cb));
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_assert_cb_EQ(mrb_state *const mrb, const mrb_value self_rb)
|
||||
{
|
||||
mrb_value assert_cb_rb = mrb_nil_value();
|
||||
mrb_get_args(mrb, "o", &assert_cb_rb);
|
||||
mrb_iv_set(mrb, self_rb, MRB_IVSYM(assert_cb), assert_cb_rb);
|
||||
return assert_cb_rb;
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_assert_do(mrb_state *const mrb, const mrb_value self_rb)
|
||||
{
|
||||
const char *file = NULL;
|
||||
mrb_int line_rb = 0;
|
||||
const char *msg = NULL;
|
||||
mrb_get_args(mrb, "ziz", &file, &line_rb, &msg);
|
||||
|
||||
current_mrb_start(mrb);
|
||||
kernaux_assert_do(file, 0, msg);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
return mrb_nil_value();
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/string.h>
|
||||
|
||||
#define ARG_COUNT_MAX 256
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
|
||||
static mrb_value rb_KernAux_cmdline(mrb_state *mrb, mrb_value self);
|
||||
|
||||
void init_cmdline(mrb_state *const mrb)
|
||||
{
|
||||
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_Error =
|
||||
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(Error));
|
||||
|
||||
mrb_define_class_under_id(mrb, rb_KernAux, MRB_SYM(CmdlineError),
|
||||
rb_KernAux_Error);
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "cmdline",
|
||||
rb_KernAux_cmdline, MRB_ARGS_REQ(1));
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_cmdline(mrb_state *const mrb, mrb_value self)
|
||||
{
|
||||
const char *str;
|
||||
mrb_get_args(mrb, "z", &str);
|
||||
size_t argc;
|
||||
char error_msg[KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX];
|
||||
char **const argv = malloc(sizeof(char*) * ARG_COUNT_MAX);
|
||||
char *const buffer = malloc(BUFFER_SIZE);
|
||||
|
||||
const bool status = kernaux_cmdline(
|
||||
str,
|
||||
error_msg,
|
||||
&argc,
|
||||
argv,
|
||||
buffer,
|
||||
ARG_COUNT_MAX,
|
||||
BUFFER_SIZE
|
||||
);
|
||||
|
||||
if (!status) {
|
||||
free(argv);
|
||||
free(buffer);
|
||||
struct RClass *const rb_KernAux =
|
||||
mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_CmdlineError =
|
||||
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(CmdlineError));
|
||||
mrb_raise(mrb, rb_KernAux_CmdlineError, error_msg);
|
||||
}
|
||||
|
||||
mrb_value values[argc];
|
||||
for (size_t index = 0; index < argc; ++index) {
|
||||
values[index] = mrb_obj_freeze(
|
||||
mrb,
|
||||
mrb_str_cat_cstr(mrb, mrb_str_new_lit(mrb, ""), argv[index])
|
||||
);
|
||||
}
|
||||
free(argv);
|
||||
free(buffer);
|
||||
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, argc, values));
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
|
@ -0,0 +1,45 @@
|
|||
#include "dynarg.h"
|
||||
|
||||
struct DynArg DynArg_create()
|
||||
{
|
||||
struct DynArg dynarg;
|
||||
DynArg_init(&dynarg);
|
||||
return dynarg;
|
||||
}
|
||||
|
||||
void DynArg_init(struct DynArg *const dynarg)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->dbl = 0.0;
|
||||
dynarg->arg.str = "";
|
||||
}
|
||||
|
||||
void DynArg_use_char(struct DynArg *const dynarg, const char chr)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.chr = chr;
|
||||
}
|
||||
|
||||
void DynArg_use_double(struct DynArg *const dynarg, const double dbl)
|
||||
{
|
||||
dynarg->use_dbl = true;
|
||||
dynarg->dbl = dbl;
|
||||
}
|
||||
|
||||
void DynArg_use_long_long(struct DynArg *const dynarg, const long long ll)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.ll = ll;
|
||||
}
|
||||
|
||||
void DynArg_use_str(struct DynArg *const dynarg, const char *const str)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.str = str;
|
||||
}
|
||||
|
||||
void DynArg_use_unsigned_long_long(struct DynArg *const dynarg, const unsigned long long ull)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.ull = ull;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef INCLUDED_DYNARG
|
||||
#define INCLUDED_DYNARG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <kernaux/macro.h>
|
||||
|
||||
#include <kernaux/macro/packing_start.run>
|
||||
|
||||
struct DynArg {
|
||||
bool use_dbl;
|
||||
double dbl;
|
||||
// TODO: check if this will work on different endianness.
|
||||
union {
|
||||
char chr;
|
||||
long long ll;
|
||||
const char *str;
|
||||
unsigned long long ull;
|
||||
} KERNAUX_PACKED arg;
|
||||
}
|
||||
KERNAUX_PACKED;
|
||||
|
||||
#include <kernaux/macro/packing_end.run>
|
||||
|
||||
struct DynArg DynArg_create();
|
||||
void DynArg_init(struct DynArg *dynarg);
|
||||
|
||||
void DynArg_use_char(struct DynArg *dynarg, char chr);
|
||||
void DynArg_use_double(struct DynArg *dynarg, double dbl);
|
||||
void DynArg_use_long_long(struct DynArg *dynarg, long long ll);
|
||||
void DynArg_use_str(struct DynArg *dynarg, const char *str);
|
||||
void DynArg_use_unsigned_long_long(struct DynArg *dynarg, unsigned long long ull);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <mruby/presym.h>
|
||||
|
||||
#define MRB_STACK_SIZE 100
|
||||
|
||||
// TODO: implement locking
|
||||
// TODO: replace assertions with actual abort
|
||||
static size_t mrb_stack_count = 0;
|
||||
static mrb_state *mrb_stack[MRB_STACK_SIZE];
|
||||
|
||||
void mrb_mruby_kernaux_gem_final(mrb_state *const mrb) {}
|
||||
|
||||
void mrb_mruby_kernaux_gem_init(mrb_state *const mrb)
|
||||
{
|
||||
for (size_t index = mrb_stack_count; index < MRB_STACK_SIZE; ++index) {
|
||||
mrb_stack[index] = NULL;
|
||||
}
|
||||
|
||||
struct RClass *const rb_KernAux =
|
||||
mrb_define_module_id(mrb, MRB_SYM(KernAux));
|
||||
mrb_define_class_under_id(mrb, rb_KernAux, MRB_SYM(Error), E_RUNTIME_ERROR);
|
||||
|
||||
init_assert(mrb);
|
||||
init_version(mrb);
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
init_cmdline(mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
init_ntoa(mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
init_printf(mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
}
|
||||
|
||||
void current_mrb_start(mrb_state *const mrb)
|
||||
{
|
||||
mrb_assert(mrb_stack_count < MRB_STACK_SIZE - 1);
|
||||
mrb_assert(mrb_stack[mrb_stack_count] == NULL);
|
||||
mrb_assert(mrb != NULL);
|
||||
|
||||
mrb_stack[mrb_stack_count++] = mrb;
|
||||
}
|
||||
|
||||
void current_mrb_finish(mrb_state *const mrb)
|
||||
{
|
||||
mrb_assert(mrb_stack_count > 0);
|
||||
mrb_assert(mrb_stack[mrb_stack_count - 1] != NULL);
|
||||
mrb_assert(mrb_stack[mrb_stack_count - 1] == mrb);
|
||||
|
||||
mrb_stack[--mrb_stack_count] = NULL;
|
||||
}
|
||||
|
||||
mrb_state *current_mrb_get()
|
||||
{
|
||||
mrb_assert(mrb_stack_count > 0);
|
||||
mrb_assert(mrb_stack[mrb_stack_count - 1] != NULL);
|
||||
|
||||
return mrb_stack[mrb_stack_count - 1];
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <kernaux.h>
|
||||
#include <mruby.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void current_mrb_start(mrb_state *mrb);
|
||||
void current_mrb_finish(mrb_state *mrb);
|
||||
mrb_state *current_mrb_get();
|
||||
|
||||
void init_assert(mrb_state *mrb);
|
||||
void init_version(mrb_state *mrb);
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
void init_cmdline(mrb_state *mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
void init_ntoa(mrb_state *mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
void init_printf(mrb_state *mrb);
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
#endif
|
|
@ -0,0 +1,298 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <mruby/numeric.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/string.h>
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
|
||||
static mrb_value rb_KernAux_utoa(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_itoa(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static mrb_value rb_KernAux_utoa2(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_itoa2(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static mrb_value rb_KernAux_utoa8(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_itoa8(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static mrb_value rb_KernAux_utoa10(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_itoa10(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static mrb_value rb_KernAux_utoa16(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_itoa16(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static int convert_base(mrb_state *mrb, mrb_value base);
|
||||
|
||||
void init_ntoa(mrb_state *const mrb)
|
||||
{
|
||||
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_Error =
|
||||
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(Error));
|
||||
|
||||
mrb_define_class_under_id(mrb, rb_KernAux, MRB_SYM(InvalidNtoaBaseError),
|
||||
rb_KernAux_Error);
|
||||
mrb_define_class_under_id(mrb, rb_KernAux, MRB_SYM(TooLongNtoaPrefixError),
|
||||
rb_KernAux_Error);
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "utoa",
|
||||
rb_KernAux_utoa, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "itoa",
|
||||
rb_KernAux_itoa, MRB_ARGS_REQ(2) | MRB_ARGS_OPT(1));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "utoa2",
|
||||
rb_KernAux_utoa2, MRB_ARGS_REQ(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "itoa2",
|
||||
rb_KernAux_itoa2, MRB_ARGS_REQ(1));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "utoa8",
|
||||
rb_KernAux_utoa8, MRB_ARGS_REQ(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "itoa8",
|
||||
rb_KernAux_itoa8, MRB_ARGS_REQ(1));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "utoa10",
|
||||
rb_KernAux_utoa10, MRB_ARGS_REQ(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "itoa10",
|
||||
rb_KernAux_itoa10, MRB_ARGS_REQ(1));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "utoa16",
|
||||
rb_KernAux_utoa16, MRB_ARGS_REQ(1));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "itoa16",
|
||||
rb_KernAux_itoa16, MRB_ARGS_REQ(1));
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_utoa(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_value base;
|
||||
const char *prefix = NULL;
|
||||
mrb_int prefix_len = 0;
|
||||
mrb_get_args(mrb, "io|s!", &value, &base, &prefix, &prefix_len);
|
||||
|
||||
if (value < 0) {
|
||||
mrb_raise(mrb, E_RANGE_ERROR,
|
||||
"can't convert negative number to uint64_t");
|
||||
}
|
||||
if (prefix_len > KERNAUX_NTOA_MAX_PREFIX_LEN || prefix_len < 0) {
|
||||
struct RClass *const rb_KernAux =
|
||||
mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_TooLongNtoaPrefixError =
|
||||
mrb_class_get_under_id(
|
||||
mrb,
|
||||
rb_KernAux,
|
||||
MRB_SYM(TooLongNtoaPrefixError)
|
||||
);
|
||||
mrb_raisef(mrb, rb_KernAux_TooLongNtoaPrefixError,
|
||||
"prefix length %d is too long", prefix_len);
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA_MIN_BUFFER_SIZE + prefix_len];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_utoa(value, buffer, convert_base(mrb, base), prefix);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_itoa(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_value base;
|
||||
const char *prefix = NULL;
|
||||
mrb_int prefix_len = 0;
|
||||
mrb_get_args(mrb, "io|s!", &value, &base, &prefix, &prefix_len);
|
||||
|
||||
if (prefix_len > KERNAUX_NTOA_MAX_PREFIX_LEN || prefix_len < 0) {
|
||||
struct RClass *const rb_KernAux =
|
||||
mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_TooLongNtoaPrefixError =
|
||||
mrb_class_get_under_id(
|
||||
mrb,
|
||||
rb_KernAux,
|
||||
MRB_SYM(TooLongNtoaPrefixError)
|
||||
);
|
||||
mrb_raisef(mrb, rb_KernAux_TooLongNtoaPrefixError,
|
||||
"prefix length %d is too long", prefix_len);
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_ITOA_MIN_BUFFER_SIZE + prefix_len];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_itoa(value, buffer, convert_base(mrb, base), prefix);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_utoa2(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
if (value < 0) {
|
||||
mrb_raise(mrb, E_RANGE_ERROR,
|
||||
"can't convert negative number to uint64_t");
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA2_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_utoa2(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_itoa2(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
char buffer[KERNAUX_ITOA2_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_itoa2(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_utoa8(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
if (value < 0) {
|
||||
mrb_raise(mrb, E_RANGE_ERROR,
|
||||
"can't convert negative number to uint64_t");
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA8_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_utoa8(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_itoa8(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
char buffer[KERNAUX_ITOA8_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_itoa8(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_utoa10(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
if (value < 0) {
|
||||
mrb_raise(mrb, E_RANGE_ERROR,
|
||||
"can't convert negative number to uint64_t");
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA10_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_utoa10(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_itoa10(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
char buffer[KERNAUX_ITOA10_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_itoa10(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_utoa16(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
if (value < 0) {
|
||||
mrb_raise(mrb, E_RANGE_ERROR,
|
||||
"can't convert negative number to uint64_t");
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA16_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_utoa16(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_itoa16(mrb_state *mrb, mrb_value self)
|
||||
{
|
||||
mrb_int value = 0;
|
||||
mrb_get_args(mrb, "i", &value);
|
||||
|
||||
char buffer[KERNAUX_ITOA16_BUFFER_SIZE];
|
||||
current_mrb_start(mrb);
|
||||
kernaux_itoa16(value, buffer);
|
||||
current_mrb_finish(mrb);
|
||||
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
result = mrb_str_cat_cstr(mrb, result, buffer);
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
int convert_base(mrb_state *mrb, mrb_value base_rb)
|
||||
{
|
||||
if (mrb_obj_is_kind_of(mrb, base_rb, mrb->symbol_class)) {
|
||||
mrb_sym base_sym = mrb_obj_to_sym(mrb, base_rb);
|
||||
switch (base_sym) {
|
||||
case MRB_SYM(b): return 'b';
|
||||
case MRB_SYM(B): return 'B';
|
||||
case MRB_SYM(h): return 'h';
|
||||
case MRB_SYM(H): return 'H';
|
||||
case MRB_SYM(o): return 'o';
|
||||
case MRB_SYM(O): return 'O';
|
||||
case MRB_SYM(d): return 'd';
|
||||
case MRB_SYM(D): return 'D';
|
||||
case MRB_SYM(x): return 'x';
|
||||
case MRB_SYM(X): return 'X';
|
||||
default:
|
||||
{
|
||||
struct RClass *const rb_KernAux =
|
||||
mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_Error =
|
||||
mrb_class_get_under_id(mrb, rb_KernAux, MRB_SYM(Error));
|
||||
mrb_raise(mrb, rb_KernAux_Error, "invalid base");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return mrb_integer(base_rb);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
|
@ -0,0 +1,144 @@
|
|||
#include "main.h"
|
||||
#include "dynarg.h"
|
||||
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/string.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
static mrb_value rb_KernAux_sprintf(mrb_state *mrb, mrb_value self);
|
||||
|
||||
void init_printf(mrb_state *const mrb)
|
||||
{
|
||||
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux, "sprintf", rb_KernAux_sprintf,
|
||||
MRB_ARGS_REQ(1) | MRB_ARGS_REST());
|
||||
}
|
||||
|
||||
#define TAKE_ARG \
|
||||
if (arg_index >= argc) { \
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "too few arguments"); \
|
||||
} \
|
||||
mrb_value arg_rb = args[arg_index++]; \
|
||||
do {} while (0)
|
||||
|
||||
mrb_value rb_KernAux_sprintf(mrb_state *const mrb, mrb_value self)
|
||||
{
|
||||
const char *format;
|
||||
mrb_value *args;
|
||||
mrb_int argc;
|
||||
mrb_get_args(mrb, "z*", &format, &args, &argc);
|
||||
|
||||
int arg_index = 0;
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
|
||||
while (*format) {
|
||||
if (*format != '%') {
|
||||
mrb_str_cat(mrb, result, format, 1);
|
||||
++format;
|
||||
continue;
|
||||
}
|
||||
|
||||
++format;
|
||||
struct KernAux_PrintfFmt_Spec spec =
|
||||
KernAux_PrintfFmt_Spec_create_out(&format);
|
||||
|
||||
if (spec.set_width) {
|
||||
TAKE_ARG;
|
||||
KernAux_PrintfFmt_Spec_set_width(&spec, mrb_integer(arg_rb));
|
||||
}
|
||||
if (spec.set_precision) {
|
||||
TAKE_ARG;
|
||||
KernAux_PrintfFmt_Spec_set_precision(&spec, mrb_integer(arg_rb));
|
||||
}
|
||||
|
||||
struct DynArg dynarg = DynArg_create();
|
||||
|
||||
if (spec.type == KERNAUX_PRINTF_FMT_TYPE_INT) {
|
||||
TAKE_ARG;
|
||||
mrb_ensure_int_type(mrb, arg_rb);
|
||||
DynArg_use_long_long(&dynarg, mrb_integer(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_UINT) {
|
||||
TAKE_ARG;
|
||||
mrb_ensure_int_type(mrb, arg_rb);
|
||||
DynArg_use_unsigned_long_long(&dynarg, mrb_integer(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_FLOAT ||
|
||||
spec.type == KERNAUX_PRINTF_FMT_TYPE_EXP)
|
||||
{
|
||||
TAKE_ARG;
|
||||
mrb_ensure_float_type(mrb, arg_rb);
|
||||
DynArg_use_double(&dynarg, mrb_float(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_CHAR) {
|
||||
TAKE_ARG;
|
||||
mrb_ensure_string_type(mrb, arg_rb);
|
||||
DynArg_use_char(&dynarg, *RSTRING_PTR(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_STR) {
|
||||
TAKE_ARG;
|
||||
mrb_ensure_string_type(mrb, arg_rb);
|
||||
DynArg_use_str(&dynarg, RSTRING_CSTR(mrb, arg_rb));
|
||||
}
|
||||
|
||||
// 1 additional byte for the '%' character.
|
||||
// 1 additional byte for the terminating '\0' character.
|
||||
char old_format[2 + spec.format_limit - spec.format_start];
|
||||
memset(old_format, '\0', sizeof(old_format));
|
||||
old_format[0] = '%';
|
||||
strncpy(&old_format[1], spec.format_start, sizeof(old_format) - 2);
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
int slen;
|
||||
|
||||
if (spec.set_width) {
|
||||
if (spec.set_precision) {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, spec.precision,
|
||||
dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, spec.precision,
|
||||
dynarg.arg);
|
||||
}
|
||||
} else {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, dynarg.arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (spec.set_precision) {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.precision, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.precision, dynarg.arg);
|
||||
}
|
||||
} else {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mrb_str_cat(mrb, result, buffer, slen);
|
||||
}
|
||||
|
||||
if (arg_index < argc) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "too many arguments");
|
||||
}
|
||||
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
|
@ -0,0 +1,49 @@
|
|||
#include "main.h"
|
||||
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/value.h>
|
||||
|
||||
static mrb_value rb_KernAux_Version_with_cmdlineQN(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_Version_with_ntoaQN(mrb_state *mrb, mrb_value self);
|
||||
static mrb_value rb_KernAux_Version_with_printfQN(mrb_state *mrb, mrb_value self);
|
||||
|
||||
void init_version(mrb_state *const mrb)
|
||||
{
|
||||
struct RClass *const rb_KernAux = mrb_module_get_id(mrb, MRB_SYM(KernAux));
|
||||
struct RClass *const rb_KernAux_Version =
|
||||
mrb_define_module_under_id(mrb, rb_KernAux, MRB_SYM(Version));
|
||||
|
||||
mrb_define_class_method(mrb, rb_KernAux_Version, "with_cmdline?",
|
||||
rb_KernAux_Version_with_cmdlineQN, MRB_ARGS_REQ(0));
|
||||
mrb_define_class_method(mrb, rb_KernAux_Version, "with_ntoa?",
|
||||
rb_KernAux_Version_with_ntoaQN, MRB_ARGS_REQ(0));
|
||||
mrb_define_class_method(mrb, rb_KernAux_Version, "with_printf?",
|
||||
rb_KernAux_Version_with_printfQN, MRB_ARGS_REQ(0));
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_Version_with_cmdlineQN(mrb_state *const mrb, const mrb_value self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
return mrb_true_value();
|
||||
#else
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_Version_with_ntoaQN(mrb_state *const mrb, const mrb_value self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
return mrb_true_value();
|
||||
#else
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_Version_with_printfQN(mrb_state *const mrb, const mrb_value self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
return mrb_true_value();
|
||||
#else
|
||||
return mrb_false_value();
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
assert 'KernAux.assert_cb is set by default' do
|
||||
assert_equal KernAux::DEFAULT_ASSERT_CB, KernAux.assert_cb
|
||||
end
|
||||
|
||||
assert 'we can set KernAux.assert_cb' do
|
||||
KernAux.assert_cb = nil
|
||||
assert_equal nil, KernAux.assert_cb
|
||||
|
||||
KernAux.assert_cb = 123
|
||||
assert_equal 123, KernAux.assert_cb
|
||||
|
||||
KernAux.assert_cb = KernAux::DEFAULT_ASSERT_CB
|
||||
assert_equal KernAux::DEFAULT_ASSERT_CB, KernAux.assert_cb
|
||||
end
|
||||
|
||||
assert 'KernAux::DEFAULT_ASSERT_CB raises' do
|
||||
assert_raise KernAux::AssertError, 'foo.rb:123:bar' do
|
||||
KernAux::DEFAULT_ASSERT_CB.call 'foo.rb', 123, 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.assert_do raises' do
|
||||
assert_raise KernAux::AssertError, 'foo.rb:123:hello' do
|
||||
KernAux.assert_do 'foo.rb', 123, 'hello'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,93 @@
|
|||
def test_cmdline(str, expected)
|
||||
result = KernAux.cmdline str
|
||||
|
||||
assert_true result.instance_of? Array
|
||||
assert_true result.frozen?
|
||||
result.each do |item|
|
||||
assert_true item.instance_of? String
|
||||
assert_true item.frozen?
|
||||
end
|
||||
|
||||
assert_equal result, expected
|
||||
end
|
||||
|
||||
if KernAux::Version.with_cmdline?
|
||||
assert 'default' do
|
||||
test_cmdline 'foo bar\\ baz "car cdr"', ['foo', 'bar baz', 'car cdr']
|
||||
end
|
||||
|
||||
assert 'when str is empty' do
|
||||
test_cmdline '', []
|
||||
end
|
||||
|
||||
assert 'when str has invalid type' do
|
||||
assert_raise TypeError, 'Integer cannot be converted to String' do
|
||||
KernAux.cmdline 123
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when str has EOL after backslash' do
|
||||
assert_raise KernAux::CmdlineError, 'EOL after backslash' do
|
||||
KernAux.cmdline '\\'
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when str has EOL after backslash inside quote' do
|
||||
assert_raise KernAux::CmdlineError, 'EOL after backslash inside quote' do
|
||||
KernAux.cmdline '"\\'
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when str has unescaped quotation mark' do
|
||||
assert_raise KernAux::CmdlineError, 'unescaped quotation mark' do
|
||||
KernAux.cmdline 'foo"'
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when str has EOL inside quote' do
|
||||
assert_raise KernAux::CmdlineError, 'EOL inside quote' do
|
||||
KernAux.cmdline '"'
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when there are not too many args' do
|
||||
test_cmdline 'a ' * 256, ['a'] * 256
|
||||
end
|
||||
|
||||
assert 'when there are too many args' do
|
||||
assert_raise KernAux::CmdlineError, 'too many args' do
|
||||
KernAux.cmdline 'a ' * 257
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when args don\'t cause buffer overflow' do
|
||||
test_cmdline 'a' * 4095, ['a' * 4095]
|
||||
end
|
||||
|
||||
assert 'when args cause buffer overflow' do
|
||||
assert_raise KernAux::CmdlineError, 'EOF or buffer overflow' do
|
||||
KernAux.cmdline 'a' * 4096
|
||||
end
|
||||
end
|
||||
|
||||
assert 'usign fixtures' do
|
||||
cmdline_yml = File.expand_path('../../../../fixtures/cmdline.yml', __FILE__)
|
||||
|
||||
YAML.load(File.read(cmdline_yml)).each do |test|
|
||||
escape_str = lambda do |str|
|
||||
eval "\"#{str}\"", nil, __FILE__, __LINE__ # "str"
|
||||
end
|
||||
|
||||
cmdline = escape_str.call test['cmdline']
|
||||
arg_count_max = test['arg_count_max']
|
||||
buffer_size = test['buffer_size']
|
||||
result = test['result']&.map(&escape_str)
|
||||
|
||||
next unless arg_count_max.nil? && buffer_size.nil? && !result.nil?
|
||||
|
||||
assert "transforms #{cmdline.inspect} to #{result.inspect}" do
|
||||
assert_equal KernAux.cmdline(cmdline), result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,348 @@
|
|||
def common_assert(expected, result)
|
||||
assert_true result.instance_of? String
|
||||
assert_true result.frozen?
|
||||
assert_equal expected, result
|
||||
end
|
||||
|
||||
def test_utoa(number, base, expected)
|
||||
common_assert expected, KernAux.utoa(number, base)
|
||||
common_assert expected, KernAux.utoa(number, base, nil)
|
||||
common_assert expected, KernAux.utoa(number, base, '')
|
||||
end
|
||||
|
||||
def test_itoa(number, base, expected)
|
||||
common_assert expected, KernAux.itoa(number, base)
|
||||
common_assert expected, KernAux.itoa(number, base, nil)
|
||||
common_assert expected, KernAux.itoa(number, base, '')
|
||||
end
|
||||
|
||||
def test_utoax(number, base, prefix, expected)
|
||||
common_assert expected, KernAux.utoa(number, base, prefix)
|
||||
end
|
||||
|
||||
def test_itoax(number, base, prefix, expected)
|
||||
common_assert expected, KernAux.itoa(number, base, prefix)
|
||||
end
|
||||
|
||||
def test_utoa2(number, expected)
|
||||
common_assert expected, KernAux.utoa2(number)
|
||||
end
|
||||
|
||||
def test_itoa2(number, expected)
|
||||
common_assert expected, KernAux.itoa2(number)
|
||||
end
|
||||
|
||||
def test_utoa8(number, expected)
|
||||
common_assert expected, KernAux.utoa8(number)
|
||||
end
|
||||
|
||||
def test_itoa8(number, expected)
|
||||
common_assert expected, KernAux.itoa8(number)
|
||||
end
|
||||
|
||||
def test_utoa10(number, expected)
|
||||
common_assert expected, KernAux.utoa10(number)
|
||||
end
|
||||
|
||||
def test_itoa10(number, expected)
|
||||
common_assert expected, KernAux.itoa10(number)
|
||||
end
|
||||
|
||||
def test_utoa16(number, expected)
|
||||
common_assert expected, KernAux.utoa16(number)
|
||||
end
|
||||
|
||||
def test_itoa16(number, expected)
|
||||
common_assert expected, KernAux.itoa16(number)
|
||||
end
|
||||
|
||||
if KernAux::Version.with_ntoa?
|
||||
assert 'KernAux.utoa' do
|
||||
number = Random.rand(2**32 - 1)
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_utoa number, base, number.to_s(base)
|
||||
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_utoa 0, base, '0'
|
||||
|
||||
number = 2**32 - 1
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_utoa number, base, number.to_s(base)
|
||||
|
||||
assert_raise RangeError, 'can\'t convert negative number to uint64_t' do
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
KernAux.utoa(-1, base)
|
||||
end
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
base = -(2 + Random.rand(36 - 2))
|
||||
test_utoa number, base, number.to_s(-base).upcase
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :b, number.to_s(2)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :B, number.to_s(2)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :o, number.to_s(8)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :O, number.to_s(8)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :d, number.to_s(10)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :D, number.to_s(10)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :h, number.to_s(16)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :x, number.to_s(16)
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :H, number.to_s(16).upcase
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
test_utoa number, :X, number.to_s(16).upcase
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'foo'
|
||||
test_utoax number, base, prefix, "#{prefix}#{number.abs.to_s(base)}"
|
||||
|
||||
number = Random.rand(2**32 - 1)
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'a' * 100
|
||||
test_utoax number, base, prefix, "#{prefix}#{number.abs.to_s(base)}"
|
||||
|
||||
assert_raise(
|
||||
KernAux::TooLongNtoaPrefixError,
|
||||
'prefix length 101 is too long',
|
||||
) do
|
||||
number = Random.rand(2**32 - 1)
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'a' * 101
|
||||
KernAux.utoa(number, base, prefix)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**32 - 1).to_s
|
||||
KernAux.utoa(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.itoa' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa number, base, number.to_s(base)
|
||||
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa 0, base, '0'
|
||||
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa 1, base, '1'
|
||||
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa(-1, base, '-1')
|
||||
|
||||
number = 2**31 - 1
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa number, base, number.to_s(base)
|
||||
|
||||
number = -(2**31 - 1)
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
test_itoa number, base, number.to_s(base)
|
||||
|
||||
number = 2**31 - 1
|
||||
base = -(2 + Random.rand(36 - 2))
|
||||
test_itoa number, base, number.to_s(-base).upcase
|
||||
|
||||
number = -(2**31 - 1)
|
||||
base = -(2 + Random.rand(36 - 2))
|
||||
test_itoa number, base, number.to_s(-base).upcase
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :b, number.to_s(2)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :B, number.to_s(2)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :o, number.to_s(8)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :O, number.to_s(8)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :d, number.to_s(10)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :D, number.to_s(10)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :h, number.to_s(16)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :x, number.to_s(16)
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :H, number.to_s(16).upcase
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
test_itoa number, :X, number.to_s(16).upcase
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'foo'
|
||||
sign = number < 0 ? '-' : ''
|
||||
test_itoax number, base, prefix, "#{sign}#{prefix}#{number.abs.to_s(base)}"
|
||||
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'a' * 100
|
||||
sign = number < 0 ? '-' : ''
|
||||
test_itoax number, base, prefix, "#{sign}#{prefix}#{number.abs.to_s(base)}"
|
||||
|
||||
assert_raise(
|
||||
KernAux::TooLongNtoaPrefixError,
|
||||
'prefix length 101 is too long',
|
||||
) do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
base = 2 + Random.rand(36 - 2)
|
||||
prefix = 'a' * 101
|
||||
KernAux.itoa(number, base, prefix)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
KernAux.itoa(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.utoa2' do
|
||||
test_utoa2 0, '0b0'
|
||||
test_utoa2 1, '0b1'
|
||||
test_utoa2 123, '0b1111011'
|
||||
test_utoa2 2**32 - 1, "0b#{(2**32 - 1).to_s(2)}"
|
||||
|
||||
assert_raise RangeError, 'can\'t convert negative number to uint64_t' do
|
||||
KernAux.utoa2(-1)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**32 - 1).to_s
|
||||
KernAux.utoa2(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.itoa2' do
|
||||
test_itoa2 0, '0b0'
|
||||
test_itoa2 1, '0b1'
|
||||
test_itoa2(-1, '-0b1')
|
||||
test_itoa2 123, '0b1111011'
|
||||
test_itoa2(-123, '-0b1111011')
|
||||
test_itoa2 2**31 - 1, "0b#{(2**31 - 1).to_s(2)}"
|
||||
test_itoa2(-2**31, "-0b#{(2**31).to_s(2)}")
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
KernAux.itoa2(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.utoa8' do
|
||||
test_utoa8 0, '0o0'
|
||||
test_utoa8 0o1, '0o1'
|
||||
test_utoa8 0o123, '0o123'
|
||||
test_utoa8 2**32 - 1, "0o#{(2**32 - 1).to_s(8)}"
|
||||
|
||||
assert_raise RangeError, 'can\'t convert negative number to uint64_t' do
|
||||
KernAux.utoa8(-1)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**32 - 1).to_s
|
||||
KernAux.utoa8(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.itoa8' do
|
||||
test_itoa8 0, '0o0'
|
||||
test_itoa8 0o1, '0o1'
|
||||
test_itoa8(-0o1, '-0o1')
|
||||
test_itoa8 0o123, '0o123'
|
||||
test_itoa8(-0o123, '-0o123')
|
||||
test_itoa8 2**31 - 1, "0o#{(2**31 - 1).to_s(8)}"
|
||||
test_itoa8(-2**31, "-0o#{(2**31).to_s(8)}")
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
KernAux.itoa8(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.utoa10' do
|
||||
test_utoa10 0, '0'
|
||||
test_utoa10 1, '1'
|
||||
test_utoa10 123, '123'
|
||||
test_utoa10 2**32 - 1, (2**32 - 1).to_s
|
||||
|
||||
assert_raise RangeError, 'can\'t convert negative number to uint64_t' do
|
||||
KernAux.utoa10(-1)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**32 - 1).to_s
|
||||
KernAux.utoa10(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.itoa10' do
|
||||
test_itoa10 0, '0'
|
||||
test_itoa10 1, '1'
|
||||
test_itoa10(-1, '-1')
|
||||
test_itoa10 123, '123'
|
||||
test_itoa10(-123, '-123')
|
||||
test_itoa10 2**31 - 1, (2**31 - 1).to_s
|
||||
test_itoa10(-2**31, (-2**31).to_s)
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
KernAux.itoa10(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.utoa16' do
|
||||
test_utoa16 0, '0x0'
|
||||
test_utoa16 1, '0x1'
|
||||
test_utoa16 0x123, '0x123'
|
||||
test_utoa16 2**32 - 1, "0x#{(2**32 - 1).to_s(16)}"
|
||||
|
||||
assert_raise RangeError, 'can\'t convert negative number to uint64_t' do
|
||||
KernAux.utoa16(-1)
|
||||
end
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**32 - 1).to_s
|
||||
KernAux.utoa16(number.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
assert 'KernAux.itoa16' do
|
||||
test_itoa16 0, '0x0'
|
||||
test_itoa16 1, '0x1'
|
||||
test_itoa16(-1, '-0x1')
|
||||
test_itoa16 0x123, '0x123'
|
||||
test_itoa16(-0x123, '-0x123')
|
||||
test_itoa16 2**31 - 1, "0x#{(2**31 - 1).to_s(16)}"
|
||||
test_itoa16(-2**31, "-0x#{(2**31).to_s(16)}")
|
||||
|
||||
assert_raise TypeError, 'no implicit conversion from string' do
|
||||
number = Random.rand(2**31 - 1) * [1, -1].sample
|
||||
KernAux.itoa16(number.to_s)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,113 @@
|
|||
if KernAux::Version.with_printf?
|
||||
assert 'KernAux.sprintf' do
|
||||
assert 'integers' do
|
||||
assert_equal 'i:0', KernAux.sprintf('i:%i', 0)
|
||||
assert_equal 'u:0', KernAux.sprintf('u:%u', 0)
|
||||
assert_equal 'i:1', KernAux.sprintf('i:%i', 1)
|
||||
assert_equal 'u:1', KernAux.sprintf('u:%u', 1)
|
||||
assert_equal 'i:10', KernAux.sprintf('i:%i', 10)
|
||||
assert_equal 'u:10', KernAux.sprintf('u:%u', 10)
|
||||
assert_equal 'i:100', KernAux.sprintf('i:%i', 100)
|
||||
assert_equal 'u:100', KernAux.sprintf('u:%u', 100)
|
||||
assert_equal 'i:1000', KernAux.sprintf('i:%i', 1000)
|
||||
assert_equal 'u:1000', KernAux.sprintf('u:%u', 1000)
|
||||
assert_equal 'i:10000', KernAux.sprintf('i:%i', 10_000)
|
||||
assert_equal 'u:10000', KernAux.sprintf('u:%u', 10_000)
|
||||
assert_equal 'i:100000', KernAux.sprintf('i:%i', 100_000)
|
||||
assert_equal 'u:100000', KernAux.sprintf('u:%u', 100_000)
|
||||
assert_equal 'i:1000000', KernAux.sprintf('i:%i', 1_000_000)
|
||||
assert_equal 'u:1000000', KernAux.sprintf('u:%u', 1_000_000)
|
||||
assert_equal 'i:10000000', KernAux.sprintf('i:%i', 10_000_000)
|
||||
assert_equal 'u:10000000', KernAux.sprintf('u:%u', 10_000_000)
|
||||
assert_equal 'i:100000000', KernAux.sprintf('i:%i', 10**8)
|
||||
assert_equal 'u:100000000', KernAux.sprintf('u:%u', 10**8)
|
||||
assert_equal 'i:1000000000', KernAux.sprintf('i:%i', 10**9)
|
||||
assert_equal 'u:1000000000', KernAux.sprintf('u:%u', 10**9)
|
||||
assert_equal 'i:2147483647', KernAux.sprintf('i:%i', 2**31 - 1)
|
||||
assert_equal 'u:2147483647', KernAux.sprintf('u:%u', 2**31 - 1)
|
||||
end
|
||||
|
||||
# TODO: test with different boxing
|
||||
# assert 'integer overflows' do
|
||||
# assert_equal 'i:-2147483648', KernAux.sprintf('i:%i', 2**31)
|
||||
# assert_equal 'u: 2147483648', KernAux.sprintf('u: %u', 2**31)
|
||||
# assert_equal 'i:-2147483647', KernAux.sprintf('i:%i', 2**31 + 1)
|
||||
# assert_equal 'u: 2147483649', KernAux.sprintf('u: %u', 2**31 + 1)
|
||||
# assert_equal 'i:-1', KernAux.sprintf('i:%i', 2**32 - 1)
|
||||
# assert_equal 'u: 4294967295', KernAux.sprintf('u: %u', 2**32 - 1)
|
||||
# assert_equal 'i: 0', KernAux.sprintf('i: %i', 2**32)
|
||||
# assert_equal 'u: 0', KernAux.sprintf('u: %u', 2**32)
|
||||
# end
|
||||
|
||||
assert 'when there are too many arguments' do
|
||||
[
|
||||
['Hello!', 'World!'],
|
||||
['Hello, %s!', 'World', 'Alex'],
|
||||
].each do |args|
|
||||
assert_raise ArgumentError, 'too many arguments' do
|
||||
KernAux.sprintf(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert 'when there are too few arguments' do
|
||||
[
|
||||
[],
|
||||
['Hello, %s!'],
|
||||
['Hello, %*s!', 20],
|
||||
['Hello, %.*s!', 20],
|
||||
['Hello, %*.*s!', 20, 20],
|
||||
].each do |args|
|
||||
assert_raise ArgumentError, 'too few arguments' do
|
||||
KernAux.sprintf(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
['', 'using regular fixtures'],
|
||||
['_orig', 'using original fixtures'],
|
||||
].each do |(suffix, description)|
|
||||
assert description do
|
||||
printf_yml =
|
||||
File.expand_path("../../../../fixtures/printf#{suffix}.yml", __FILE__)
|
||||
|
||||
YAML.load(File.read(printf_yml)).each do |test|
|
||||
expected = test['result']
|
||||
|
||||
format = ''
|
||||
args = []
|
||||
|
||||
test['args'].each do |arg|
|
||||
if arg.is_a? String
|
||||
format += arg
|
||||
else
|
||||
format += arg[0]
|
||||
is_int_format = %w[i d u x X o b].any? { |s| arg[0].include? s }
|
||||
arg[1..].each do |item|
|
||||
if item.is_a? Array
|
||||
if item.length == 1
|
||||
args << item[0]
|
||||
elsif item[0] == 'long long'
|
||||
args << item[1]
|
||||
else
|
||||
raise "Unknown format: #{args.inspect}"
|
||||
end
|
||||
elsif is_int_format && item.is_a?(Float)
|
||||
args << item.round
|
||||
else
|
||||
args << item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert "transforms (#{format.inspect}, #{args.inspect[1...-1]}) " \
|
||||
"to #{expected.inspect}" do
|
||||
assert_equal expected, KernAux.sprintf(format, *args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
/dist/
|
||||
/src/kernaux.egg-info/
|
|
@ -0,0 +1,5 @@
|
|||
kernaux
|
||||
=======
|
||||
|
||||
Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
|
||||
library for kernel development.
|
|
@ -0,0 +1,6 @@
|
|||
[build-system]
|
||||
requires = [
|
||||
"setuptools>=42",
|
||||
"wheel"
|
||||
]
|
||||
build-backend = "setuptools.build_meta"
|
|
@ -0,0 +1,24 @@
|
|||
[metadata]
|
||||
name = kernaux
|
||||
version = 0.0.0
|
||||
author = Alex Kotov
|
||||
author_email = kotovalexarian@gmail.com
|
||||
description = Binding to libkernaux - auxiliary library for kernel development
|
||||
long_description = file: README.md
|
||||
long_description_content_type = text/markdown
|
||||
url = https://github.com/tailix/libkernaux/tree/master/bindings/python
|
||||
project_urls =
|
||||
Bug Tracker = https://github.com/tailix/libkernaux/issues
|
||||
classifiers =
|
||||
Programming Language :: Python :: 3
|
||||
License :: OSI Approved :: MIT License
|
||||
Operating System :: POSIX
|
||||
|
||||
[options]
|
||||
package_dir =
|
||||
= src
|
||||
packages = find:
|
||||
python_requires = >=3.8
|
||||
|
||||
[options.packages.find]
|
||||
where = src
|
|
@ -0,0 +1,2 @@
|
|||
def add_one(number):
|
||||
return number + 1
|
|
@ -0,0 +1,44 @@
|
|||
*.gem
|
||||
*.rbc
|
||||
*.so
|
||||
|
||||
/.byebug_history
|
||||
/.config/
|
||||
/.rake_tasks~
|
||||
/InstalledFiles/
|
||||
/pkg/
|
||||
/tmp/
|
||||
|
||||
# Used by dotenv library to load environment variables.
|
||||
/.env
|
||||
|
||||
# RSpec configuration and generated files.
|
||||
/.rspec
|
||||
/coverage/
|
||||
/spec/examples.txt
|
||||
/spec/reports/
|
||||
/test/tmp/
|
||||
/test/version_tmp/
|
||||
|
||||
# Documentation cache and generated files.
|
||||
/.yardoc/
|
||||
/_yardoc/
|
||||
/doc/
|
||||
/rdoc/
|
||||
|
||||
# Environment normalization.
|
||||
/.bundle/
|
||||
/vendor/bundle/
|
||||
/lib/bundler/man/
|
||||
|
||||
# For a library or gem, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in.
|
||||
/Gemfile.lock
|
||||
/.ruby-version
|
||||
/.ruby-gemset
|
||||
|
||||
# Unless supporting rvm < 1.11.0 or doing something fancy, ignore this.
|
||||
/.rvmrc
|
||||
|
||||
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
||||
/.rubocop-https?--*
|
|
@ -0,0 +1,79 @@
|
|||
require:
|
||||
- rubocop-performance
|
||||
- rubocop-rake
|
||||
- rubocop-rspec
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 3.0
|
||||
DisplayCopNames: true
|
||||
NewCops: enable
|
||||
|
||||
Layout/AccessModifierIndentation:
|
||||
EnforcedStyle: outdent
|
||||
|
||||
Layout/LineLength:
|
||||
Max: 80
|
||||
|
||||
Lint/AmbiguousOperatorPrecedence:
|
||||
Enabled: false
|
||||
|
||||
Lint/ReturnInVoidContext:
|
||||
Enabled: false
|
||||
|
||||
Metrics/BlockLength:
|
||||
Exclude:
|
||||
- '*.gemspec'
|
||||
- 'Rakefile'
|
||||
- 'spec/**/*_spec.rb'
|
||||
|
||||
RSpec/ContextWording:
|
||||
Prefixes:
|
||||
- 'and'
|
||||
- 'for'
|
||||
- 'using'
|
||||
- 'when'
|
||||
- 'with'
|
||||
- 'without'
|
||||
|
||||
RSpec/FilePath:
|
||||
CustomTransform:
|
||||
KernAux: kernaux
|
||||
|
||||
RSpec/ExampleLength:
|
||||
CountAsOne: ['array', 'hash', 'heredoc']
|
||||
|
||||
Security/Eval:
|
||||
Exclude:
|
||||
- 'spec/**/*_spec.rb'
|
||||
|
||||
Style/AndOr:
|
||||
EnforcedStyle: conditionals
|
||||
|
||||
Style/Documentation:
|
||||
Exclude:
|
||||
- 'Rakefile'
|
||||
|
||||
Style/DoubleNegation:
|
||||
Enabled: false
|
||||
|
||||
Style/GlobalVars:
|
||||
Exclude:
|
||||
- 'ext/*/extconf.rb'
|
||||
|
||||
Style/HashAsLastArrayItem:
|
||||
Enabled: false
|
||||
|
||||
Style/PerlBackrefs:
|
||||
Enabled: false
|
||||
|
||||
Style/TrailingCommaInArguments:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/TrailingCommaInArrayLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/TrailingCommaInHashLiteral:
|
||||
EnforcedStyleForMultiline: comma
|
||||
|
||||
Style/VariableInterpolation:
|
||||
Enabled: false
|
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
if ENV['SKIP_COVERAGE'].to_s.empty?
|
||||
SimpleCov.start do
|
||||
minimum_coverage 95
|
||||
|
||||
add_filter '/spec/'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,4 @@
|
|||
--markup markdown
|
||||
--readme README.md
|
||||
--protected
|
||||
--private
|
|
@ -0,0 +1,8 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
# Specify your gem's dependencies in kernaux.gemspec
|
||||
gemspec
|
||||
|
||||
gem 'simplecov', require: false
|
|
@ -0,0 +1,29 @@
|
|||
::KernAux
|
||||
=========
|
||||
|
||||
[![Build status](https://github.com/tailix/libkernaux/actions/workflows/ruby.yml/badge.svg)](https://github.com/tailix/libkernaux/actions/workflows/ruby.yml)
|
||||
[![Build status (FreeBSD)](https://api.cirrus-ci.com/github/tailix/libkernaux.svg?task=Ruby%20(FreeBSD))](https://cirrus-ci.com/github/tailix/libkernaux)
|
||||
|
||||
Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
|
||||
library for kernel development.
|
||||
|
||||
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
Add the following to your `Gemfile`:
|
||||
|
||||
```ruby
|
||||
gem 'kernaux', '~> 0.7.0'
|
||||
```
|
||||
|
||||
Or add the following to your `*.gemspec`:
|
||||
|
||||
```ruby
|
||||
Gem::Specification.new do |spec|
|
||||
# ...
|
||||
spec.add_runtime_dependency 'kernaux', '~> 0.7.0'
|
||||
# ...
|
||||
end
|
||||
```
|
|
@ -0,0 +1,92 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/gem_tasks'
|
||||
|
||||
module Bundler
|
||||
class GemHelper
|
||||
def tag_version(*)
|
||||
yield if block_given?
|
||||
end
|
||||
|
||||
def git_push(*); end
|
||||
|
||||
def perform_git_push(*); end
|
||||
end
|
||||
end
|
||||
|
||||
CLEAN << '.yardoc'
|
||||
CLEAN << 'coverage'
|
||||
CLEAN << 'doc'
|
||||
CLEAN << 'spec/examples.txt'
|
||||
|
||||
desc 'Run default checks'
|
||||
task default: %i[test lint]
|
||||
|
||||
desc 'Run tests'
|
||||
task test: :spec
|
||||
|
||||
desc 'Run code analysis tools'
|
||||
task lint: %i[rubocop cppcheck yard:cov]
|
||||
|
||||
desc 'Fix code style (rubocop --auto-correct)'
|
||||
task fix: 'rubocop:auto_correct'
|
||||
|
||||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rubocop/rake_task'
|
||||
RuboCop::RakeTask.new
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
begin
|
||||
require 'yard'
|
||||
YARD::Rake::YardocTask.new
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rake/extensiontask'
|
||||
Rake::ExtensionTask.new 'default' do |ext|
|
||||
ext.lib_dir = 'lib/kernaux'
|
||||
end
|
||||
rescue LoadError
|
||||
nil
|
||||
end
|
||||
|
||||
desc 'Open development console'
|
||||
task :console do
|
||||
sh 'bundle', 'exec', File.expand_path(File.join('bin', 'console'), __dir__)
|
||||
end
|
||||
|
||||
desc 'Run cppcheck'
|
||||
task :cppcheck do
|
||||
sh(
|
||||
'cppcheck',
|
||||
'--quiet',
|
||||
'--error-exitcode=1',
|
||||
'--std=c99',
|
||||
'--enable=warning,style,performance,portability',
|
||||
__dir__,
|
||||
)
|
||||
end
|
||||
|
||||
namespace :yard do
|
||||
desc 'Measure documentation coverage'
|
||||
task :cov do
|
||||
result = `bundle exec yard stats`.lines.last.strip.freeze
|
||||
m = result.match(/\A(\d+(\.\d+)?)% documented\z/)
|
||||
raise 'Invalid result' if m.nil?
|
||||
|
||||
coverage = m[1].to_f.round(2)
|
||||
puts "Documentation coverage: #{coverage}%"
|
||||
raise 'Not fully documented!' if coverage != 100
|
||||
end
|
||||
end
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'bundler/setup'
|
||||
require 'kernaux'
|
||||
require 'pry'
|
||||
|
||||
Pry.start
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
set -eux
|
||||
|
||||
bundle install
|
||||
|
||||
# Do any other automated setup that you need to do here
|
|
@ -0,0 +1,55 @@
|
|||
#include "main.h"
|
||||
|
||||
static void assert_cb(const char *file, int line, const char *msg);
|
||||
|
||||
static VALUE rb_KernAux_assert_cb(VALUE self);
|
||||
static VALUE rb_KernAux_assert_cb_EQ(VALUE self, VALUE assert_cb);
|
||||
static
|
||||
VALUE rb_KernAux_assert_do(VALUE self, VALUE file, VALUE line, VALUE msg);
|
||||
|
||||
void init_assert()
|
||||
{
|
||||
kernaux_assert_cb = assert_cb;
|
||||
|
||||
rb_define_singleton_method(rb_KernAux, "assert_cb",
|
||||
rb_KernAux_assert_cb, 0);
|
||||
rb_define_singleton_method(rb_KernAux, "assert_cb=",
|
||||
rb_KernAux_assert_cb_EQ, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "assert_do",
|
||||
rb_KernAux_assert_do, 3);
|
||||
}
|
||||
|
||||
void assert_cb(const char *const file, const int line, const char *const msg)
|
||||
{
|
||||
const VALUE assert_cb_rb = rb_iv_get(rb_KernAux, "@assert_cb");
|
||||
if (assert_cb_rb == Qnil) return;
|
||||
const VALUE file_rb = rb_str_new2(file);
|
||||
const VALUE line_rb = INT2FIX(line);
|
||||
const VALUE msg_rb = rb_str_new2(msg);
|
||||
rb_funcall(assert_cb_rb, rb_intern_call, 3, file_rb, line_rb, msg_rb);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_assert_cb(const VALUE self)
|
||||
{
|
||||
return rb_iv_get(self, "@assert_cb");
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_assert_cb_EQ(const VALUE self, const VALUE assert_cb_rb)
|
||||
{
|
||||
return rb_iv_set(self, "@assert_cb", assert_cb_rb);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_assert_do(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
VALUE file_rb,
|
||||
const VALUE line_rb,
|
||||
VALUE msg_rb
|
||||
) {
|
||||
const char *const file = StringValueCStr(file_rb);
|
||||
const int line = FIX2INT(line_rb);
|
||||
const char *const msg = StringValueCStr(msg_rb);
|
||||
|
||||
kernaux_assert_do(file, line, msg);
|
||||
|
||||
return Qnil;
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
#include "main.h"
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
|
||||
#define ARG_COUNT_MAX 256
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
struct Data {
|
||||
char error_msg[KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX];
|
||||
char *argv[ARG_COUNT_MAX];
|
||||
char buffer[BUFFER_SIZE];
|
||||
};
|
||||
|
||||
static VALUE rb_ANON_Data_ALLOC(VALUE klass);
|
||||
static VALUE rb_KernAux_cmdline(VALUE self, VALUE cmdline);
|
||||
|
||||
static const struct rb_data_type_struct info = {
|
||||
.wrap_struct_name = "cmdline",
|
||||
.function = {
|
||||
.dmark = NULL,
|
||||
.dfree = RUBY_DEFAULT_FREE,
|
||||
.dsize = NULL,
|
||||
.dcompact = NULL,
|
||||
.reserved = { 0 },
|
||||
},
|
||||
.parent = NULL,
|
||||
.data = NULL,
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
};
|
||||
|
||||
static VALUE rb_KernAux_CmdlineError = Qnil;
|
||||
static VALUE rb_ANON_Data = Qnil;
|
||||
|
||||
void init_cmdline()
|
||||
{
|
||||
rb_gc_register_mark_object(rb_KernAux_CmdlineError =
|
||||
rb_define_class_under(rb_KernAux, "CmdlineError", rb_KernAux_Error));
|
||||
rb_gc_register_mark_object(rb_ANON_Data =
|
||||
rb_funcall(rb_cClass, rb_intern_new, 1, rb_cObject));
|
||||
|
||||
rb_define_alloc_func(rb_ANON_Data, rb_ANON_Data_ALLOC);
|
||||
rb_define_singleton_method(rb_KernAux, "cmdline", rb_KernAux_cmdline, 1);
|
||||
}
|
||||
|
||||
VALUE rb_ANON_Data_ALLOC(const VALUE klass)
|
||||
{
|
||||
struct Data *data;
|
||||
return TypedData_Make_Struct(klass, struct Data, &info, data);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_cmdline(const VALUE self_rb, VALUE cmdline_rb)
|
||||
{
|
||||
const char *const cmdline = StringValueCStr(cmdline_rb);
|
||||
size_t argc;
|
||||
|
||||
const VALUE data_rb = rb_funcall(rb_ANON_Data, rb_intern_new, 0);
|
||||
struct Data *data;
|
||||
TypedData_Get_Struct(data_rb, struct Data, &info, data);
|
||||
if (!data) rb_raise(rb_KernAux_CmdlineError, "internal error");
|
||||
|
||||
const bool result = kernaux_cmdline(
|
||||
cmdline,
|
||||
data->error_msg,
|
||||
&argc,
|
||||
data->argv,
|
||||
data->buffer,
|
||||
ARG_COUNT_MAX,
|
||||
BUFFER_SIZE
|
||||
);
|
||||
|
||||
if (!result) rb_raise(rb_KernAux_CmdlineError, "%s", data->error_msg);
|
||||
|
||||
VALUE result_rb = rb_ary_new2(argc);
|
||||
for (size_t index = 0; index < argc; ++index) {
|
||||
rb_ary_push(
|
||||
result_rb,
|
||||
rb_funcall(rb_str_new2(data->argv[index]), rb_intern_freeze, 0)
|
||||
);
|
||||
}
|
||||
return rb_funcall(result_rb, rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
|
@ -0,0 +1,45 @@
|
|||
#include "dynarg.h"
|
||||
|
||||
struct DynArg DynArg_create()
|
||||
{
|
||||
struct DynArg dynarg;
|
||||
DynArg_init(&dynarg);
|
||||
return dynarg;
|
||||
}
|
||||
|
||||
void DynArg_init(struct DynArg *const dynarg)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->dbl = 0.0;
|
||||
dynarg->arg.str = "";
|
||||
}
|
||||
|
||||
void DynArg_use_char(struct DynArg *const dynarg, const char chr)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.chr = chr;
|
||||
}
|
||||
|
||||
void DynArg_use_double(struct DynArg *const dynarg, const double dbl)
|
||||
{
|
||||
dynarg->use_dbl = true;
|
||||
dynarg->dbl = dbl;
|
||||
}
|
||||
|
||||
void DynArg_use_long_long(struct DynArg *const dynarg, const long long ll)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.ll = ll;
|
||||
}
|
||||
|
||||
void DynArg_use_str(struct DynArg *const dynarg, const char *const str)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.str = str;
|
||||
}
|
||||
|
||||
void DynArg_use_unsigned_long_long(struct DynArg *const dynarg, const unsigned long long ull)
|
||||
{
|
||||
dynarg->use_dbl = false;
|
||||
dynarg->arg.ull = ull;
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef INCLUDED_DYNARG
|
||||
#define INCLUDED_DYNARG
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <kernaux/macro.h>
|
||||
|
||||
#include <kernaux/macro/packing_start.run>
|
||||
|
||||
struct DynArg {
|
||||
bool use_dbl;
|
||||
double dbl;
|
||||
// TODO: check if this will work on different endianness.
|
||||
union {
|
||||
char chr;
|
||||
long long ll;
|
||||
const char *str;
|
||||
unsigned long long ull;
|
||||
} KERNAUX_PACKED arg;
|
||||
}
|
||||
KERNAUX_PACKED;
|
||||
|
||||
#include <kernaux/macro/packing_end.run>
|
||||
|
||||
struct DynArg DynArg_create();
|
||||
void DynArg_init(struct DynArg *dynarg);
|
||||
|
||||
void DynArg_use_char(struct DynArg *dynarg, char chr);
|
||||
void DynArg_use_double(struct DynArg *dynarg, double dbl);
|
||||
void DynArg_use_long_long(struct DynArg *dynarg, long long ll);
|
||||
void DynArg_use_str(struct DynArg *dynarg, const char *str);
|
||||
void DynArg_use_unsigned_long_long(struct DynArg *dynarg, unsigned long long ull);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'mkmf'
|
||||
|
||||
$CFLAGS += ' -pedantic -Wall -Wextra'
|
||||
|
||||
raise 'libkernaux not found' unless have_library 'kernaux'
|
||||
|
||||
raise 'kernaux_assert_do not found' unless have_func 'kernaux_assert_do'
|
||||
unless have_var 'kernaux_assert_cb', 'kernaux.h'
|
||||
raise 'kernaux_assert_cb not found'
|
||||
end
|
||||
|
||||
raise 'can\'t create Makefile' unless create_makefile 'kernaux/default'
|
|
@ -0,0 +1,34 @@
|
|||
#include "main.h"
|
||||
|
||||
ID rb_intern_call = Qnil;
|
||||
ID rb_intern_freeze = Qnil;
|
||||
ID rb_intern_LESS = Qnil;
|
||||
ID rb_intern_new = Qnil;
|
||||
|
||||
VALUE rb_KernAux = Qnil;
|
||||
VALUE rb_KernAux_Error = Qnil;
|
||||
|
||||
void Init_default()
|
||||
{
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_call = rb_intern("call")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_freeze = rb_intern("freeze")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_LESS = rb_intern("<")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_new = rb_intern("new")));
|
||||
|
||||
rb_gc_register_mark_object(rb_KernAux = rb_define_module("KernAux"));
|
||||
rb_gc_register_mark_object(rb_KernAux_Error =
|
||||
rb_define_class_under(rb_KernAux, "Error", rb_eRuntimeError));
|
||||
|
||||
init_version();
|
||||
init_assert();
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
init_cmdline();
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
init_ntoa();
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
init_printf();
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
#ifndef __MAIN_H__
|
||||
#define __MAIN_H__
|
||||
|
||||
#include <kernaux.h>
|
||||
#include <ruby.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
extern ID rb_intern_call;
|
||||
extern ID rb_intern_freeze;
|
||||
extern ID rb_intern_LESS;
|
||||
extern ID rb_intern_new;
|
||||
|
||||
extern VALUE rb_KernAux;
|
||||
extern VALUE rb_KernAux_Error;
|
||||
|
||||
void init_version();
|
||||
void init_assert();
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
void init_cmdline();
|
||||
#endif // KERNAUX_VERSION_WITH_CMDLINE
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
void init_ntoa();
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
void init_printf();
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
#endif
|
|
@ -0,0 +1,254 @@
|
|||
#include "main.h"
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
|
||||
static VALUE rb_KernAux_utoa(int argc, const VALUE *argv, VALUE self);
|
||||
static VALUE rb_KernAux_itoa(int argc, const VALUE *argv, VALUE self);
|
||||
static VALUE rb_KernAux_utoa2(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_itoa2(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_utoa8(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_itoa8(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_utoa10(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_itoa10(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_utoa16(VALUE self, VALUE number);
|
||||
static VALUE rb_KernAux_itoa16(VALUE self, VALUE number);
|
||||
|
||||
static ID rb_intern_b = Qnil;
|
||||
static ID rb_intern_B = Qnil;
|
||||
static ID rb_intern_h = Qnil;
|
||||
static ID rb_intern_H = Qnil;
|
||||
static ID rb_intern_o = Qnil;
|
||||
static ID rb_intern_O = Qnil;
|
||||
static ID rb_intern_d = Qnil;
|
||||
static ID rb_intern_D = Qnil;
|
||||
static ID rb_intern_x = Qnil;
|
||||
static ID rb_intern_X = Qnil;
|
||||
|
||||
static VALUE rb_KernAux_InvalidNtoaBaseError = Qnil;
|
||||
static VALUE rb_KernAux_TooLongNtoaPrefixError = Qnil;
|
||||
|
||||
static int convert_base(VALUE base);
|
||||
|
||||
void init_ntoa()
|
||||
{
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_b = rb_intern("b")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_B = rb_intern("B")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_h = rb_intern("h")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_H = rb_intern("H")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_o = rb_intern("o")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_O = rb_intern("O")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_d = rb_intern("d")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_D = rb_intern("D")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_x = rb_intern("x")));
|
||||
rb_gc_register_mark_object(ID2SYM(rb_intern_X = rb_intern("X")));
|
||||
|
||||
rb_gc_register_mark_object(rb_KernAux_InvalidNtoaBaseError =
|
||||
rb_define_class_under(rb_KernAux, "InvalidNtoaBaseError",
|
||||
rb_KernAux_Error));
|
||||
rb_gc_register_mark_object(rb_KernAux_TooLongNtoaPrefixError =
|
||||
rb_define_class_under(rb_KernAux, "TooLongNtoaPrefixError",
|
||||
rb_KernAux_Error));
|
||||
|
||||
rb_define_singleton_method(rb_KernAux, "utoa", rb_KernAux_utoa, -1);
|
||||
rb_define_singleton_method(rb_KernAux, "itoa", rb_KernAux_itoa, -1);
|
||||
rb_define_singleton_method(rb_KernAux, "utoa2", rb_KernAux_utoa2, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "itoa2", rb_KernAux_itoa2, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "utoa8", rb_KernAux_utoa8, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "itoa8", rb_KernAux_itoa8, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "utoa10", rb_KernAux_utoa10, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "itoa10", rb_KernAux_itoa10, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "utoa16", rb_KernAux_utoa16, 1);
|
||||
rb_define_singleton_method(rb_KernAux, "itoa16", rb_KernAux_itoa16, 1);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_utoa(const int argc, const VALUE *argv, const VALUE self)
|
||||
{
|
||||
if (argc < 2 || argc > 3) {
|
||||
rb_raise(
|
||||
rb_eArgError,
|
||||
"wrong number of arguments (given %d, expected 2..3)",
|
||||
argc
|
||||
);
|
||||
}
|
||||
|
||||
VALUE number_rb = argv[0];
|
||||
VALUE base_rb = argv[1];
|
||||
VALUE prefix_rb = argc == 3 ? argv[2] : Qnil;
|
||||
|
||||
const uint64_t number = NUM2ULL(number_rb);
|
||||
|
||||
if (rb_funcall(number_rb, rb_intern_LESS, 1, INT2FIX(0))) {
|
||||
rb_raise(rb_eRangeError, "can't convert negative number to uint64_t");
|
||||
}
|
||||
|
||||
const char *prefix = NULL;
|
||||
long prefix_len = 0;
|
||||
if (!NIL_P(prefix_rb)) {
|
||||
prefix = StringValueCStr(prefix_rb);
|
||||
prefix_len = RSTRING_LEN(prefix_rb);
|
||||
|
||||
if (prefix_len > KERNAUX_NTOA_MAX_PREFIX_LEN || prefix_len < 0) {
|
||||
rb_raise(
|
||||
rb_KernAux_TooLongNtoaPrefixError,
|
||||
"prefix length %ld is too long",
|
||||
prefix_len
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_UTOA_MIN_BUFFER_SIZE + prefix_len];
|
||||
kernaux_utoa(number, buffer, convert_base(base_rb), prefix);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_itoa(const int argc, const VALUE *argv, const VALUE self)
|
||||
{
|
||||
if (argc < 2 || argc > 3) {
|
||||
rb_raise(
|
||||
rb_eArgError,
|
||||
"wrong number of arguments (given %d, expected 2..3)",
|
||||
argc
|
||||
);
|
||||
}
|
||||
|
||||
VALUE number_rb = argv[0];
|
||||
VALUE base_rb = argv[1];
|
||||
VALUE prefix_rb = argc == 3 ? argv[2] : Qnil;
|
||||
|
||||
const int64_t number = NUM2LL(number_rb);
|
||||
|
||||
const char *prefix = NULL;
|
||||
long prefix_len = 0;
|
||||
if (!NIL_P(prefix_rb)) {
|
||||
prefix = StringValueCStr(prefix_rb);
|
||||
prefix_len = RSTRING_LEN(prefix_rb);
|
||||
|
||||
if (prefix_len > KERNAUX_NTOA_MAX_PREFIX_LEN || prefix_len < 0) {
|
||||
rb_raise(
|
||||
rb_KernAux_TooLongNtoaPrefixError,
|
||||
"prefix length %ld is too long",
|
||||
prefix_len
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
char buffer[KERNAUX_ITOA_MIN_BUFFER_SIZE + prefix_len];
|
||||
kernaux_itoa(number, buffer, convert_base(base_rb), prefix);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_utoa2(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const uint64_t number = NUM2ULL(number_rb);
|
||||
if (rb_funcall(number_rb, rb_intern_LESS, 1, INT2FIX(0))) {
|
||||
rb_raise(rb_eRangeError, "can't convert negative number to uint64_t");
|
||||
}
|
||||
char buffer[KERNAUX_UTOA2_BUFFER_SIZE];
|
||||
kernaux_utoa2(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_itoa2(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const int64_t number = NUM2LL(number_rb);
|
||||
char buffer[KERNAUX_ITOA2_BUFFER_SIZE];
|
||||
kernaux_itoa2(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_utoa8(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const uint64_t number = NUM2ULL(number_rb);
|
||||
if (rb_funcall(number_rb, rb_intern_LESS, 1, INT2FIX(0))) {
|
||||
rb_raise(rb_eRangeError, "can't convert negative number to uint64_t");
|
||||
}
|
||||
char buffer[KERNAUX_UTOA8_BUFFER_SIZE];
|
||||
kernaux_utoa8(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_itoa8(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const int64_t number = NUM2LL(number_rb);
|
||||
char buffer[KERNAUX_ITOA8_BUFFER_SIZE];
|
||||
kernaux_itoa8(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_utoa10(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const uint64_t number = NUM2ULL(number_rb);
|
||||
if (rb_funcall(number_rb, rb_intern_LESS, 1, INT2FIX(0))) {
|
||||
rb_raise(rb_eRangeError, "can't convert negative number to uint64_t");
|
||||
}
|
||||
char buffer[KERNAUX_UTOA10_BUFFER_SIZE];
|
||||
kernaux_utoa10(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_itoa10(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const int64_t number = NUM2LL(number_rb);
|
||||
char buffer[KERNAUX_ITOA10_BUFFER_SIZE];
|
||||
kernaux_itoa10(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_utoa16(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const uint64_t number = NUM2ULL(number_rb);
|
||||
if (rb_funcall(number_rb, rb_intern_LESS, 1, INT2FIX(0))) {
|
||||
rb_raise(rb_eRangeError, "can't convert negative number to uint64_t");
|
||||
}
|
||||
char buffer[KERNAUX_UTOA16_BUFFER_SIZE];
|
||||
kernaux_utoa16(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_itoa16(
|
||||
const VALUE self_rb KERNAUX_UNUSED,
|
||||
const VALUE number_rb
|
||||
) {
|
||||
const int64_t number = NUM2LL(number_rb);
|
||||
char buffer[KERNAUX_ITOA16_BUFFER_SIZE];
|
||||
kernaux_itoa16(number, buffer);
|
||||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
int convert_base(const VALUE base_rb)
|
||||
{
|
||||
if (TYPE(base_rb) == T_SYMBOL) {
|
||||
const ID base_id = SYM2ID(base_rb);
|
||||
if (base_id == rb_intern_b) return 'b';
|
||||
else if (base_id == rb_intern_B) return 'B';
|
||||
else if (base_id == rb_intern_h) return 'h';
|
||||
else if (base_id == rb_intern_H) return 'H';
|
||||
else if (base_id == rb_intern_o) return 'o';
|
||||
else if (base_id == rb_intern_O) return 'O';
|
||||
else if (base_id == rb_intern_d) return 'd';
|
||||
else if (base_id == rb_intern_D) return 'D';
|
||||
else if (base_id == rb_intern_x) return 'x';
|
||||
else if (base_id == rb_intern_X) return 'X';
|
||||
else {
|
||||
rb_raise(rb_KernAux_InvalidNtoaBaseError, "invalid base");
|
||||
}
|
||||
} else {
|
||||
return NUM2INT(base_rb);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_NTOA
|
|
@ -0,0 +1,137 @@
|
|||
#include "main.h"
|
||||
#include "dynarg.h"
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
/**
|
||||
* Typical `printf`.
|
||||
*
|
||||
* @param format [String] format string
|
||||
* @return [String] formatted output
|
||||
*
|
||||
* @example
|
||||
* KernAux.sprintf 'foo%*scar%d', 5, 'bar', 123
|
||||
* #=> "foo barcar123"
|
||||
*/
|
||||
static VALUE rb_KernAux_sprintf(int argc, VALUE *argv, VALUE self);
|
||||
|
||||
void init_printf()
|
||||
{
|
||||
rb_define_singleton_method(rb_KernAux, "sprintf", rb_KernAux_sprintf, -1);
|
||||
}
|
||||
|
||||
#define TAKE_ARG \
|
||||
if (arg_index >= argc) rb_raise(rb_eArgError, "too few arguments"); \
|
||||
VALUE arg_rb = argv[arg_index++]; \
|
||||
do {} while (0)
|
||||
|
||||
VALUE rb_KernAux_sprintf(const int argc, VALUE *const argv, VALUE self)
|
||||
{
|
||||
if (argc == 0) rb_raise(rb_eArgError, "too few arguments");
|
||||
|
||||
const char *format = StringValueCStr(argv[0]);
|
||||
int arg_index = 1;
|
||||
VALUE result = rb_str_new_literal("");
|
||||
|
||||
while (*format) {
|
||||
if (*format != '%') {
|
||||
rb_str_cat(result, format, 1);
|
||||
++format;
|
||||
continue;
|
||||
}
|
||||
|
||||
++format;
|
||||
struct KernAux_PrintfFmt_Spec spec =
|
||||
KernAux_PrintfFmt_Spec_create_out(&format);
|
||||
|
||||
if (spec.set_width) {
|
||||
TAKE_ARG;
|
||||
KernAux_PrintfFmt_Spec_set_width(&spec, NUM2INT(arg_rb));
|
||||
}
|
||||
if (spec.set_precision) {
|
||||
TAKE_ARG;
|
||||
KernAux_PrintfFmt_Spec_set_precision(&spec, NUM2INT(arg_rb));
|
||||
}
|
||||
|
||||
struct DynArg dynarg = DynArg_create();
|
||||
|
||||
if (spec.type == KERNAUX_PRINTF_FMT_TYPE_INT) {
|
||||
TAKE_ARG;
|
||||
DynArg_use_long_long(&dynarg, NUM2LL(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_UINT) {
|
||||
TAKE_ARG;
|
||||
DynArg_use_unsigned_long_long(&dynarg, NUM2ULL(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_FLOAT ||
|
||||
spec.type == KERNAUX_PRINTF_FMT_TYPE_EXP)
|
||||
{
|
||||
TAKE_ARG;
|
||||
DynArg_use_double(&dynarg, NUM2DBL(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_CHAR) {
|
||||
TAKE_ARG;
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
DynArg_use_char(&dynarg, *StringValuePtr(arg_rb));
|
||||
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_STR) {
|
||||
TAKE_ARG;
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
DynArg_use_str(&dynarg, StringValueCStr(arg_rb));
|
||||
}
|
||||
|
||||
// 1 additional byte for the '%' character.
|
||||
// 1 additional byte for the terminating '\0' character.
|
||||
char old_format[2 + spec.format_limit - spec.format_start];
|
||||
memset(old_format, '\0', sizeof(old_format));
|
||||
old_format[0] = '%';
|
||||
strncpy(&old_format[1], spec.format_start, sizeof(old_format) - 2);
|
||||
|
||||
char buffer[BUFFER_SIZE];
|
||||
int slen;
|
||||
|
||||
if (spec.set_width) {
|
||||
if (spec.set_precision) {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, spec.precision,
|
||||
dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, spec.precision,
|
||||
dynarg.arg);
|
||||
}
|
||||
} else {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.width, dynarg.arg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (spec.set_precision) {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.precision, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format,
|
||||
spec.precision, dynarg.arg);
|
||||
}
|
||||
} else {
|
||||
if (dynarg.use_dbl) {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.dbl);
|
||||
} else {
|
||||
slen = kernaux_snprintf(buffer, BUFFER_SIZE, old_format, dynarg.arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rb_str_cat(result, buffer, slen);
|
||||
}
|
||||
|
||||
if (arg_index < argc) rb_raise(rb_eArgError, "too many arguments");
|
||||
|
||||
return rb_funcall(result, rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
|
@ -0,0 +1,47 @@
|
|||
#include "main.h"
|
||||
|
||||
static VALUE rb_KernAux_Version_with_cmdlineQN(VALUE self);
|
||||
static VALUE rb_KernAux_Version_with_ntoaQN(VALUE self);
|
||||
static VALUE rb_KernAux_Version_with_printfQN(VALUE self);
|
||||
|
||||
static VALUE rb_KernAux_Version = Qnil;
|
||||
|
||||
void init_version()
|
||||
{
|
||||
rb_gc_register_mark_object(rb_KernAux_Version =
|
||||
rb_define_module_under(rb_KernAux, "Version"));
|
||||
|
||||
rb_define_singleton_method(rb_KernAux_Version, "with_cmdline?",
|
||||
rb_KernAux_Version_with_cmdlineQN, 0);
|
||||
rb_define_singleton_method(rb_KernAux_Version, "with_ntoa?",
|
||||
rb_KernAux_Version_with_ntoaQN, 0);
|
||||
rb_define_singleton_method(rb_KernAux_Version, "with_printf?",
|
||||
rb_KernAux_Version_with_printfQN, 0);
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_Version_with_cmdlineQN(VALUE self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_CMDLINE
|
||||
return Qtrue;
|
||||
#else
|
||||
return Qfalse;
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_Version_with_ntoaQN(VALUE self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_NTOA
|
||||
return Qtrue;
|
||||
#else
|
||||
return Qfalse;
|
||||
#endif
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_Version_with_printfQN(VALUE self)
|
||||
{
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
return Qtrue;
|
||||
#else
|
||||
return Qfalse;
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'lib/kernaux/version'
|
||||
|
||||
Gem::Specification.new do |spec|
|
||||
repo = 'https://github.com/tailix/libkernaux'
|
||||
home = "#{repo}/tree/master/bindings/ruby"
|
||||
bugs = "#{repo}/issues"
|
||||
docs = "https://www.rubydoc.info/gems/kernaux/#{KernAux::VERSION}"
|
||||
|
||||
spec.name = 'kernaux'
|
||||
spec.version = KernAux::VERSION
|
||||
spec.license = 'MIT'
|
||||
spec.homepage = home
|
||||
spec.platform = Gem::Platform::RUBY
|
||||
|
||||
spec.required_ruby_version = '~> 3.0'
|
||||
|
||||
spec.authors = ['Alex Kotov']
|
||||
spec.email = %w[kotovalexarian@gmail.com]
|
||||
|
||||
spec.summary =
|
||||
'Binding to libkernaux - auxiliary library for kernel development'
|
||||
|
||||
spec.description = <<~DESCRIPTION.split("\n").map(&:strip).join ' '
|
||||
Binding to libkernaux - auxiliary library for kernel development.
|
||||
DESCRIPTION
|
||||
|
||||
spec.metadata['rubygems_mfa_required'] = 'true'
|
||||
spec.metadata['homepage_uri'] = home
|
||||
spec.metadata['source_code_uri'] = home
|
||||
spec.metadata['bug_tracker_uri'] = bugs
|
||||
spec.metadata['documentation_uri'] = docs
|
||||
|
||||
spec.bindir = 'exe'
|
||||
spec.require_paths = ['lib']
|
||||
|
||||
spec.files = Dir.chdir File.expand_path __dir__ do
|
||||
`git ls-files -z`.split("\x0").reject do |f|
|
||||
f.match %r{\A(?:test|spec|features)/}
|
||||
end
|
||||
end
|
||||
|
||||
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename f }
|
||||
|
||||
spec.extensions << 'ext/default/extconf.rb'
|
||||
|
||||
spec.add_development_dependency 'bundler', '~> 2.2'
|
||||
spec.add_development_dependency 'pry', '~> 0.14'
|
||||
spec.add_development_dependency 'rake', '~> 13.0'
|
||||
spec.add_development_dependency 'rake-compiler', '~> 1.1'
|
||||
spec.add_development_dependency 'rspec', '~> 3.10'
|
||||
spec.add_development_dependency 'rubocop', '~> 1.25'
|
||||
spec.add_development_dependency 'rubocop-performance', '~> 1.13'
|
||||
spec.add_development_dependency 'rubocop-rake', '~> 0.6'
|
||||
spec.add_development_dependency 'rubocop-rspec', '~> 2.7'
|
||||
spec.add_development_dependency 'simplecov', '~> 0.21'
|
||||
spec.add_development_dependency 'yard', '~> 0.9'
|
||||
end
|
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
##
|
||||
# Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
|
||||
# library for kernel development.
|
||||
#
|
||||
module KernAux
|
||||
end
|
||||
|
||||
require_relative 'kernaux/version'
|
||||
|
||||
# Native extensions
|
||||
require_relative 'kernaux/default'
|
||||
|
||||
# Non-standard directory structure
|
||||
require_relative 'kernaux/assert'
|
||||
require_relative 'kernaux/cmdline'
|
||||
require_relative 'kernaux/errors'
|
||||
require_relative 'kernaux/ntoa'
|
|
@ -0,0 +1,58 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/Documentation
|
||||
begin; end
|
||||
|
||||
module KernAux
|
||||
# Default callback for assertions.
|
||||
# @see .assert_cb
|
||||
DEFAULT_ASSERT_CB = @assert_cb = lambda { |file, line, msg|
|
||||
raise AssertError, "#{file}:#{line}:#{msg}"
|
||||
}
|
||||
|
||||
# @!scope class
|
||||
|
||||
##
|
||||
# @!attribute [rw] assert_cb
|
||||
# Panic callback.
|
||||
#
|
||||
# @return [Proc]
|
||||
#
|
||||
# @api unsafe
|
||||
#
|
||||
# @see .panic
|
||||
# @see .assert_do
|
||||
##
|
||||
|
||||
##
|
||||
# Raise assertion with implicit file and line, retrieved from `caller`, and
|
||||
# explicit message.
|
||||
#
|
||||
# @param msg [String] any message
|
||||
# @return [nil]
|
||||
#
|
||||
# @raise [AssertError] if {.assert_cb} have not been changed
|
||||
#
|
||||
# @see .assert_do Explicit file and line.
|
||||
#
|
||||
def self.panic(msg)
|
||||
file, line = caller(1..1).first.split(':')[0..1]
|
||||
assert_do file, Integer(line), msg
|
||||
end
|
||||
|
||||
##
|
||||
# @!method assert_do(file, line, msg)
|
||||
# Raise assertion with explicit file, line and message.
|
||||
#
|
||||
# @param file [String] file name, usually from `__FILE__`
|
||||
# @param line [Integer] line number, usually from `__LINE__`
|
||||
# @param msg [String] any message
|
||||
# @return [nil]
|
||||
#
|
||||
# @raise [AssertError] if {.assert_cb} have not been changed
|
||||
#
|
||||
# @see .panic Implicit file and line
|
||||
##
|
||||
end
|
||||
|
||||
# rubocop:enable Style/Documentation
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/Documentation
|
||||
begin; end
|
||||
|
||||
module KernAux
|
||||
# @!scope class
|
||||
|
||||
##
|
||||
# @!method cmdline(str)
|
||||
# Parse command line.
|
||||
#
|
||||
# @param str [String] command line string
|
||||
# @return [Array<String>] command line arguments
|
||||
#
|
||||
# @raise [CmdlineError] syntax is invalid
|
||||
##
|
||||
end
|
||||
|
||||
# rubocop:enable Style/Documentation
|
|
@ -0,0 +1,39 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module KernAux
|
||||
##
|
||||
# Our base class for runtime errors.
|
||||
#
|
||||
class Error < RuntimeError; end
|
||||
|
||||
##
|
||||
# Raised when assertion has failed or panic has been called.
|
||||
#
|
||||
# @see .panic
|
||||
# @see .assert_do
|
||||
#
|
||||
class AssertError < Error; end
|
||||
|
||||
##
|
||||
# Raised when command line parsing goes wrong.
|
||||
#
|
||||
# @see .cmdline
|
||||
#
|
||||
class CmdlineError < Error; end
|
||||
|
||||
##
|
||||
# Raised when integer base is invalid.
|
||||
#
|
||||
# @see .utoa
|
||||
# @see .itoa
|
||||
#
|
||||
class InvalidNtoaBaseError < Error; end
|
||||
|
||||
##
|
||||
# Raised when prefix is too long.
|
||||
#
|
||||
# @see .utoa
|
||||
# @see .itoa
|
||||
#
|
||||
class TooLongNtoaPrefixError < Error; end
|
||||
end
|
|
@ -0,0 +1,136 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/Documentation
|
||||
begin; end
|
||||
|
||||
module KernAux
|
||||
# @!scope class
|
||||
|
||||
##
|
||||
# @!method utoa(number, base, prefix)
|
||||
# Convert `uint64_t` to a string in multiple numeral systems.
|
||||
#
|
||||
# Base can be a positive or negative integer between 2 and 36, or a symbol
|
||||
# which is an alias to a valid integer value. Positive integers and lowercase
|
||||
# symbols mean lowercase output when base is greater than 10. Negative
|
||||
# integers and uppercase symbols mean uppercase output when base is greater
|
||||
# than 10. Aliases are: `:b`, `:B` - 2; `:o`, `:O` - 8; `:d`, `:D` - 10; `:h`,
|
||||
# `:x` - 16 (lowercase); `:H`, `:X` - -10 (uppercase).
|
||||
#
|
||||
# @param number [Integer] a number between 0 and `UINT64_MAX`
|
||||
# @param base [Integer, Symbol] base of a numeral system
|
||||
# @param prefix [nil, String] string to put before a number
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
# @raise [InvalidNtoaBaseError] base is invalid
|
||||
# @raise [TooLongNtoaPrefixError] prefix is too long
|
||||
#
|
||||
# @see .itoa Convert signed integers
|
||||
##
|
||||
|
||||
##
|
||||
# @!method itoa(number, base, prefix)
|
||||
# Convert `int64_t` to a string in multiple numeral systems.
|
||||
#
|
||||
# Base can be a positive or negative integer between 2 and 36, or a symbol
|
||||
# which is an alias to a valid integer value. Positive integers and lowercase
|
||||
# symbols mean lowercase output when base is greater than 10. Negative
|
||||
# integers and uppercase symbols mean uppercase output when base is greater
|
||||
# than 10. Aliases are: `:b`, `:B` - 2; `:o`, `:O` - 8; `:d`, `:D` - 10; `:h`,
|
||||
# `:x` - 16 (lowercase); `:H`, `:X` - -10 (uppercase).
|
||||
#
|
||||
# @param number [Integer] a number between `INT64_MIN` and `INT64_MAX`
|
||||
# @param base [Integer, Symbol] base of a numeral system
|
||||
# @param prefix [nil, String] string to put between a sign and a number
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
# @raise [InvalidNtoaBaseError] base is invalid
|
||||
# @raise [TooLongNtoaPrefixError] prefix is too long
|
||||
#
|
||||
# @see .utoa Convert unsigned integers
|
||||
##
|
||||
|
||||
##
|
||||
# @!method utoa2(number)
|
||||
# Convert `uint64_t` to a binary string.
|
||||
#
|
||||
# @param number [Integer] a number between 0 and `UINT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method itoa2(number)
|
||||
# Convert `int64_t` to a binary string.
|
||||
#
|
||||
# @param number [Integer] a number between `INT64_MIN` and `INT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method utoa8(number)
|
||||
# Convert `uint64_t` to a octal string.
|
||||
#
|
||||
# @param number [Integer] a number between 0 and `UINT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method itoa8(number)
|
||||
# Convert `int64_t` to a octal string.
|
||||
#
|
||||
# @param number [Integer] a number between `INT64_MIN` and `INT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method utoa10(number)
|
||||
# Convert `uint64_t` to a decimal string.
|
||||
#
|
||||
# @param number [Integer] a number between 0 and `UINT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method itoa10(number)
|
||||
# Convert `int64_t` to a decimal string.
|
||||
#
|
||||
# @param number [Integer] a number between `INT64_MIN` and `INT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method utoa16(number)
|
||||
# Convert `uint64_t` to a hexadecimal string.
|
||||
#
|
||||
# @param number [Integer] a number between 0 and `UINT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
|
||||
##
|
||||
# @!method itoa16(number)
|
||||
# Convert `int64_t` to a hexadecimal string.
|
||||
#
|
||||
# @param number [Integer] a number between `INT64_MIN` and `INT64_MAX`
|
||||
# @return [String]
|
||||
#
|
||||
# @raise [RangeError] number is out of range
|
||||
##
|
||||
end
|
||||
|
||||
# rubocop:enable Style/Documentation
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module KernAux
|
||||
# Gem version.
|
||||
VERSION = '0.7.0'
|
||||
|
||||
##
|
||||
# This module includes functions to determine if specific features are
|
||||
# supported by the library.
|
||||
#
|
||||
module Version
|
||||
end
|
||||
end
|
|
@ -0,0 +1,32 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe KernAux, '.panic' do
|
||||
subject(:panic) { described_class.panic msg }
|
||||
|
||||
let(:msg) { 'Hello, World!' }
|
||||
|
||||
specify do
|
||||
expect { panic }.to \
|
||||
raise_error described_class::AssertError, /\A#{__FILE__}:\d+:#{msg}\z/
|
||||
end
|
||||
|
||||
context 'when custom assertion callback has beed set' do
|
||||
around do |example|
|
||||
described_class.assert_cb = lambda { |file, line, msg|
|
||||
raise "file: #{file.inspect}, line: #{line}, msg: #{msg.inspect}"
|
||||
}
|
||||
example.run
|
||||
ensure
|
||||
described_class.assert_cb = described_class::DEFAULT_ASSERT_CB
|
||||
end
|
||||
|
||||
specify do
|
||||
expect { panic }.to raise_error(
|
||||
RuntimeError,
|
||||
/\Afile: #{__FILE__.inspect}, line: \d+, msg: #{msg.inspect}\z/,
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,115 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_cmdline? and RSpec.describe KernAux, '.cmdline' do
|
||||
subject(:cmdline) { described_class.cmdline str }
|
||||
|
||||
let(:str) { 'foo bar\\ baz "car cdr"' }
|
||||
|
||||
it { is_expected.to be_instance_of Array }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to all be_instance_of String }
|
||||
it { is_expected.to all be_frozen }
|
||||
it { is_expected.to eq ['foo', 'bar baz', 'car cdr'] }
|
||||
|
||||
context 'when str is empty' do
|
||||
let(:str) { '' }
|
||||
|
||||
it { is_expected.to eq [] }
|
||||
end
|
||||
|
||||
context 'when str has invalid type' do
|
||||
let(:str) { 123 }
|
||||
|
||||
specify { expect { cmdline }.to raise_error TypeError }
|
||||
end
|
||||
|
||||
context 'when str has EOL after backslash' do
|
||||
let(:str) { '\\' }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to \
|
||||
raise_error described_class::CmdlineError, 'EOL after backslash'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when str has EOL after backslash inside quote' do
|
||||
let(:str) { '"\\' }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to raise_error \
|
||||
described_class::CmdlineError, 'EOL after backslash inside quote'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when str has unescaped quotation mark' do
|
||||
let(:str) { 'foo"' }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to \
|
||||
raise_error described_class::CmdlineError, 'unescaped quotation mark'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when str has EOL inside quote' do
|
||||
let(:str) { '"' }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to \
|
||||
raise_error described_class::CmdlineError, 'EOL inside quote'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are not too many args' do
|
||||
let(:str) { 'a ' * 256 }
|
||||
|
||||
it { is_expected.to eq ['a'] * 256 }
|
||||
end
|
||||
|
||||
context 'when there are too many args' do
|
||||
let(:str) { 'a ' * 257 }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to \
|
||||
raise_error described_class::CmdlineError, 'too many args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when args don\'t cause buffer overflow' do
|
||||
let(:str) { 'a' * 4095 }
|
||||
|
||||
it { is_expected.to eq ['a' * 4095] }
|
||||
end
|
||||
|
||||
context 'when args cause buffer overflow' do
|
||||
let(:str) { 'a' * 4096 }
|
||||
|
||||
specify do
|
||||
expect { cmdline }.to \
|
||||
raise_error described_class::CmdlineError, 'EOF or buffer overflow'
|
||||
end
|
||||
end
|
||||
|
||||
context 'using fixtures' do
|
||||
cmdline_yml =
|
||||
File.expand_path('../../../../../fixtures/cmdline.yml', __dir__)
|
||||
|
||||
YAML.safe_load_file(cmdline_yml).each do |test|
|
||||
escape_str = lambda do |str|
|
||||
eval "\"#{str}\"", binding, __FILE__, __LINE__ # "str"
|
||||
end
|
||||
|
||||
cmdline = escape_str.call test['cmdline']
|
||||
arg_count_max = test['arg_count_max']
|
||||
buffer_size = test['buffer_size']
|
||||
result = test['result']&.map(&escape_str)
|
||||
|
||||
next unless arg_count_max.nil? && buffer_size.nil? && !result.nil?
|
||||
|
||||
it "transforms #{cmdline.inspect} to #{result.inspect}" do
|
||||
expect(described_class.cmdline(cmdline)).to eq result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.itoa10' do
|
||||
subject(:itoa10) { described_class.itoa10 number }
|
||||
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0' }
|
||||
end
|
||||
|
||||
context 'when number is 1' do
|
||||
let(:number) { 1 }
|
||||
|
||||
it { is_expected.to eq '1' }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
it { is_expected.to eq '-1' }
|
||||
end
|
||||
|
||||
context 'when number is min int64_t' do
|
||||
let(:number) { -2**63 }
|
||||
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when number is max int64_t' do
|
||||
let(:number) { 2**63 - 1 }
|
||||
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when number is lesser than min uint64_t' do
|
||||
let(:number) { -2**63 - 1 }
|
||||
|
||||
specify do
|
||||
expect { itoa10 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**63 }
|
||||
|
||||
specify do
|
||||
expect { itoa10 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { itoa10 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.itoa16' do
|
||||
subject(:itoa16) { described_class.itoa16 number }
|
||||
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)) }
|
||||
|
||||
def sign = number < 0 ? '-' : ''
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{sign}0x#{number.abs.to_s(16)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0x0' }
|
||||
end
|
||||
|
||||
context 'when number is 1' do
|
||||
let(:number) { 1 }
|
||||
|
||||
it { is_expected.to eq '0x1' }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
it { is_expected.to eq '-0x1' }
|
||||
end
|
||||
|
||||
context 'when number is min int64_t' do
|
||||
let(:number) { -2**63 }
|
||||
|
||||
it { is_expected.to eq "-0x#{number.abs.to_s(16)}" }
|
||||
end
|
||||
|
||||
context 'when number is max int64_t' do
|
||||
let(:number) { 2**63 - 1 }
|
||||
|
||||
it { is_expected.to eq "0x#{number.to_s(16)}" }
|
||||
end
|
||||
|
||||
context 'when number is lesser than min uint64_t' do
|
||||
let(:number) { -2**63 - 1 }
|
||||
|
||||
specify do
|
||||
expect { itoa16 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**63 }
|
||||
|
||||
specify do
|
||||
expect { itoa16 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { itoa16 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.itoa2' do
|
||||
subject(:itoa2) { described_class.itoa2 number }
|
||||
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)) }
|
||||
|
||||
def sign = number < 0 ? '-' : ''
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{sign}0b#{number.abs.to_s(2)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0b0' }
|
||||
end
|
||||
|
||||
context 'when number is 1' do
|
||||
let(:number) { 1 }
|
||||
|
||||
it { is_expected.to eq '0b1' }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
it { is_expected.to eq '-0b1' }
|
||||
end
|
||||
|
||||
context 'when number is min int64_t' do
|
||||
let(:number) { -2**63 }
|
||||
|
||||
it { is_expected.to eq "-0b#{number.abs.to_s(2)}" }
|
||||
end
|
||||
|
||||
context 'when number is max int64_t' do
|
||||
let(:number) { 2**63 - 1 }
|
||||
|
||||
it { is_expected.to eq "0b#{number.to_s(2)}" }
|
||||
end
|
||||
|
||||
context 'when number is lesser than min uint64_t' do
|
||||
let(:number) { -2**63 - 1 }
|
||||
|
||||
specify do
|
||||
expect { itoa2 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**63 }
|
||||
|
||||
specify do
|
||||
expect { itoa2 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { itoa2 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,72 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.itoa8' do
|
||||
subject(:itoa8) { described_class.itoa8 number }
|
||||
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)) }
|
||||
|
||||
def sign = number < 0 ? '-' : ''
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{sign}0o#{number.abs.to_s(8)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0o0' }
|
||||
end
|
||||
|
||||
context 'when number is 1' do
|
||||
let(:number) { 1 }
|
||||
|
||||
it { is_expected.to eq '0o1' }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
it { is_expected.to eq '-0o1' }
|
||||
end
|
||||
|
||||
context 'when number is min int64_t' do
|
||||
let(:number) { -2**63 }
|
||||
|
||||
it { is_expected.to eq "-0o#{number.abs.to_s(8)}" }
|
||||
end
|
||||
|
||||
context 'when number is max int64_t' do
|
||||
let(:number) { 2**63 - 1 }
|
||||
|
||||
it { is_expected.to eq "0o#{number.to_s(8)}" }
|
||||
end
|
||||
|
||||
context 'when number is lesser than min uint64_t' do
|
||||
let(:number) { -2**63 - 1 }
|
||||
|
||||
specify do
|
||||
expect { itoa8 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**63 }
|
||||
|
||||
specify do
|
||||
expect { itoa8 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { itoa8 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,245 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.itoa' do
|
||||
subject(:itoa) { described_class.itoa number, base, prefix }
|
||||
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)) }
|
||||
let(:base) { rand 2..36 }
|
||||
let(:prefix) { [nil, ''].sample }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq '0' }
|
||||
end
|
||||
|
||||
context 'when number is 1' do
|
||||
let(:number) { 1 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq '1' }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq '-1' }
|
||||
end
|
||||
|
||||
context 'when number is min int64_t' do
|
||||
let(:number) { -2**63 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when number is max int64_t' do
|
||||
let(:number) { 2**63 - 1 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when number is lesser than min uint64_t' do
|
||||
let(:number) { -2**63 - 1 }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**63 }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand((-2**63)..(2**63 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base is negative' do
|
||||
let(:base) { -rand(2..36) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(-base).upcase }
|
||||
end
|
||||
|
||||
context 'when base is :b' do
|
||||
let(:base) { :b }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 2 }
|
||||
end
|
||||
|
||||
context 'when base is :B:' do
|
||||
let(:base) { :B }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 2 }
|
||||
end
|
||||
|
||||
context 'when base is :o' do
|
||||
let(:base) { :o }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 8 }
|
||||
end
|
||||
|
||||
context 'when base is :O' do
|
||||
let(:base) { :O }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 8 }
|
||||
end
|
||||
|
||||
context 'when base is :d' do
|
||||
let(:base) { :d }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when base is :D' do
|
||||
let(:base) { :D }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when base is :h' do
|
||||
let(:base) { :h }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 16 }
|
||||
end
|
||||
|
||||
context 'when base is :x' do
|
||||
let(:base) { :x }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 16 }
|
||||
end
|
||||
|
||||
context 'when base is :H' do
|
||||
let(:base) { :H }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(16).upcase }
|
||||
end
|
||||
|
||||
context 'when base is :X' do
|
||||
let(:base) { :X }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(16).upcase }
|
||||
end
|
||||
|
||||
context 'when base is an invalid symbol' do
|
||||
let(:base) { :foo }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to \
|
||||
raise_error described_class::InvalidNtoaBaseError, 'invalid base'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no prefix is given' do
|
||||
subject(:itoa) { described_class.itoa number, base }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is nil' do
|
||||
let(:prefix) { nil }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is empty' do
|
||||
let(:prefix) { '' }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is present' do
|
||||
let(:prefix) { 'foo' }
|
||||
|
||||
def sign = number < 0 ? '-' : ''
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{sign}foo#{number.abs.to_s(base)}" }
|
||||
end
|
||||
|
||||
context 'when prefix is not a string' do
|
||||
let(:prefix) { 123 }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to raise_error(
|
||||
TypeError,
|
||||
"no implicit conversion of #{prefix.class} into String",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prefix has max length' do
|
||||
let(:prefix) { 'a' * 100 }
|
||||
|
||||
def sign = number < 0 ? '-' : ''
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{sign}#{prefix}#{number.abs.to_s(base)}" }
|
||||
end
|
||||
|
||||
context 'when prefix is too long' do
|
||||
let(:prefix) { 'a' * 101 }
|
||||
|
||||
specify do
|
||||
expect { itoa }.to raise_error(
|
||||
described_class::TooLongNtoaPrefixError,
|
||||
"prefix length #{prefix.length} is too long",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.utoa10' do
|
||||
subject(:utoa10) { described_class.utoa10 number }
|
||||
|
||||
let(:number) { rand 0..(2**64 - 1) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0' }
|
||||
end
|
||||
|
||||
context 'when number is max uint64_t' do
|
||||
let(:number) { 2**64 - 1 }
|
||||
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
specify do
|
||||
expect { utoa10 }.to \
|
||||
raise_error RangeError, 'can\'t convert negative number to uint64_t'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**64 }
|
||||
|
||||
specify do
|
||||
expect { utoa10 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `unsigned long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand(0..(2**64 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { utoa10 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.utoa16' do
|
||||
subject(:utoa16) { described_class.utoa16 number }
|
||||
|
||||
let(:number) { rand 0..(2**64 - 1) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "0x#{number.to_s(16)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0x0' }
|
||||
end
|
||||
|
||||
context 'when number is max uint64_t' do
|
||||
let(:number) { 2**64 - 1 }
|
||||
|
||||
it { is_expected.to eq "0x#{number.to_s(16)}" }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
specify do
|
||||
expect { utoa16 }.to \
|
||||
raise_error RangeError, 'can\'t convert negative number to uint64_t'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**64 }
|
||||
|
||||
specify do
|
||||
expect { utoa16 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `unsigned long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand(0..(2**64 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { utoa16 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.utoa2' do
|
||||
subject(:utoa2) { described_class.utoa2 number }
|
||||
|
||||
let(:number) { rand 0..(2**64 - 1) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "0b#{number.to_s(2)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0b0' }
|
||||
end
|
||||
|
||||
context 'when number is max uint64_t' do
|
||||
let(:number) { 2**64 - 1 }
|
||||
|
||||
it { is_expected.to eq "0b#{number.to_s(2)}" }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
specify do
|
||||
expect { utoa2 }.to \
|
||||
raise_error RangeError, 'can\'t convert negative number to uint64_t'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**64 }
|
||||
|
||||
specify do
|
||||
expect { utoa2 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `unsigned long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand(0..(2**64 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { utoa2 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,52 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.utoa8' do
|
||||
subject(:utoa8) { described_class.utoa8 number }
|
||||
|
||||
let(:number) { rand 0..(2**64 - 1) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "0o#{number.to_s(8)}" }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to eq '0o0' }
|
||||
end
|
||||
|
||||
context 'when number is max uint64_t' do
|
||||
let(:number) { 2**64 - 1 }
|
||||
|
||||
it { is_expected.to eq "0o#{number.to_s(8)}" }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
specify do
|
||||
expect { utoa8 }.to \
|
||||
raise_error RangeError, 'can\'t convert negative number to uint64_t'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**64 }
|
||||
|
||||
specify do
|
||||
expect { utoa8 }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `unsigned long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand(0..(2**64 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { utoa8 }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,217 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_ntoa? and RSpec.describe KernAux, '.utoa' do
|
||||
subject(:utoa) { described_class.utoa number, base, prefix }
|
||||
|
||||
let(:number) { rand 0..(2**64 - 1) }
|
||||
let(:base) { rand 2..36 }
|
||||
let(:prefix) { [nil, ''].sample }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
|
||||
context 'when number is 0' do
|
||||
let(:number) { 0 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq '0' }
|
||||
end
|
||||
|
||||
context 'when number is max uint64_t' do
|
||||
let(:number) { 2**64 - 1 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when number is -1' do
|
||||
let(:number) { -1 }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to \
|
||||
raise_error RangeError, 'can\'t convert negative number to uint64_t'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is greater than max uint64_t' do
|
||||
let(:number) { 2**64 }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to raise_error \
|
||||
RangeError, 'bignum too big to convert into `unsigned long long\''
|
||||
end
|
||||
end
|
||||
|
||||
context 'when number is not numeric' do
|
||||
let(:number) { rand(0..(2**64 - 1)).to_s }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to raise_error \
|
||||
TypeError, 'no implicit conversion from string'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when base is negative' do
|
||||
let(:base) { -rand(2..36) }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(-base).upcase }
|
||||
end
|
||||
|
||||
context 'when base is :b' do
|
||||
let(:base) { :b }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 2 }
|
||||
end
|
||||
|
||||
context 'when base is :B:' do
|
||||
let(:base) { :B }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 2 }
|
||||
end
|
||||
|
||||
context 'when base is :o' do
|
||||
let(:base) { :o }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 8 }
|
||||
end
|
||||
|
||||
context 'when base is :O' do
|
||||
let(:base) { :O }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 8 }
|
||||
end
|
||||
|
||||
context 'when base is :d' do
|
||||
let(:base) { :d }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when base is :D' do
|
||||
let(:base) { :D }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s }
|
||||
end
|
||||
|
||||
context 'when base is :h' do
|
||||
let(:base) { :h }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 16 }
|
||||
end
|
||||
|
||||
context 'when base is :x' do
|
||||
let(:base) { :x }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s 16 }
|
||||
end
|
||||
|
||||
context 'when base is :H' do
|
||||
let(:base) { :H }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(16).upcase }
|
||||
end
|
||||
|
||||
context 'when base is :X' do
|
||||
let(:base) { :X }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s(16).upcase }
|
||||
end
|
||||
|
||||
context 'when base is an invalid symbol' do
|
||||
let(:base) { :foo }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to \
|
||||
raise_error described_class::InvalidNtoaBaseError, 'invalid base'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when no prefix is given' do
|
||||
subject(:utoa) { described_class.utoa number, base }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is nil' do
|
||||
let(:prefix) { nil }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is empty' do
|
||||
let(:prefix) { '' }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq number.to_s base }
|
||||
end
|
||||
|
||||
context 'when prefix is present' do
|
||||
let(:prefix) { 'foo' }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{prefix}#{number.to_s(base)}" }
|
||||
end
|
||||
|
||||
context 'when prefix is not a string' do
|
||||
let(:prefix) { 123 }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to raise_error(
|
||||
TypeError,
|
||||
"no implicit conversion of #{prefix.class} into String",
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when prefix has max length' do
|
||||
let(:prefix) { 'a' * 100 }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq "#{prefix}#{number.to_s(base)}" }
|
||||
end
|
||||
|
||||
context 'when prefix is too long' do
|
||||
let(:prefix) { 'a' * 101 }
|
||||
|
||||
specify do
|
||||
expect { utoa }.to raise_error(
|
||||
described_class::TooLongNtoaPrefixError,
|
||||
"prefix length #{prefix.length} is too long",
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_printf? and RSpec.describe KernAux, '.sprintf' do
|
||||
subject :sprintf do
|
||||
described_class.sprintf 'Hello, %s!', 'World'
|
||||
end
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq 'Hello, World!' }
|
||||
|
||||
context 'when there are too many arguments' do
|
||||
[
|
||||
['Hello!', 'World!'],
|
||||
['Hello, %s!', 'World', 'Alex'],
|
||||
].each do |args|
|
||||
it "raises on #{args.inspect}" do
|
||||
expect { described_class.sprintf(*args) }.to \
|
||||
raise_error ArgumentError, 'too many arguments'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are too few arguments' do
|
||||
[
|
||||
[],
|
||||
['Hello, %s!'],
|
||||
['Hello, %*s!', 20],
|
||||
['Hello, %.*s!', 20],
|
||||
['Hello, %*.*s!', 20, 20],
|
||||
].each do |args|
|
||||
it "raises on #{args.inspect}" do
|
||||
expect { described_class.sprintf(*args) }.to \
|
||||
raise_error ArgumentError, 'too few arguments'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
['', 'using regular fixtures'],
|
||||
['_orig', 'using original fixtures'],
|
||||
].each do |(suffix, description)|
|
||||
context description do
|
||||
printf_yml = File.expand_path(
|
||||
"../../../../../fixtures/printf#{suffix}.yml",
|
||||
__dir__,
|
||||
)
|
||||
|
||||
YAML.safe_load_file(printf_yml).each do |test|
|
||||
expected = test['result']
|
||||
|
||||
format = ''
|
||||
args = []
|
||||
|
||||
test['args'].each do |arg|
|
||||
if arg.is_a? String
|
||||
format += arg
|
||||
else
|
||||
format += arg[0]
|
||||
arg[1..].each do |item|
|
||||
if item.is_a? Array
|
||||
if item.length == 1
|
||||
args << item[0]
|
||||
elsif item[0] == 'long long'
|
||||
args << item[1]
|
||||
else
|
||||
raise "Unknown format: #{args.inspect}"
|
||||
end
|
||||
else
|
||||
args << item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "transforms (#{format.inspect}, #{args.inspect[1...-1]}) " \
|
||||
"to #{expected.inspect}" do
|
||||
expect(described_class.sprintf(format, *args)).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe KernAux do
|
||||
describe '::VERSION' do
|
||||
specify do
|
||||
expect(described_class::VERSION).to match(/\A\d+\.\d+\.\d+\z/)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,106 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# This should be on the top of the file.
|
||||
require 'simplecov'
|
||||
|
||||
# This file was generated by the `rspec --init` command. Conventionally, all
|
||||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
||||
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
||||
# this file to always be loaded, without a need to explicitly require it in any
|
||||
# files.
|
||||
#
|
||||
# Given that it is always loaded, you are encouraged to keep this file as
|
||||
# light-weight as possible. Requiring heavyweight dependencies from this file
|
||||
# will add to the boot time of your test suite on EVERY test run, even for an
|
||||
# individual file that may not need all of that loaded. Instead, consider making
|
||||
# a separate helper file that requires the additional dependencies and performs
|
||||
# the additional setup, and require it from the spec files that actually need
|
||||
# it.
|
||||
#
|
||||
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||
|
||||
require 'kernaux'
|
||||
|
||||
require 'yaml'
|
||||
|
||||
RSpec.configure do |config|
|
||||
# rspec-expectations config goes here. You can use an alternate
|
||||
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||
# assertions if you prefer.
|
||||
config.expect_with :rspec do |expectations|
|
||||
# This option will default to `true` in RSpec 4. It makes the `description`
|
||||
# and `failure_message` of custom matchers include text for helper methods
|
||||
# defined using `chain`, e.g.:
|
||||
# be_bigger_than(2).and_smaller_than(4).description
|
||||
# # => "be bigger than 2 and smaller than 4"
|
||||
# ...rather than:
|
||||
# # => "be bigger than 2"
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
end
|
||||
|
||||
# rspec-mocks config goes here. You can use an alternate test double
|
||||
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
||||
config.mock_with :rspec do |mocks|
|
||||
# Prevents you from mocking or stubbing a method that does not exist on
|
||||
# a real object. This is generally recommended, and will default to
|
||||
# `true` in RSpec 4.
|
||||
mocks.verify_partial_doubles = true
|
||||
end
|
||||
|
||||
# This option will default to `:apply_to_host_groups` in RSpec 4 (and will
|
||||
# have no way to turn it off -- the option exists only for backwards
|
||||
# compatibility in RSpec 3). It causes shared context metadata to be
|
||||
# inherited by the metadata hash of host groups and examples, rather than
|
||||
# triggering implicit auto-inclusion in groups with matching metadata.
|
||||
config.shared_context_metadata_behavior = :apply_to_host_groups
|
||||
|
||||
# This allows you to limit a spec run to individual examples or groups
|
||||
# you care about by tagging them with `:focus` metadata. When nothing
|
||||
# is tagged with `:focus`, all examples get run. RSpec also provides
|
||||
# aliases for `it`, `describe`, and `context` that include `:focus`
|
||||
# metadata: `fit`, `fdescribe` and `fcontext`, respectively.
|
||||
config.filter_run_when_matching :focus
|
||||
|
||||
# Allows RSpec to persist some state between runs in order to support
|
||||
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
||||
# you configure your source control system to ignore this file.
|
||||
config.example_status_persistence_file_path = 'spec/examples.txt'
|
||||
|
||||
# Limits the available syntax to the non-monkey patched syntax that is
|
||||
# recommended. For more details, see:
|
||||
# - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
|
||||
# - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
||||
# - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
|
||||
config.disable_monkey_patching!
|
||||
|
||||
# This setting enables warnings. It's recommended, but in some cases may
|
||||
# be too noisy due to issues in dependencies.
|
||||
config.warnings = true
|
||||
|
||||
# Many RSpec users commonly either run the entire suite or an individual
|
||||
# file, and it's useful to allow more verbose output when running an
|
||||
# individual spec file.
|
||||
if config.files_to_run.one?
|
||||
# Use the documentation formatter for detailed output,
|
||||
# unless a formatter has already been configured
|
||||
# (e.g. via a command-line flag).
|
||||
config.default_formatter = 'doc'
|
||||
end
|
||||
|
||||
# Print the 10 slowest examples and example groups at the
|
||||
# end of the spec run, to help surface which specs are running
|
||||
# particularly slow.
|
||||
config.profile_examples = 10
|
||||
|
||||
# Run specs in random order to surface order dependencies. If you find an
|
||||
# order dependency and want to debug it, you can fix the order by providing
|
||||
# the seed, which is printed after each run.
|
||||
# --seed 1234
|
||||
config.order = :random
|
||||
|
||||
# Seed global randomization in this process using the `--seed` CLI option.
|
||||
# Setting this allows you to use `--seed` to deterministically reproduce
|
||||
# test failures related to randomization by passing the same `--seed` value
|
||||
# as the one that triggered the failure.
|
||||
Kernel.srand config.seed
|
||||
end
|
|
@ -0,0 +1,2 @@
|
|||
/Cargo.lock
|
||||
/target/
|
|
@ -0,0 +1,5 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"kernaux",
|
||||
"kernaux-sys",
|
||||
]
|
|
@ -0,0 +1,23 @@
|
|||
[package]
|
||||
name = "kernaux-sys"
|
||||
version = "0.7.0"
|
||||
authors = ["Alex Kotov <kotovalexarian@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "Unsafe no-std binding to libkernaux - auxiliary library for kernel development"
|
||||
readme = true
|
||||
homepage = "https://github.com/tailix/libkernaux/tree/master/bindings/rust"
|
||||
repository = "https://github.com/tailix/libkernaux/tree/master/bindings/rust"
|
||||
license = "MIT"
|
||||
keywords = ["ffi", "no_std", "no-std", "embedded", "bindings"]
|
||||
categories = ["embedded", "external-ffi-bindings", "no-std", "parsing"]
|
||||
publish = true
|
||||
|
||||
[features]
|
||||
default = ["cmdline", "ntoa"]
|
||||
cmdline = []
|
||||
ntoa = []
|
||||
|
||||
[dependencies.libc]
|
||||
version = "0.2.113"
|
||||
default-features = false
|
||||
features = ["extra_traits"]
|
|
@ -0,0 +1,8 @@
|
|||
kernaux_sys
|
||||
===========
|
||||
|
||||
[![Build status](https://github.com/tailix/libkernaux/actions/workflows/rust.yml/badge.svg)](https://github.com/tailix/libkernaux/actions/workflows/rust.yml)
|
||||
[![Build status (FreeBSD)](https://api.cirrus-ci.com/github/tailix/libkernaux.svg?task=Rust%20(FreeBSD))](https://cirrus-ci.com/github/tailix/libkernaux)
|
||||
|
||||
Unsafe no-std binding to [libkernaux](https://github.com/tailix/libkernaux) -
|
||||
auxiliary library for kernel development.
|
|
@ -0,0 +1,67 @@
|
|||
use libc::{c_char, c_int};
|
||||
|
||||
pub type AssertCB = unsafe extern "C" fn(*const c_char, c_int, *const c_char);
|
||||
|
||||
#[link(name = "kernaux")]
|
||||
extern "C" {
|
||||
#[link_name = "kernaux_assert_do"]
|
||||
pub fn assert_do(file: *const c_char, line: c_int, msg: *const c_char);
|
||||
|
||||
#[link_name = "kernaux_assert_cb"]
|
||||
pub static mut assert_cb: Option<AssertCB>;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr::null;
|
||||
|
||||
static mut LAST_FILE: *const c_char = null();
|
||||
static mut LAST_LINE: c_int = 0;
|
||||
static mut LAST_MSG: *const c_char = null();
|
||||
|
||||
unsafe extern "C" fn some_assert_cb(
|
||||
file: *const c_char,
|
||||
line: c_int,
|
||||
msg: *const c_char,
|
||||
) {
|
||||
LAST_FILE = file;
|
||||
LAST_LINE = line;
|
||||
LAST_MSG = msg;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
unsafe {
|
||||
assert_cb = None;
|
||||
assert!(assert_cb.is_none());
|
||||
|
||||
assert_cb = Some(some_assert_cb);
|
||||
match assert_cb {
|
||||
None => panic!(),
|
||||
Some(actual_assert_cb) => {
|
||||
assert!(actual_assert_cb == some_assert_cb)
|
||||
}
|
||||
}
|
||||
|
||||
let file_cstr = CString::new("foo.rs").unwrap();
|
||||
let msg_cstr = CString::new("bar").unwrap();
|
||||
|
||||
assert_do(
|
||||
file_cstr.as_ptr() as *const c_char,
|
||||
123,
|
||||
msg_cstr.as_ptr() as *const c_char,
|
||||
);
|
||||
|
||||
let file = CStr::from_ptr(LAST_FILE).to_str().unwrap();
|
||||
let line = LAST_LINE;
|
||||
let msg = CStr::from_ptr(LAST_MSG).to_str().unwrap();
|
||||
|
||||
assert_eq!(file, "foo.rs");
|
||||
assert_eq!(line, 123);
|
||||
assert_eq!(msg, "bar");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
use libc::{c_char, size_t};
|
||||
|
||||
pub const ERROR_MSG_SIZE_MAX: usize = 256;
|
||||
pub const ERROR_MSG_SLEN_MAX: usize = ERROR_MSG_SIZE_MAX - 1;
|
||||
|
||||
#[link(name = "kernaux")]
|
||||
extern "C" {
|
||||
#[link_name = "kernaux_cmdline"]
|
||||
// TODO: Rust's "bool" is not guaranteed to be compatible with C's one
|
||||
pub fn cmdline(
|
||||
cmdline: *const c_char,
|
||||
error_msg: *mut c_char,
|
||||
argc: *mut size_t,
|
||||
argv: *mut *mut c_char,
|
||||
buffer: *mut c_char,
|
||||
arg_count_max: size_t,
|
||||
buffer_size: size_t,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
|
||||
const ARG_COUNT_MAX: usize = 100;
|
||||
const BUFFER_SIZE: usize = 4096;
|
||||
|
||||
#[test]
|
||||
fn default() {
|
||||
let cmdline = CString::new("foo bar\\ baz \"car cdr\"").unwrap();
|
||||
|
||||
let mut error_msg: [c_char; ERROR_MSG_SIZE_MAX] =
|
||||
[0; ERROR_MSG_SIZE_MAX];
|
||||
let mut argc: size_t = 0;
|
||||
let mut argv: [*mut c_char; ARG_COUNT_MAX] =
|
||||
[0 as *mut c_char; ARG_COUNT_MAX];
|
||||
let mut buffer: [c_char; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
|
||||
unsafe {
|
||||
assert!(super::cmdline(
|
||||
cmdline.as_ptr(),
|
||||
error_msg.as_mut_ptr(),
|
||||
&mut argc,
|
||||
argv.as_mut_ptr(),
|
||||
buffer.as_mut_ptr(),
|
||||
ARG_COUNT_MAX,
|
||||
BUFFER_SIZE,
|
||||
));
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(error_msg.as_ptr()) }
|
||||
.to_str()
|
||||
.unwrap(),
|
||||
"",
|
||||
);
|
||||
assert_eq!(argc, 3);
|
||||
assert_eq!(unsafe { CStr::from_ptr(argv[0]) }.to_str().unwrap(), "foo");
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(argv[1]) }.to_str().unwrap(),
|
||||
"bar baz",
|
||||
);
|
||||
assert_eq!(
|
||||
unsafe { CStr::from_ptr(argv[2]) }.to_str().unwrap(),
|
||||
"car cdr",
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
#![no_std]
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate std;
|
||||
|
||||
pub mod assert;
|
||||
pub mod version;
|
||||
|
||||
#[cfg(feature = "cmdline")]
|
||||
pub mod cmdline;
|
||||
#[cfg(feature = "ntoa")]
|
||||
pub mod ntoa;
|
||||
|
||||
pub use assert::*;
|
||||
|
||||
#[cfg(feature = "cmdline")]
|
||||
pub use cmdline::cmdline;
|
||||
#[cfg(feature = "ntoa")]
|
||||
pub use ntoa::{MAX_PREFIX_LEN as NTOA_MAX_PREFIX_LEN, *};
|
|
@ -0,0 +1,266 @@
|
|||
use libc::{c_char, c_int};
|
||||
|
||||
pub const MAX_PREFIX_LEN: usize = 100;
|
||||
|
||||
pub const UTOA_MIN_BUFFER_SIZE: usize = 64 + 1;
|
||||
pub const ITOA_MIN_BUFFER_SIZE: usize = 65 + 1;
|
||||
|
||||
pub const UTOA2_BUFFER_SIZE: usize = 64 + 2 + 1;
|
||||
pub const ITOA2_BUFFER_SIZE: usize = 65 + 2 + 1;
|
||||
pub const UTOA8_BUFFER_SIZE: usize = 21 + 2 + 1;
|
||||
pub const ITOA8_BUFFER_SIZE: usize = 22 + 2 + 1;
|
||||
pub const UTOA10_BUFFER_SIZE: usize = 20 + 1;
|
||||
pub const ITOA10_BUFFER_SIZE: usize = 20 + 1;
|
||||
pub const UTOA16_BUFFER_SIZE: usize = 16 + 2 + 1;
|
||||
pub const ITOA16_BUFFER_SIZE: usize = 17 + 2 + 1;
|
||||
|
||||
#[link(name = "kernaux")]
|
||||
extern "C" {
|
||||
#[link_name = "kernaux_utoa"]
|
||||
pub fn utoa(
|
||||
value: u64,
|
||||
buffer: *mut c_char,
|
||||
base: c_int,
|
||||
prefix: *const c_char,
|
||||
) -> *mut c_char;
|
||||
#[link_name = "kernaux_itoa"]
|
||||
pub fn itoa(
|
||||
value: i64,
|
||||
buffer: *mut c_char,
|
||||
base: c_int,
|
||||
prefix: *const c_char,
|
||||
) -> *mut c_char;
|
||||
|
||||
#[link_name = "kernaux_utoa2"]
|
||||
pub fn utoa2(value: u64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_itoa2"]
|
||||
pub fn itoa2(value: i64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_utoa8"]
|
||||
pub fn utoa8(value: u64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_itoa8"]
|
||||
pub fn itoa8(value: i64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_utoa10"]
|
||||
pub fn utoa10(value: u64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_itoa10"]
|
||||
pub fn itoa10(value: i64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_utoa16"]
|
||||
pub fn utoa16(value: u64, buffer: *mut c_char) -> *mut c_char;
|
||||
#[link_name = "kernaux_itoa16"]
|
||||
pub fn itoa16(value: i64, buffer: *mut c_char) -> *mut c_char;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::ptr::null;
|
||||
|
||||
fn empty_prefix() -> CString {
|
||||
CString::new("").unwrap()
|
||||
}
|
||||
|
||||
fn foo_prefix() -> CString {
|
||||
CString::new("foo").unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utoa() {
|
||||
let mut buffer: [i8; UTOA_MIN_BUFFER_SIZE + 3] =
|
||||
[0; UTOA_MIN_BUFFER_SIZE + 3];
|
||||
|
||||
let empty_pfx = empty_prefix();
|
||||
let foo_pfx = foo_prefix();
|
||||
|
||||
let end: *mut c_char =
|
||||
unsafe { utoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, null()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
utoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, empty_pfx.as_ptr())
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
utoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, foo_pfx.as_ptr())
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "foo123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(6) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itoa() {
|
||||
let mut buffer: [i8; ITOA_MIN_BUFFER_SIZE + 3] =
|
||||
[0; ITOA_MIN_BUFFER_SIZE + 3];
|
||||
|
||||
let empty_pfx = empty_prefix();
|
||||
let foo_pfx = foo_prefix();
|
||||
|
||||
let end: *mut c_char =
|
||||
unsafe { itoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, null()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
itoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, empty_pfx.as_ptr())
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
itoa(0x123, buffer.as_mut_ptr(), 'x' as c_int, foo_pfx.as_ptr())
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "foo123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(6) });
|
||||
|
||||
let end: *mut c_char =
|
||||
unsafe { itoa(-0x123, buffer.as_mut_ptr(), 'x' as c_int, null()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(4) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
itoa(
|
||||
-0x123,
|
||||
buffer.as_mut_ptr(),
|
||||
'x' as c_int,
|
||||
empty_pfx.as_ptr(),
|
||||
)
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(4) });
|
||||
|
||||
let end: *mut c_char = unsafe {
|
||||
itoa(-0x123, buffer.as_mut_ptr(), 'x' as c_int, foo_pfx.as_ptr())
|
||||
};
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-foo123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(7) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utoa2() {
|
||||
let mut buffer: [i8; UTOA2_BUFFER_SIZE] = [0; UTOA2_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { utoa2(123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0b1111011");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(9) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itoa2() {
|
||||
let mut buffer: [i8; ITOA2_BUFFER_SIZE] = [0; ITOA2_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa2(123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0b1111011");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(9) });
|
||||
|
||||
let mut buffer: [i8; ITOA2_BUFFER_SIZE] = [0; ITOA2_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa2(-123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-0b1111011");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(10) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utoa8() {
|
||||
let mut buffer: [i8; UTOA8_BUFFER_SIZE] = [0; UTOA8_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { utoa8(0o123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0o123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(5) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itoa8() {
|
||||
let mut buffer: [i8; ITOA8_BUFFER_SIZE] = [0; ITOA8_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa8(0o123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0o123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(5) });
|
||||
|
||||
let mut buffer: [i8; ITOA8_BUFFER_SIZE] = [0; ITOA8_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa8(-0o123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-0o123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(6) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utoa10() {
|
||||
let mut buffer: [i8; UTOA10_BUFFER_SIZE] = [0; UTOA10_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { utoa10(123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itoa10() {
|
||||
let mut buffer: [i8; ITOA10_BUFFER_SIZE] = [0; ITOA10_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa10(123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(3) });
|
||||
|
||||
let mut buffer: [i8; ITOA10_BUFFER_SIZE] = [0; ITOA10_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa10(-123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(4) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_utoa16() {
|
||||
let mut buffer: [i8; UTOA16_BUFFER_SIZE] = [0; UTOA16_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { utoa16(0x123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0x123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(5) });
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_itoa16() {
|
||||
let mut buffer: [i8; ITOA16_BUFFER_SIZE] = [0; ITOA16_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa16(0x123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "0x123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(5) });
|
||||
|
||||
let mut buffer: [i8; ITOA16_BUFFER_SIZE] = [0; ITOA16_BUFFER_SIZE];
|
||||
let end: *mut c_char = unsafe { itoa16(-0x123, buffer.as_mut_ptr()) };
|
||||
let result =
|
||||
unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap();
|
||||
assert_eq!(result, "-0x123");
|
||||
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(6) });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
pub fn with_cmdline() -> bool {
|
||||
cfg!(feature = "cmdline")
|
||||
}
|
||||
|
||||
pub fn with_ntoa() -> bool {
|
||||
cfg!(feature = "ntoa")
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
name = "kernaux"
|
||||
version = "0.7.0"
|
||||
authors = ["Alex Kotov <kotovalexarian@gmail.com>"]
|
||||
edition = "2021"
|
||||
description = "Safe binding to libkernaux - auxiliary library for kernel development"
|
||||
readme = true
|
||||
homepage = "https://github.com/tailix/libkernaux/tree/master/bindings/rust"
|
||||
repository = "https://github.com/tailix/libkernaux/tree/master/bindings/rust"
|
||||
license = "MIT"
|
||||
keywords = ["ffi", "embedded", "bindings"]
|
||||
categories = ["api-bindings", "embedded", "parsing"]
|
||||
publish = true
|
||||
|
||||
[features]
|
||||
default = ["cmdline", "ntoa"]
|
||||
cmdline = ["kernaux-sys/cmdline"]
|
||||
ntoa = ["kernaux-sys/ntoa"]
|
||||
|
||||
[dependencies]
|
||||
ctor = "0.1.22"
|
||||
|
||||
[dependencies.kernaux-sys]
|
||||
version = "0.7.0"
|
||||
default-features = false
|
||||
path = "../kernaux-sys"
|
||||
|
||||
[dependencies.libc]
|
||||
version = "0.2.113"
|
||||
default-features = false
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue