Use the code from libkernaux

This commit is contained in:
Alex Kotov 2022-12-25 12:58:00 +04:00
commit 772db061c5
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
283 changed files with 25641 additions and 0 deletions

104
.cirrus.yml Normal file
View File

@ -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 .

9
.github/sources.list vendored Normal file
View File

@ -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

148
.github/workflows/main.yml vendored Normal file
View File

@ -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

48
.github/workflows/mruby.yml vendored Normal file
View File

@ -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

46
.github/workflows/ruby.yml vendored Normal file
View File

@ -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

45
.github/workflows/rust.yml vendored Normal file
View File

@ -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

85
.gitignore vendored Normal file
View File

@ -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

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "vendor/mruby"]
path = vendor/mruby
url = https://github.com/mruby/mruby.git

39
.openbsd.yml Normal file
View File

@ -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

1
AUTHORS Normal file
View File

@ -0,0 +1 @@
Alex Kotov <kotovalexarian@gmail.com>

194
CONTRIBUTING.md Normal file
View File

@ -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)

27
COPYING Normal file
View File

@ -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.

3
ChangeLog Normal file
View File

@ -0,0 +1,3 @@
2022-12-23 Alex Kotov <kotovalexarian@gmail.com>
libkernaux 0.7.0 released

125
Makefile.am Normal file
View File

@ -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

1
NEWS Symbolic link
View File

@ -0,0 +1 @@
NEWS.md

204
NEWS.md Normal file
View File

@ -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.

1
README Symbolic link
View File

@ -0,0 +1 @@
README.md

264
README.md Normal file
View File

@ -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
```

1
VERSION Normal file
View File

@ -0,0 +1 @@
0.7.0

1
VERSION_SO Normal file
View File

@ -0,0 +1 @@
0:0:0

5
autogen.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -eux
exec autoreconf -isf -Wall

7
bindings/mruby/.gitignore vendored Normal file
View File

@ -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

View File

@ -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

9
bindings/mruby/Gemfile Normal file
View File

@ -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'

26
bindings/mruby/README.md Normal file
View File

@ -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
```

29
bindings/mruby/Rakefile Normal file
View File

@ -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

7
bindings/mruby/bin/setup Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -eux
bundle install
# Do any other automated setup that you need to do here

View File

@ -0,0 +1,6 @@
MRuby::Build.new do |conf|
conf.toolchain
conf.enable_test
conf.gem '.'
conf.gem core: 'mruby-bin-mirb'
end

View File

@ -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

View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

64
bindings/mruby/src/main.c Normal file
View File

@ -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];
}

30
bindings/mruby/src/main.h Normal file
View File

@ -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

298
bindings/mruby/src/ntoa.c Normal file
View File

@ -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

144
bindings/mruby/src/printf.c Normal file
View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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

348
bindings/mruby/test/ntoa.rb Normal file
View File

@ -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

View File

@ -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

2
bindings/python/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/dist/
/src/kernaux.egg-info/

View File

@ -0,0 +1,5 @@
kernaux
=======
Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary
library for kernel development.

View File

@ -0,0 +1,6 @@
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"

24
bindings/python/setup.cfg Normal file
View File

@ -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

View File

View File

@ -0,0 +1,2 @@
def add_one(number):
return number + 1

44
bindings/ruby/.gitignore vendored Normal file
View File

@ -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?--*

View File

@ -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

9
bindings/ruby/.simplecov Normal file
View File

@ -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

4
bindings/ruby/.yardopts Normal file
View File

@ -0,0 +1,4 @@
--markup markdown
--readme README.md
--protected
--private

8
bindings/ruby/Gemfile Normal file
View File

@ -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

29
bindings/ruby/README.md Normal file
View File

@ -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
```

92
bindings/ruby/Rakefile Normal file
View File

@ -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

8
bindings/ruby/bin/console Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require 'bundler/setup'
require 'kernaux'
require 'pry'
Pry.start

7
bindings/ruby/bin/setup Executable file
View File

@ -0,0 +1,7 @@
#!/bin/sh
set -eux
bundle install
# Do any other automated setup that you need to do here

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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'

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

2
bindings/rust/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/Cargo.lock
/target/

5
bindings/rust/Cargo.toml Normal file
View File

@ -0,0 +1,5 @@
[workspace]
members = [
"kernaux",
"kernaux-sys",
]

View File

@ -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"]

View File

@ -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.

View File

@ -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");
}
}
}

View File

@ -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",
);
}
}

View File

@ -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, *};

View File

@ -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) });
}
}

View File

@ -0,0 +1,7 @@
pub fn with_cmdline() -> bool {
cfg!(feature = "cmdline")
}
pub fn with_ntoa() -> bool {
cfg!(feature = "ntoa")
}

View File

@ -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