From 772db061c5194180bba6d8a1122cb69a18f72884 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Sun, 25 Dec 2022 12:58:00 +0400 Subject: [PATCH] Use the code from libkernaux --- .cirrus.yml | 104 ++ .github/sources.list | 9 + .github/workflows/main.yml | 148 +++ .github/workflows/mruby.yml | 48 + .github/workflows/ruby.yml | 46 + .github/workflows/rust.yml | 45 + .gitignore | 85 ++ .gitmodules | 3 + .openbsd.yml | 39 + AUTHORS | 1 + CONTRIBUTING.md | 194 +++ COPYING | 27 + ChangeLog | 3 + Makefile.am | 125 ++ NEWS | 1 + NEWS.md | 204 +++ README | 1 + README.md | 264 ++++ VERSION | 1 + VERSION_SO | 1 + autogen.sh | 5 + bindings/mruby/.gitignore | 7 + bindings/mruby/.rubocop.yml | 81 ++ bindings/mruby/Gemfile | 9 + bindings/mruby/README.md | 26 + bindings/mruby/Rakefile | 29 + bindings/mruby/bin/setup | 7 + bindings/mruby/build_config.rb | 6 + bindings/mruby/mrbgem.rake | 21 + bindings/mruby/mrblib/kernaux.rb | 15 + bindings/mruby/src/assert.c | 74 ++ bindings/mruby/src/cmdline.c | 68 + bindings/mruby/src/dynarg.c | 45 + bindings/mruby/src/dynarg.h | 42 + bindings/mruby/src/main.c | 64 + bindings/mruby/src/main.h | 30 + bindings/mruby/src/ntoa.c | 298 +++++ bindings/mruby/src/printf.c | 144 +++ bindings/mruby/src/version.c | 49 + bindings/mruby/test/assert.rb | 26 + bindings/mruby/test/cmdline.rb | 93 ++ bindings/mruby/test/ntoa.rb | 348 +++++ bindings/mruby/test/sprintf.rb | 113 ++ bindings/python/.gitignore | 2 + bindings/python/README.md | 5 + bindings/python/pyproject.toml | 6 + bindings/python/setup.cfg | 24 + bindings/python/src/kernaux/__init__.py | 0 bindings/python/src/kernaux/example.py | 2 + bindings/ruby/.gitignore | 44 + bindings/ruby/.rubocop.yml | 79 ++ bindings/ruby/.simplecov | 9 + bindings/ruby/.yardopts | 4 + bindings/ruby/Gemfile | 8 + bindings/ruby/README.md | 29 + bindings/ruby/Rakefile | 92 ++ bindings/ruby/bin/console | 8 + bindings/ruby/bin/setup | 7 + bindings/ruby/ext/default/assert.c | 55 + bindings/ruby/ext/default/cmdline.c | 83 ++ bindings/ruby/ext/default/dynarg.c | 45 + bindings/ruby/ext/default/dynarg.h | 42 + bindings/ruby/ext/default/extconf.rb | 14 + bindings/ruby/ext/default/main.c | 34 + bindings/ruby/ext/default/main.h | 34 + bindings/ruby/ext/default/ntoa.c | 254 ++++ bindings/ruby/ext/default/printf.c | 137 ++ bindings/ruby/ext/default/version.c | 47 + bindings/ruby/kernaux.gemspec | 59 + bindings/ruby/lib/kernaux.rb | 19 + bindings/ruby/lib/kernaux/assert.rb | 58 + bindings/ruby/lib/kernaux/cmdline.rb | 20 + bindings/ruby/lib/kernaux/errors.rb | 39 + bindings/ruby/lib/kernaux/ntoa.rb | 136 ++ bindings/ruby/lib/kernaux/version.rb | 13 + .../spec/lib/kernaux/assert/panic_spec.rb | 32 + .../ruby/spec/lib/kernaux/cmdline_spec.rb | 115 ++ .../ruby/spec/lib/kernaux/ntoa/itoa10_spec.rb | 70 + .../ruby/spec/lib/kernaux/ntoa/itoa16_spec.rb | 72 ++ .../ruby/spec/lib/kernaux/ntoa/itoa2_spec.rb | 72 ++ .../ruby/spec/lib/kernaux/ntoa/itoa8_spec.rb | 72 ++ .../ruby/spec/lib/kernaux/ntoa/itoa_spec.rb | 245 ++++ .../ruby/spec/lib/kernaux/ntoa/utoa10_spec.rb | 52 + .../ruby/spec/lib/kernaux/ntoa/utoa16_spec.rb | 52 + .../ruby/spec/lib/kernaux/ntoa/utoa2_spec.rb | 52 + .../ruby/spec/lib/kernaux/ntoa/utoa8_spec.rb | 52 + .../ruby/spec/lib/kernaux/ntoa/utoa_spec.rb | 217 ++++ .../ruby/spec/lib/kernaux/sprintf_spec.rb | 85 ++ bindings/ruby/spec/lib/kernaux_spec.rb | 11 + bindings/ruby/spec/spec_helper.rb | 106 ++ bindings/rust/.gitignore | 2 + bindings/rust/Cargo.toml | 5 + bindings/rust/kernaux-sys/Cargo.toml | 23 + bindings/rust/kernaux-sys/README.md | 8 + bindings/rust/kernaux-sys/src/assert.rs | 67 + bindings/rust/kernaux-sys/src/cmdline.rs | 70 + bindings/rust/kernaux-sys/src/lib.rs | 19 + bindings/rust/kernaux-sys/src/ntoa.rs | 266 ++++ bindings/rust/kernaux-sys/src/version.rs | 7 + bindings/rust/kernaux/Cargo.toml | 30 + bindings/rust/kernaux/README.md | 8 + bindings/rust/kernaux/src/assert.rs | 33 + bindings/rust/kernaux/src/lib.rs | 52 + bindings/rust/kernaux/src/ntoa.rs | 771 +++++++++++ bindings/rust/kernaux/src/version.rs | 1 + bindings/rust/rustfmt.toml | 1 + build/.keep | 0 configure.ac | 413 ++++++ examples/.gitignore | 19 + examples/Makefile.am | 180 +++ examples/cmdline.c | 34 + examples/generic_display.c | 115 ++ examples/generic_malloc.c | 93 ++ examples/generic_mutex.c | 79 ++ examples/macro_bits.c | 21 + examples/macro_cast.c | 25 + examples/macro_container_of.c | 35 + examples/macro_packing.c | 25 + examples/macro_static_test.c | 28 + examples/main.c | 26 + examples/memmap.c | 60 + examples/multiboot2_header_macro.c | 104 ++ examples/ntoa.c | 281 ++++ examples/pfa.c | 71 ++ examples/printf_file.c | 33 + examples/printf_file_va.c | 36 + examples/printf_fmt.c | 101 ++ examples/printf_str.c | 21 + examples/printf_str_va.c | 24 + examples/units_human.c | 45 + fixtures/.gitignore | 8 + fixtures/Makefile.am | 61 + fixtures/cmdline.yml | 241 ++++ fixtures/multiboot2_bin_examples_gen.c.in | 84 ++ fixtures/multiboot2_header_example0.h | 32 + fixtures/multiboot2_header_example0.txt | 11 + fixtures/multiboot2_header_example1.h | 96 ++ fixtures/multiboot2_header_example1.txt | 52 + fixtures/multiboot2_header_example2.h | 172 +++ fixtures/multiboot2_header_example2.txt | 100 ++ fixtures/multiboot2_info_example0.h | 26 + fixtures/multiboot2_info_example0.txt | 8 + fixtures/multiboot2_info_example1.h | 60 + fixtures/multiboot2_info_example1.txt | 264 ++++ fixtures/multiboot2_info_example2.h | 393 ++++++ fixtures/multiboot2_info_example2.txt | 369 ++++++ fixtures/printf.yml | 131 ++ fixtures/printf_fmt.yml | 340 +++++ fixtures/printf_orig.yml | 667 ++++++++++ include/.gitignore | 1 + include/Makefile.am | 39 + include/kernaux.h | 25 + include/kernaux/arch/i386-idt.h | 62 + include/kernaux/arch/i386.h | 217 ++++ include/kernaux/arch/riscv64.h | 12 + include/kernaux/arch/x86.h | 113 ++ include/kernaux/arch/x86_64.h | 41 + include/kernaux/asm/i386.h | 30 + include/kernaux/asm/riscv64.h | 14 + include/kernaux/asm/x86.h | 61 + include/kernaux/asm/x86_64.h | 15 + include/kernaux/cmdline.h | 29 + include/kernaux/elf.h | 181 +++ include/kernaux/free_list.h | 38 + include/kernaux/generic/display.h | 37 + include/kernaux/generic/malloc.h | 33 + include/kernaux/generic/mutex.h | 25 + include/kernaux/macro.h | 101 ++ include/kernaux/macro/packing_end.run | 3 + include/kernaux/macro/packing_start.run | 3 + include/kernaux/mbr.h | 68 + include/kernaux/memmap.h | 67 + include/kernaux/multiboot2.h | 577 +++++++++ include/kernaux/multiboot2/header_enums.h | 40 + include/kernaux/multiboot2/header_helpers.h | 27 + include/kernaux/multiboot2/header_is_valid.h | 66 + include/kernaux/multiboot2/header_macro.h | 46 + include/kernaux/multiboot2/header_print.h | 79 ++ include/kernaux/multiboot2/info_enums.h | 49 + include/kernaux/multiboot2/info_helpers.h | 31 + include/kernaux/multiboot2/info_is_valid.h | 110 ++ include/kernaux/multiboot2/info_print.h | 134 ++ include/kernaux/ntoa.h | 70 + include/kernaux/pfa.h | 36 + include/kernaux/printf.h | 48 + include/kernaux/printf_fmt.h | 75 ++ include/kernaux/runtime.h | 18 + include/kernaux/units.h | 42 + include/kernaux/version.h.in | 16 + libc/Makefile.am | 30 + libc/include/Makefile.am | 9 + libc/include/ctype.h | 20 + libc/include/errno.h | 16 + libc/include/inttypes.h | 12 + libc/include/kernaux/libc.h | 26 + libc/include/setjmp.h | 27 + libc/include/stdlib.h | 29 + libc/include/string.h | 37 + libc/include/sys/types.h | 12 + libc/src/asm/i386/longjmp.S | 25 + libc/src/asm/i386/setjmp.S | 25 + libc/src/asm/x86_64/longjmp.S | 27 + libc/src/asm/x86_64/setjmp.S | 28 + libc/src/ctype.c | 35 + libc/src/errno.c | 7 + libc/src/kernaux.c | 17 + libc/src/stdlib.c | 70 + libc/src/string.c | 135 ++ libkernaux.pc.in | 11 + m4/.keep | 0 make/checks.am | 40 + make/shared.am | 20 + make/test-suite-log | 6 + pkgs/freebsd/.gitignore | 1 + pkgs/freebsd/devel/libkernaux/Makefile | 19 + pkgs/freebsd/devel/libkernaux/distinfo | 3 + pkgs/freebsd/devel/libkernaux/pkg-descr | 3 + pkgs/freebsd/devel/libkernaux/pkg-plist | 43 + sha256sums.txt | 33 + src/arch/i386/idt.c | 75 ++ src/asm/i386.S | 60 + src/asm/riscv64.S | 3 + src/asm/x86_64.S | 3 + src/assert.h | 16 + src/cmdline.c | 291 +++++ src/elf.c | 48 + src/free_list.c | 297 +++++ src/generic/display.c | 116 ++ src/generic/malloc.c | 68 + src/generic/mutex.c | 23 + src/mbr.c | 41 + src/memmap.c | 258 ++++ src/multiboot2/header_enums.c | 65 + src/multiboot2/header_helpers.c | 62 + src/multiboot2/header_is_valid.c | 265 ++++ src/multiboot2/header_print.c | 473 +++++++ src/multiboot2/info_convert.c | 71 ++ src/multiboot2/info_enums.c | 79 ++ src/multiboot2/info_helpers.c | 79 ++ src/multiboot2/info_is_valid.c | 428 +++++++ src/multiboot2/info_print.c | 767 +++++++++++ src/ntoa.c | 119 ++ src/pfa.c | 163 +++ src/printf.c | 665 ++++++++++ src/printf_fmt.c | 349 +++++ src/runtime.c | 17 + src/units.c | 149 +++ tests/.gitignore | 33 + tests/Makefile.am | 414 ++++++ tests/cmdline_gen.jinja | 46 + tests/cmdline_gen.py | 49 + tests/cmdline_test.c | 71 ++ tests/cmdline_test.h | 26 + tests/main.c | 26 + tests/multiboot2_header_print0.c | 43 + tests/multiboot2_header_print1.c | 43 + tests/multiboot2_header_print2.c | 43 + tests/multiboot2_info_print0.c | 43 + tests/multiboot2_info_print1.c | 43 + tests/multiboot2_info_print2.c | 43 + tests/printf_fmt_gen.jinja | 36 + tests/printf_fmt_gen.py | 65 + tests/printf_gen.jinja | 76 ++ tests/printf_gen.py | 88 ++ tests/test_arch_i386.c | 132 ++ tests/test_cmdline.c | 119 ++ tests/test_elf.c | 7 + tests/test_free_list.c | 365 ++++++ tests/test_mbr.c | 43 + tests/test_multiboot2_common_packing.c | 164 +++ tests/test_multiboot2_header_helpers.c | 13 + tests/test_multiboot2_header_print.c.in | 61 + tests/test_multiboot2_header_validation.c | 8 + tests/test_multiboot2_info_convert_memmap.c | 128 ++ tests/test_multiboot2_info_helpers.c | 401 ++++++ tests/test_multiboot2_info_print.c.in | 61 + tests/test_multiboot2_info_validation.c | 1128 +++++++++++++++++ tests/test_ntoa.c | 777 ++++++++++++ tests/test_ntoa_assert.c | 154 +++ tests/test_pfa.c | 86 ++ tests/test_pfa_assert.c | 99 ++ tests/test_printf.c | 34 + tests/test_units_human.c | 169 +++ 283 files changed, 25641 insertions(+) create mode 100644 .cirrus.yml create mode 100644 .github/sources.list create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/mruby.yml create mode 100644 .github/workflows/ruby.yml create mode 100644 .github/workflows/rust.yml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .openbsd.yml create mode 100644 AUTHORS create mode 100644 CONTRIBUTING.md create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 120000 NEWS create mode 100644 NEWS.md create mode 120000 README create mode 100644 README.md create mode 100644 VERSION create mode 100644 VERSION_SO create mode 100755 autogen.sh create mode 100644 bindings/mruby/.gitignore create mode 100644 bindings/mruby/.rubocop.yml create mode 100644 bindings/mruby/Gemfile create mode 100644 bindings/mruby/README.md create mode 100644 bindings/mruby/Rakefile create mode 100755 bindings/mruby/bin/setup create mode 100644 bindings/mruby/build_config.rb create mode 100644 bindings/mruby/mrbgem.rake create mode 100644 bindings/mruby/mrblib/kernaux.rb create mode 100644 bindings/mruby/src/assert.c create mode 100644 bindings/mruby/src/cmdline.c create mode 100644 bindings/mruby/src/dynarg.c create mode 100644 bindings/mruby/src/dynarg.h create mode 100644 bindings/mruby/src/main.c create mode 100644 bindings/mruby/src/main.h create mode 100644 bindings/mruby/src/ntoa.c create mode 100644 bindings/mruby/src/printf.c create mode 100644 bindings/mruby/src/version.c create mode 100644 bindings/mruby/test/assert.rb create mode 100644 bindings/mruby/test/cmdline.rb create mode 100644 bindings/mruby/test/ntoa.rb create mode 100644 bindings/mruby/test/sprintf.rb create mode 100644 bindings/python/.gitignore create mode 100644 bindings/python/README.md create mode 100644 bindings/python/pyproject.toml create mode 100644 bindings/python/setup.cfg create mode 100644 bindings/python/src/kernaux/__init__.py create mode 100644 bindings/python/src/kernaux/example.py create mode 100644 bindings/ruby/.gitignore create mode 100644 bindings/ruby/.rubocop.yml create mode 100644 bindings/ruby/.simplecov create mode 100644 bindings/ruby/.yardopts create mode 100644 bindings/ruby/Gemfile create mode 100644 bindings/ruby/README.md create mode 100644 bindings/ruby/Rakefile create mode 100755 bindings/ruby/bin/console create mode 100755 bindings/ruby/bin/setup create mode 100644 bindings/ruby/ext/default/assert.c create mode 100644 bindings/ruby/ext/default/cmdline.c create mode 100644 bindings/ruby/ext/default/dynarg.c create mode 100644 bindings/ruby/ext/default/dynarg.h create mode 100644 bindings/ruby/ext/default/extconf.rb create mode 100644 bindings/ruby/ext/default/main.c create mode 100644 bindings/ruby/ext/default/main.h create mode 100644 bindings/ruby/ext/default/ntoa.c create mode 100644 bindings/ruby/ext/default/printf.c create mode 100644 bindings/ruby/ext/default/version.c create mode 100644 bindings/ruby/kernaux.gemspec create mode 100644 bindings/ruby/lib/kernaux.rb create mode 100644 bindings/ruby/lib/kernaux/assert.rb create mode 100644 bindings/ruby/lib/kernaux/cmdline.rb create mode 100644 bindings/ruby/lib/kernaux/errors.rb create mode 100644 bindings/ruby/lib/kernaux/ntoa.rb create mode 100644 bindings/ruby/lib/kernaux/version.rb create mode 100644 bindings/ruby/spec/lib/kernaux/assert/panic_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/cmdline_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/itoa10_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/itoa16_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/itoa2_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/itoa8_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/itoa_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/utoa10_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/utoa16_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/utoa2_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/utoa8_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/ntoa/utoa_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux/sprintf_spec.rb create mode 100644 bindings/ruby/spec/lib/kernaux_spec.rb create mode 100644 bindings/ruby/spec/spec_helper.rb create mode 100644 bindings/rust/.gitignore create mode 100644 bindings/rust/Cargo.toml create mode 100644 bindings/rust/kernaux-sys/Cargo.toml create mode 100644 bindings/rust/kernaux-sys/README.md create mode 100644 bindings/rust/kernaux-sys/src/assert.rs create mode 100644 bindings/rust/kernaux-sys/src/cmdline.rs create mode 100644 bindings/rust/kernaux-sys/src/lib.rs create mode 100644 bindings/rust/kernaux-sys/src/ntoa.rs create mode 100644 bindings/rust/kernaux-sys/src/version.rs create mode 100644 bindings/rust/kernaux/Cargo.toml create mode 100644 bindings/rust/kernaux/README.md create mode 100644 bindings/rust/kernaux/src/assert.rs create mode 100644 bindings/rust/kernaux/src/lib.rs create mode 100644 bindings/rust/kernaux/src/ntoa.rs create mode 120000 bindings/rust/kernaux/src/version.rs create mode 100644 bindings/rust/rustfmt.toml create mode 100644 build/.keep create mode 100644 configure.ac create mode 100644 examples/.gitignore create mode 100644 examples/Makefile.am create mode 100644 examples/cmdline.c create mode 100644 examples/generic_display.c create mode 100644 examples/generic_malloc.c create mode 100644 examples/generic_mutex.c create mode 100644 examples/macro_bits.c create mode 100644 examples/macro_cast.c create mode 100644 examples/macro_container_of.c create mode 100644 examples/macro_packing.c create mode 100644 examples/macro_static_test.c create mode 100644 examples/main.c create mode 100644 examples/memmap.c create mode 100644 examples/multiboot2_header_macro.c create mode 100644 examples/ntoa.c create mode 100644 examples/pfa.c create mode 100644 examples/printf_file.c create mode 100644 examples/printf_file_va.c create mode 100644 examples/printf_fmt.c create mode 100644 examples/printf_str.c create mode 100644 examples/printf_str_va.c create mode 100644 examples/units_human.c create mode 100644 fixtures/.gitignore create mode 100644 fixtures/Makefile.am create mode 100644 fixtures/cmdline.yml create mode 100644 fixtures/multiboot2_bin_examples_gen.c.in create mode 100644 fixtures/multiboot2_header_example0.h create mode 100644 fixtures/multiboot2_header_example0.txt create mode 100644 fixtures/multiboot2_header_example1.h create mode 100644 fixtures/multiboot2_header_example1.txt create mode 100644 fixtures/multiboot2_header_example2.h create mode 100644 fixtures/multiboot2_header_example2.txt create mode 100644 fixtures/multiboot2_info_example0.h create mode 100644 fixtures/multiboot2_info_example0.txt create mode 100644 fixtures/multiboot2_info_example1.h create mode 100644 fixtures/multiboot2_info_example1.txt create mode 100644 fixtures/multiboot2_info_example2.h create mode 100644 fixtures/multiboot2_info_example2.txt create mode 100644 fixtures/printf.yml create mode 100644 fixtures/printf_fmt.yml create mode 100644 fixtures/printf_orig.yml create mode 100644 include/.gitignore create mode 100644 include/Makefile.am create mode 100644 include/kernaux.h create mode 100644 include/kernaux/arch/i386-idt.h create mode 100644 include/kernaux/arch/i386.h create mode 100644 include/kernaux/arch/riscv64.h create mode 100644 include/kernaux/arch/x86.h create mode 100644 include/kernaux/arch/x86_64.h create mode 100644 include/kernaux/asm/i386.h create mode 100644 include/kernaux/asm/riscv64.h create mode 100644 include/kernaux/asm/x86.h create mode 100644 include/kernaux/asm/x86_64.h create mode 100644 include/kernaux/cmdline.h create mode 100644 include/kernaux/elf.h create mode 100644 include/kernaux/free_list.h create mode 100644 include/kernaux/generic/display.h create mode 100644 include/kernaux/generic/malloc.h create mode 100644 include/kernaux/generic/mutex.h create mode 100644 include/kernaux/macro.h create mode 100644 include/kernaux/macro/packing_end.run create mode 100644 include/kernaux/macro/packing_start.run create mode 100644 include/kernaux/mbr.h create mode 100644 include/kernaux/memmap.h create mode 100644 include/kernaux/multiboot2.h create mode 100644 include/kernaux/multiboot2/header_enums.h create mode 100644 include/kernaux/multiboot2/header_helpers.h create mode 100644 include/kernaux/multiboot2/header_is_valid.h create mode 100644 include/kernaux/multiboot2/header_macro.h create mode 100644 include/kernaux/multiboot2/header_print.h create mode 100644 include/kernaux/multiboot2/info_enums.h create mode 100644 include/kernaux/multiboot2/info_helpers.h create mode 100644 include/kernaux/multiboot2/info_is_valid.h create mode 100644 include/kernaux/multiboot2/info_print.h create mode 100644 include/kernaux/ntoa.h create mode 100644 include/kernaux/pfa.h create mode 100644 include/kernaux/printf.h create mode 100644 include/kernaux/printf_fmt.h create mode 100644 include/kernaux/runtime.h create mode 100644 include/kernaux/units.h create mode 100644 include/kernaux/version.h.in create mode 100644 libc/Makefile.am create mode 100644 libc/include/Makefile.am create mode 100644 libc/include/ctype.h create mode 100644 libc/include/errno.h create mode 100644 libc/include/inttypes.h create mode 100644 libc/include/kernaux/libc.h create mode 100644 libc/include/setjmp.h create mode 100644 libc/include/stdlib.h create mode 100644 libc/include/string.h create mode 100644 libc/include/sys/types.h create mode 100644 libc/src/asm/i386/longjmp.S create mode 100644 libc/src/asm/i386/setjmp.S create mode 100644 libc/src/asm/x86_64/longjmp.S create mode 100644 libc/src/asm/x86_64/setjmp.S create mode 100644 libc/src/ctype.c create mode 100644 libc/src/errno.c create mode 100644 libc/src/kernaux.c create mode 100644 libc/src/stdlib.c create mode 100644 libc/src/string.c create mode 100644 libkernaux.pc.in create mode 100644 m4/.keep create mode 100644 make/checks.am create mode 100644 make/shared.am create mode 100755 make/test-suite-log create mode 100644 pkgs/freebsd/.gitignore create mode 100644 pkgs/freebsd/devel/libkernaux/Makefile create mode 100644 pkgs/freebsd/devel/libkernaux/distinfo create mode 100644 pkgs/freebsd/devel/libkernaux/pkg-descr create mode 100644 pkgs/freebsd/devel/libkernaux/pkg-plist create mode 100644 sha256sums.txt create mode 100644 src/arch/i386/idt.c create mode 100644 src/asm/i386.S create mode 100644 src/asm/riscv64.S create mode 100644 src/asm/x86_64.S create mode 100644 src/assert.h create mode 100644 src/cmdline.c create mode 100644 src/elf.c create mode 100644 src/free_list.c create mode 100644 src/generic/display.c create mode 100644 src/generic/malloc.c create mode 100644 src/generic/mutex.c create mode 100644 src/mbr.c create mode 100644 src/memmap.c create mode 100644 src/multiboot2/header_enums.c create mode 100644 src/multiboot2/header_helpers.c create mode 100644 src/multiboot2/header_is_valid.c create mode 100644 src/multiboot2/header_print.c create mode 100644 src/multiboot2/info_convert.c create mode 100644 src/multiboot2/info_enums.c create mode 100644 src/multiboot2/info_helpers.c create mode 100644 src/multiboot2/info_is_valid.c create mode 100644 src/multiboot2/info_print.c create mode 100644 src/ntoa.c create mode 100644 src/pfa.c create mode 100644 src/printf.c create mode 100644 src/printf_fmt.c create mode 100644 src/runtime.c create mode 100644 src/units.c create mode 100644 tests/.gitignore create mode 100644 tests/Makefile.am create mode 100644 tests/cmdline_gen.jinja create mode 100644 tests/cmdline_gen.py create mode 100644 tests/cmdline_test.c create mode 100644 tests/cmdline_test.h create mode 100644 tests/main.c create mode 100644 tests/multiboot2_header_print0.c create mode 100644 tests/multiboot2_header_print1.c create mode 100644 tests/multiboot2_header_print2.c create mode 100644 tests/multiboot2_info_print0.c create mode 100644 tests/multiboot2_info_print1.c create mode 100644 tests/multiboot2_info_print2.c create mode 100644 tests/printf_fmt_gen.jinja create mode 100644 tests/printf_fmt_gen.py create mode 100644 tests/printf_gen.jinja create mode 100644 tests/printf_gen.py create mode 100644 tests/test_arch_i386.c create mode 100644 tests/test_cmdline.c create mode 100644 tests/test_elf.c create mode 100644 tests/test_free_list.c create mode 100644 tests/test_mbr.c create mode 100644 tests/test_multiboot2_common_packing.c create mode 100644 tests/test_multiboot2_header_helpers.c create mode 100644 tests/test_multiboot2_header_print.c.in create mode 100644 tests/test_multiboot2_header_validation.c create mode 100644 tests/test_multiboot2_info_convert_memmap.c create mode 100644 tests/test_multiboot2_info_helpers.c create mode 100644 tests/test_multiboot2_info_print.c.in create mode 100644 tests/test_multiboot2_info_validation.c create mode 100644 tests/test_ntoa.c create mode 100644 tests/test_ntoa_assert.c create mode 100644 tests/test_pfa.c create mode 100644 tests/test_pfa_assert.c create mode 100644 tests/test_printf.c create mode 100644 tests/test_units_human.c diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000..55447c1 --- /dev/null +++ b/.cirrus.yml @@ -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 . diff --git a/.github/sources.list b/.github/sources.list new file mode 100644 index 0000000..a519ac3 --- /dev/null +++ b/.github/sources.list @@ -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 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..7a21c54 --- /dev/null +++ b/.github/workflows/main.yml @@ -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 diff --git a/.github/workflows/mruby.yml b/.github/workflows/mruby.yml new file mode 100644 index 0000000..5519483 --- /dev/null +++ b/.github/workflows/mruby.yml @@ -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 diff --git a/.github/workflows/ruby.yml b/.github/workflows/ruby.yml new file mode 100644 index 0000000..d633625 --- /dev/null +++ b/.github/workflows/ruby.yml @@ -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 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 0000000..63dd9e1 --- /dev/null +++ b/.github/workflows/rust.yml @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0eef5a1 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0f7ea81 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/mruby"] + path = vendor/mruby + url = https://github.com/mruby/mruby.git diff --git a/.openbsd.yml b/.openbsd.yml new file mode 100644 index 0000000..593ce5f --- /dev/null +++ b/.openbsd.yml @@ -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 diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..3a0bbea --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Alex Kotov diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..c75e7d2 --- /dev/null +++ b/CONTRIBUTING.md @@ -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) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3adbc01 --- /dev/null +++ b/COPYING @@ -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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..9f91e1a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,3 @@ +2022-12-23 Alex Kotov + + libkernaux 0.7.0 released diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..203bdba --- /dev/null +++ b/Makefile.am @@ -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 diff --git a/NEWS b/NEWS new file mode 120000 index 0000000..7b97b99 --- /dev/null +++ b/NEWS @@ -0,0 +1 @@ +NEWS.md \ No newline at end of file diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..16a79b6 --- /dev/null +++ b/NEWS.md @@ -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. +* `` - definition `KERNAUX_DEBUG` has been removed. +* `` - it has been removed. +* `` - 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. +* `` - definition `KERNAUX_BITFIELDS` has been added. +* `` - new header. +* `` - macro `KERNAUX_STATIC_TEST` has been added. +* `` - macros `KERNAUX_CAST_(VAR|CONST)` have been added. +* `` - new header. +* `` - new header. Now it contains the variable + `kernaux_assert_cb`. + +### Other changes + +* `./configure` - a shared library is built by default. +* `` - the headers are the same regardless of the selection + of features and packages (except ``). +* `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*`. +* `` - 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. +* `` - macros `KERNAUX_ALIGNED`, `KERNAUX_ASM`, + `KERNAUX_NORETURN`, `KERNAUX_PRINTF`, `KERNAUX_RETURNS_TWICE`, + `KERNAUX_SECTION`, `KERNAUX_UNUSED`, `KERNAUX_USED` have been added. +* `` - new header. It's printf format parser. + +### Other changes + +* `` - fix a bug that was making the allocator unusable. +* `` - 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. +* `` - the header has been removed. +* `` - 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. +* `` - new header. It's a free list memory allocator. +* `` - new header. It's a set of macros. +* `` - macro `KERNAUX_PRIVATE_FIELD` has been added. +* `` - macro `KERNAUX_PROTECTED_FIELD` has been added. +* `` - macros `KERNAUX_BITS[8|16|32|64]` have been added. +* `` - macros `KERNAUX_CONTAINER_OF` have been added. +* `` - new header. It's a generic memory allocator + interface. +* `` - 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. +* `` - API has been completely changed. +* `` - header has been removed. +* `` - API has been completely changed. +* `` - Easter egg has been removed. +* `` - 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`. +* `` - new header. It's a memory map. + + + +0.3.0 (2022-05-28) +------------------ + +134 files changed, 6149 insertions(+), 1770 deletions(-) + +### Breaking changes + +* `` - a semicolon is required after macros. +* `` - 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`. +* `` - added functions `atoi`, `isdigit`, `isspace`. +* `` - added functions `kernaux_[u|i]toa`, `kernaux_[u|i]toa16` + and related constants. + +### Other changes + +* Assertions are used more broadly. +* `` - 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. +* `` - 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 + ``. +* `` - removed preprocessor directives that can disable panic + calls and returns. + +### New features + +* `` is stable now. It's a simple command line parser. + +### Other changes + +* Now `./configure` options also follow semantic versioning. +* Bug fixes in ``. + + + +0.1.0 (2022-01-20) +------------------ + +Initial release. diff --git a/README b/README new file mode 120000 index 0000000..42061c0 --- /dev/null +++ b/README @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1ca6878 --- /dev/null +++ b/README.md @@ -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 `` and +`` 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 +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 : + 0: 0f 20 c0 mov %cr0,%eax + 3: c3 ret + +00000004 : + 4: 0f 20 e0 mov %cr4,%eax + 7: c3 ret + +00000008 : + 8: 8b 44 24 04 mov 0x4(%esp),%eax + c: 0f 22 c0 mov %eax,%cr0 + f: c3 ret + +00000010 : + 10: 8b 44 24 04 mov 0x4(%esp),%eax + 14: 0f 22 d8 mov %eax,%cr3 + 17: c3 ret + +00000018 : + 18: 8b 44 24 04 mov 0x4(%esp),%eax + 1c: 0f 22 e0 mov %eax,%cr4 + 1f: c3 ret +``` diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..faef31a --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.7.0 diff --git a/VERSION_SO b/VERSION_SO new file mode 100644 index 0000000..f730b58 --- /dev/null +++ b/VERSION_SO @@ -0,0 +1 @@ +0:0:0 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..b273a20 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +set -eux + +exec autoreconf -isf -Wall diff --git a/bindings/mruby/.gitignore b/bindings/mruby/.gitignore new file mode 100644 index 0000000..3bf69b1 --- /dev/null +++ b/bindings/mruby/.gitignore @@ -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 diff --git a/bindings/mruby/.rubocop.yml b/bindings/mruby/.rubocop.yml new file mode 100644 index 0000000..7d92103 --- /dev/null +++ b/bindings/mruby/.rubocop.yml @@ -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 diff --git a/bindings/mruby/Gemfile b/bindings/mruby/Gemfile new file mode 100644 index 0000000..50f4deb --- /dev/null +++ b/bindings/mruby/Gemfile @@ -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' diff --git a/bindings/mruby/README.md b/bindings/mruby/README.md new file mode 100644 index 0000000..1a42a7b --- /dev/null +++ b/bindings/mruby/README.md @@ -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 +``` diff --git a/bindings/mruby/Rakefile b/bindings/mruby/Rakefile new file mode 100644 index 0000000..d71d39f --- /dev/null +++ b/bindings/mruby/Rakefile @@ -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 diff --git a/bindings/mruby/bin/setup b/bindings/mruby/bin/setup new file mode 100755 index 0000000..b0f0b0a --- /dev/null +++ b/bindings/mruby/bin/setup @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eux + +bundle install + +# Do any other automated setup that you need to do here diff --git a/bindings/mruby/build_config.rb b/bindings/mruby/build_config.rb new file mode 100644 index 0000000..2c4a639 --- /dev/null +++ b/bindings/mruby/build_config.rb @@ -0,0 +1,6 @@ +MRuby::Build.new do |conf| + conf.toolchain + conf.enable_test + conf.gem '.' + conf.gem core: 'mruby-bin-mirb' +end diff --git a/bindings/mruby/mrbgem.rake b/bindings/mruby/mrbgem.rake new file mode 100644 index 0000000..9093b24 --- /dev/null +++ b/bindings/mruby/mrbgem.rake @@ -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 diff --git a/bindings/mruby/mrblib/kernaux.rb b/bindings/mruby/mrblib/kernaux.rb new file mode 100644 index 0000000..456de38 --- /dev/null +++ b/bindings/mruby/mrblib/kernaux.rb @@ -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 diff --git a/bindings/mruby/src/assert.c b/bindings/mruby/src/assert.c new file mode 100644 index 0000000..0942a99 --- /dev/null +++ b/bindings/mruby/src/assert.c @@ -0,0 +1,74 @@ +#include "main.h" + +#include +#include +#include + +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(); +} diff --git a/bindings/mruby/src/cmdline.c b/bindings/mruby/src/cmdline.c new file mode 100644 index 0000000..9dd1aa7 --- /dev/null +++ b/bindings/mruby/src/cmdline.c @@ -0,0 +1,68 @@ +#include "main.h" + +#include +#include +#include + +#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 diff --git a/bindings/mruby/src/dynarg.c b/bindings/mruby/src/dynarg.c new file mode 100644 index 0000000..d45b72c --- /dev/null +++ b/bindings/mruby/src/dynarg.c @@ -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; +} diff --git a/bindings/mruby/src/dynarg.h b/bindings/mruby/src/dynarg.h new file mode 100644 index 0000000..239751e --- /dev/null +++ b/bindings/mruby/src/dynarg.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_DYNARG +#define INCLUDED_DYNARG + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + +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 + +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 diff --git a/bindings/mruby/src/main.c b/bindings/mruby/src/main.c new file mode 100644 index 0000000..d47e165 --- /dev/null +++ b/bindings/mruby/src/main.c @@ -0,0 +1,64 @@ +#include "main.h" + +#include + +#include + +#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]; +} diff --git a/bindings/mruby/src/main.h b/bindings/mruby/src/main.h new file mode 100644 index 0000000..a38d805 --- /dev/null +++ b/bindings/mruby/src/main.h @@ -0,0 +1,30 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include +#include + +#include +#include +#include +#include +#include + +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 diff --git a/bindings/mruby/src/ntoa.c b/bindings/mruby/src/ntoa.c new file mode 100644 index 0000000..f8f9c03 --- /dev/null +++ b/bindings/mruby/src/ntoa.c @@ -0,0 +1,298 @@ +#include "main.h" + +#include +#include +#include + +#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 diff --git a/bindings/mruby/src/printf.c b/bindings/mruby/src/printf.c new file mode 100644 index 0000000..71bc1ef --- /dev/null +++ b/bindings/mruby/src/printf.c @@ -0,0 +1,144 @@ +#include "main.h" +#include "dynarg.h" + +#include +#include +#include +#include + +#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 diff --git a/bindings/mruby/src/version.c b/bindings/mruby/src/version.c new file mode 100644 index 0000000..12802b7 --- /dev/null +++ b/bindings/mruby/src/version.c @@ -0,0 +1,49 @@ +#include "main.h" + +#include +#include + +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 +} diff --git a/bindings/mruby/test/assert.rb b/bindings/mruby/test/assert.rb new file mode 100644 index 0000000..338d16b --- /dev/null +++ b/bindings/mruby/test/assert.rb @@ -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 diff --git a/bindings/mruby/test/cmdline.rb b/bindings/mruby/test/cmdline.rb new file mode 100644 index 0000000..c24536c --- /dev/null +++ b/bindings/mruby/test/cmdline.rb @@ -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 diff --git a/bindings/mruby/test/ntoa.rb b/bindings/mruby/test/ntoa.rb new file mode 100644 index 0000000..532b962 --- /dev/null +++ b/bindings/mruby/test/ntoa.rb @@ -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 diff --git a/bindings/mruby/test/sprintf.rb b/bindings/mruby/test/sprintf.rb new file mode 100644 index 0000000..affabf1 --- /dev/null +++ b/bindings/mruby/test/sprintf.rb @@ -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 diff --git a/bindings/python/.gitignore b/bindings/python/.gitignore new file mode 100644 index 0000000..f6a85b8 --- /dev/null +++ b/bindings/python/.gitignore @@ -0,0 +1,2 @@ +/dist/ +/src/kernaux.egg-info/ diff --git a/bindings/python/README.md b/bindings/python/README.md new file mode 100644 index 0000000..8022c86 --- /dev/null +++ b/bindings/python/README.md @@ -0,0 +1,5 @@ +kernaux +======= + +Binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary +library for kernel development. diff --git a/bindings/python/pyproject.toml b/bindings/python/pyproject.toml new file mode 100644 index 0000000..374b58c --- /dev/null +++ b/bindings/python/pyproject.toml @@ -0,0 +1,6 @@ +[build-system] +requires = [ + "setuptools>=42", + "wheel" +] +build-backend = "setuptools.build_meta" diff --git a/bindings/python/setup.cfg b/bindings/python/setup.cfg new file mode 100644 index 0000000..9a56fe9 --- /dev/null +++ b/bindings/python/setup.cfg @@ -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 diff --git a/bindings/python/src/kernaux/__init__.py b/bindings/python/src/kernaux/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/bindings/python/src/kernaux/example.py b/bindings/python/src/kernaux/example.py new file mode 100644 index 0000000..c929f88 --- /dev/null +++ b/bindings/python/src/kernaux/example.py @@ -0,0 +1,2 @@ +def add_one(number): + return number + 1 diff --git a/bindings/ruby/.gitignore b/bindings/ruby/.gitignore new file mode 100644 index 0000000..4834e46 --- /dev/null +++ b/bindings/ruby/.gitignore @@ -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?--* diff --git a/bindings/ruby/.rubocop.yml b/bindings/ruby/.rubocop.yml new file mode 100644 index 0000000..faf698b --- /dev/null +++ b/bindings/ruby/.rubocop.yml @@ -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 diff --git a/bindings/ruby/.simplecov b/bindings/ruby/.simplecov new file mode 100644 index 0000000..8c6bdb8 --- /dev/null +++ b/bindings/ruby/.simplecov @@ -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 diff --git a/bindings/ruby/.yardopts b/bindings/ruby/.yardopts new file mode 100644 index 0000000..505c853 --- /dev/null +++ b/bindings/ruby/.yardopts @@ -0,0 +1,4 @@ +--markup markdown +--readme README.md +--protected +--private diff --git a/bindings/ruby/Gemfile b/bindings/ruby/Gemfile new file mode 100644 index 0000000..b455047 --- /dev/null +++ b/bindings/ruby/Gemfile @@ -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 diff --git a/bindings/ruby/README.md b/bindings/ruby/README.md new file mode 100644 index 0000000..1f4eef2 --- /dev/null +++ b/bindings/ruby/README.md @@ -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 +``` diff --git a/bindings/ruby/Rakefile b/bindings/ruby/Rakefile new file mode 100644 index 0000000..5bf5204 --- /dev/null +++ b/bindings/ruby/Rakefile @@ -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 diff --git a/bindings/ruby/bin/console b/bindings/ruby/bin/console new file mode 100755 index 0000000..bde46c0 --- /dev/null +++ b/bindings/ruby/bin/console @@ -0,0 +1,8 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require 'bundler/setup' +require 'kernaux' +require 'pry' + +Pry.start diff --git a/bindings/ruby/bin/setup b/bindings/ruby/bin/setup new file mode 100755 index 0000000..b0f0b0a --- /dev/null +++ b/bindings/ruby/bin/setup @@ -0,0 +1,7 @@ +#!/bin/sh + +set -eux + +bundle install + +# Do any other automated setup that you need to do here diff --git a/bindings/ruby/ext/default/assert.c b/bindings/ruby/ext/default/assert.c new file mode 100644 index 0000000..b4a3340 --- /dev/null +++ b/bindings/ruby/ext/default/assert.c @@ -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; +} diff --git a/bindings/ruby/ext/default/cmdline.c b/bindings/ruby/ext/default/cmdline.c new file mode 100644 index 0000000..ca30b23 --- /dev/null +++ b/bindings/ruby/ext/default/cmdline.c @@ -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 diff --git a/bindings/ruby/ext/default/dynarg.c b/bindings/ruby/ext/default/dynarg.c new file mode 100644 index 0000000..d45b72c --- /dev/null +++ b/bindings/ruby/ext/default/dynarg.c @@ -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; +} diff --git a/bindings/ruby/ext/default/dynarg.h b/bindings/ruby/ext/default/dynarg.h new file mode 100644 index 0000000..239751e --- /dev/null +++ b/bindings/ruby/ext/default/dynarg.h @@ -0,0 +1,42 @@ +#ifndef INCLUDED_DYNARG +#define INCLUDED_DYNARG + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + +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 + +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 diff --git a/bindings/ruby/ext/default/extconf.rb b/bindings/ruby/ext/default/extconf.rb new file mode 100644 index 0000000..e370b63 --- /dev/null +++ b/bindings/ruby/ext/default/extconf.rb @@ -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' diff --git a/bindings/ruby/ext/default/main.c b/bindings/ruby/ext/default/main.c new file mode 100644 index 0000000..2578858 --- /dev/null +++ b/bindings/ruby/ext/default/main.c @@ -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 +} diff --git a/bindings/ruby/ext/default/main.h b/bindings/ruby/ext/default/main.h new file mode 100644 index 0000000..ddbb2ff --- /dev/null +++ b/bindings/ruby/ext/default/main.h @@ -0,0 +1,34 @@ +#ifndef __MAIN_H__ +#define __MAIN_H__ + +#include +#include + +#include +#include +#include +#include +#include + +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 diff --git a/bindings/ruby/ext/default/ntoa.c b/bindings/ruby/ext/default/ntoa.c new file mode 100644 index 0000000..208ea24 --- /dev/null +++ b/bindings/ruby/ext/default/ntoa.c @@ -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 diff --git a/bindings/ruby/ext/default/printf.c b/bindings/ruby/ext/default/printf.c new file mode 100644 index 0000000..e73ddf0 --- /dev/null +++ b/bindings/ruby/ext/default/printf.c @@ -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 diff --git a/bindings/ruby/ext/default/version.c b/bindings/ruby/ext/default/version.c new file mode 100644 index 0000000..c4507d7 --- /dev/null +++ b/bindings/ruby/ext/default/version.c @@ -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 +} diff --git a/bindings/ruby/kernaux.gemspec b/bindings/ruby/kernaux.gemspec new file mode 100644 index 0000000..1dec663 --- /dev/null +++ b/bindings/ruby/kernaux.gemspec @@ -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 diff --git a/bindings/ruby/lib/kernaux.rb b/bindings/ruby/lib/kernaux.rb new file mode 100644 index 0000000..9b356c9 --- /dev/null +++ b/bindings/ruby/lib/kernaux.rb @@ -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' diff --git a/bindings/ruby/lib/kernaux/assert.rb b/bindings/ruby/lib/kernaux/assert.rb new file mode 100644 index 0000000..f9c6d03 --- /dev/null +++ b/bindings/ruby/lib/kernaux/assert.rb @@ -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 diff --git a/bindings/ruby/lib/kernaux/cmdline.rb b/bindings/ruby/lib/kernaux/cmdline.rb new file mode 100644 index 0000000..eb52d4f --- /dev/null +++ b/bindings/ruby/lib/kernaux/cmdline.rb @@ -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] command line arguments + # + # @raise [CmdlineError] syntax is invalid + ## +end + +# rubocop:enable Style/Documentation diff --git a/bindings/ruby/lib/kernaux/errors.rb b/bindings/ruby/lib/kernaux/errors.rb new file mode 100644 index 0000000..1789e00 --- /dev/null +++ b/bindings/ruby/lib/kernaux/errors.rb @@ -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 diff --git a/bindings/ruby/lib/kernaux/ntoa.rb b/bindings/ruby/lib/kernaux/ntoa.rb new file mode 100644 index 0000000..cbb2081 --- /dev/null +++ b/bindings/ruby/lib/kernaux/ntoa.rb @@ -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 diff --git a/bindings/ruby/lib/kernaux/version.rb b/bindings/ruby/lib/kernaux/version.rb new file mode 100644 index 0000000..4c74da7 --- /dev/null +++ b/bindings/ruby/lib/kernaux/version.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/assert/panic_spec.rb b/bindings/ruby/spec/lib/kernaux/assert/panic_spec.rb new file mode 100644 index 0000000..8399e2c --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/assert/panic_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/cmdline_spec.rb b/bindings/ruby/spec/lib/kernaux/cmdline_spec.rb new file mode 100644 index 0000000..280d504 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/cmdline_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/itoa10_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/itoa10_spec.rb new file mode 100644 index 0000000..74bf680 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/itoa10_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/itoa16_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/itoa16_spec.rb new file mode 100644 index 0000000..30a235b --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/itoa16_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/itoa2_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/itoa2_spec.rb new file mode 100644 index 0000000..3e8d680 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/itoa2_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/itoa8_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/itoa8_spec.rb new file mode 100644 index 0000000..41b0cfb --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/itoa8_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/itoa_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/itoa_spec.rb new file mode 100644 index 0000000..86bb230 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/itoa_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/utoa10_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/utoa10_spec.rb new file mode 100644 index 0000000..3f287ee --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/utoa10_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/utoa16_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/utoa16_spec.rb new file mode 100644 index 0000000..21b02b7 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/utoa16_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/utoa2_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/utoa2_spec.rb new file mode 100644 index 0000000..cb90180 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/utoa2_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/utoa8_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/utoa8_spec.rb new file mode 100644 index 0000000..164252e --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/utoa8_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/ntoa/utoa_spec.rb b/bindings/ruby/spec/lib/kernaux/ntoa/utoa_spec.rb new file mode 100644 index 0000000..3ba6fce --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/ntoa/utoa_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux/sprintf_spec.rb b/bindings/ruby/spec/lib/kernaux/sprintf_spec.rb new file mode 100644 index 0000000..e172052 --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux/sprintf_spec.rb @@ -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 diff --git a/bindings/ruby/spec/lib/kernaux_spec.rb b/bindings/ruby/spec/lib/kernaux_spec.rb new file mode 100644 index 0000000..932373a --- /dev/null +++ b/bindings/ruby/spec/lib/kernaux_spec.rb @@ -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 diff --git a/bindings/ruby/spec/spec_helper.rb b/bindings/ruby/spec/spec_helper.rb new file mode 100644 index 0000000..7d7af85 --- /dev/null +++ b/bindings/ruby/spec/spec_helper.rb @@ -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 diff --git a/bindings/rust/.gitignore b/bindings/rust/.gitignore new file mode 100644 index 0000000..042776a --- /dev/null +++ b/bindings/rust/.gitignore @@ -0,0 +1,2 @@ +/Cargo.lock +/target/ diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 0000000..d1bd533 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "kernaux", + "kernaux-sys", +] diff --git a/bindings/rust/kernaux-sys/Cargo.toml b/bindings/rust/kernaux-sys/Cargo.toml new file mode 100644 index 0000000..3617fbe --- /dev/null +++ b/bindings/rust/kernaux-sys/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "kernaux-sys" +version = "0.7.0" +authors = ["Alex Kotov "] +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"] diff --git a/bindings/rust/kernaux-sys/README.md b/bindings/rust/kernaux-sys/README.md new file mode 100644 index 0000000..2cf7222 --- /dev/null +++ b/bindings/rust/kernaux-sys/README.md @@ -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. diff --git a/bindings/rust/kernaux-sys/src/assert.rs b/bindings/rust/kernaux-sys/src/assert.rs new file mode 100644 index 0000000..3d0d569 --- /dev/null +++ b/bindings/rust/kernaux-sys/src/assert.rs @@ -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; +} + +#[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"); + } + } +} diff --git a/bindings/rust/kernaux-sys/src/cmdline.rs b/bindings/rust/kernaux-sys/src/cmdline.rs new file mode 100644 index 0000000..79b6ec1 --- /dev/null +++ b/bindings/rust/kernaux-sys/src/cmdline.rs @@ -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", + ); + } +} diff --git a/bindings/rust/kernaux-sys/src/lib.rs b/bindings/rust/kernaux-sys/src/lib.rs new file mode 100644 index 0000000..b8c022e --- /dev/null +++ b/bindings/rust/kernaux-sys/src/lib.rs @@ -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, *}; diff --git a/bindings/rust/kernaux-sys/src/ntoa.rs b/bindings/rust/kernaux-sys/src/ntoa.rs new file mode 100644 index 0000000..07213f0 --- /dev/null +++ b/bindings/rust/kernaux-sys/src/ntoa.rs @@ -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) }); + } +} diff --git a/bindings/rust/kernaux-sys/src/version.rs b/bindings/rust/kernaux-sys/src/version.rs new file mode 100644 index 0000000..506a26e --- /dev/null +++ b/bindings/rust/kernaux-sys/src/version.rs @@ -0,0 +1,7 @@ +pub fn with_cmdline() -> bool { + cfg!(feature = "cmdline") +} + +pub fn with_ntoa() -> bool { + cfg!(feature = "ntoa") +} diff --git a/bindings/rust/kernaux/Cargo.toml b/bindings/rust/kernaux/Cargo.toml new file mode 100644 index 0000000..930dc43 --- /dev/null +++ b/bindings/rust/kernaux/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "kernaux" +version = "0.7.0" +authors = ["Alex Kotov "] +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 diff --git a/bindings/rust/kernaux/README.md b/bindings/rust/kernaux/README.md new file mode 100644 index 0000000..83255d8 --- /dev/null +++ b/bindings/rust/kernaux/README.md @@ -0,0 +1,8 @@ +kernaux +======= + +[![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) + +Safe binding to [libkernaux](https://github.com/tailix/libkernaux) - auxiliary +library for kernel development. diff --git a/bindings/rust/kernaux/src/assert.rs b/bindings/rust/kernaux/src/assert.rs new file mode 100644 index 0000000..3e7dee2 --- /dev/null +++ b/bindings/rust/kernaux/src/assert.rs @@ -0,0 +1,33 @@ +use ctor::ctor; +use libc::{c_char, c_int}; +use std::ffi::CStr; + +#[ctor] +unsafe fn ctor() { + kernaux_sys::assert_cb = Some(assert_cb); +} + +unsafe extern "C" fn assert_cb( + file: *const c_char, + line: c_int, + msg: *const c_char, +) { + let file = CStr::from_ptr(file).to_str().unwrap(); + let msg = CStr::from_ptr(msg).to_str().unwrap(); + + panic!("{}:{}:{}", file, line, msg); +} + +#[cfg(test)] +mod tests { + use std::ffi::CString; + + #[test] + #[should_panic(expected = "foo.rs:123:bar")] + fn default() { + let file = CString::new("foo.rs").unwrap(); + let msg = CString::new("bar").unwrap(); + + unsafe { kernaux_sys::assert_do(file.as_ptr(), 123, msg.as_ptr()) } + } +} diff --git a/bindings/rust/kernaux/src/lib.rs b/bindings/rust/kernaux/src/lib.rs new file mode 100644 index 0000000..7ad1f67 --- /dev/null +++ b/bindings/rust/kernaux/src/lib.rs @@ -0,0 +1,52 @@ +pub mod assert; +pub mod version; + +#[cfg(feature = "ntoa")] +pub mod ntoa; + +#[cfg(feature = "ntoa")] +pub use ntoa::{ + itoa, itoa10, itoa16, itoa2, itoa8, utoa, utoa10, utoa16, utoa2, utoa8, + Config as NtoaConfig, Error as NtoaError, Result as NtoaResult, +}; + +#[cfg(test)] +#[cfg(feature = "ntoa")] +mod tests { + use super::*; + + #[test] + #[cfg(feature = "ntoa")] + fn test_ntoa() { + assert_eq!(NtoaConfig::try_from('x').unwrap().base(), 16); + assert_eq!(NtoaConfig::try_from('x').unwrap().uppercase(), false); + assert_eq!(NtoaConfig::try_from('x'), NtoaConfig::try_from(16)); + assert_eq!(NtoaConfig::try_from('X'), NtoaConfig::try_from(-16)); + + assert_eq!(utoa(123, Default::default(), None), Ok("123".into())); + assert_eq!(itoa(123, Default::default(), None), Ok("123".into())); + assert_eq!(itoa(-123, Default::default(), None), Ok("-123".into())); + + assert_eq!( + utoa(123, Default::default(), Some("foo")), + Ok("foo123".into()), + ); + assert_eq!( + itoa(123, Default::default(), Some("foo")), + Ok("foo123".into()), + ); + assert_eq!( + itoa(-123, Default::default(), Some("foo")), + Ok("-foo123".into()), + ); + + assert_eq!(utoa2(123), "0b1111011"); + assert_eq!(itoa2(123), "0b1111011"); + assert_eq!(utoa8(0o123), "0o123"); + assert_eq!(itoa8(0o123), "0o123"); + assert_eq!(utoa10(123), "123"); + assert_eq!(itoa10(123), "123"); + assert_eq!(utoa16(0x123), "0x123"); + assert_eq!(itoa16(0x123), "0x123"); + } +} diff --git a/bindings/rust/kernaux/src/ntoa.rs b/bindings/rust/kernaux/src/ntoa.rs new file mode 100644 index 0000000..232d3c5 --- /dev/null +++ b/bindings/rust/kernaux/src/ntoa.rs @@ -0,0 +1,771 @@ +use kernaux_sys::ntoa::{ + itoa as kernaux_itoa, itoa10 as kernaux_itoa10, itoa16 as kernaux_itoa16, + itoa2 as kernaux_itoa2, itoa8 as kernaux_itoa8, utoa as kernaux_utoa, + utoa10 as kernaux_utoa10, utoa16 as kernaux_utoa16, utoa2 as kernaux_utoa2, + utoa8 as kernaux_utoa8, ITOA10_BUFFER_SIZE, ITOA16_BUFFER_SIZE, + ITOA2_BUFFER_SIZE, ITOA8_BUFFER_SIZE, ITOA_MIN_BUFFER_SIZE, MAX_PREFIX_LEN, + UTOA10_BUFFER_SIZE, UTOA16_BUFFER_SIZE, UTOA2_BUFFER_SIZE, + UTOA8_BUFFER_SIZE, UTOA_MIN_BUFFER_SIZE, +}; + +use std::ffi::{CStr, CString, NulError}; +use std::ptr::null; +use std::str::Utf8Error; + +use libc::c_int; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Config { + base: u8, + uppercase: bool, +} + +pub type Result = std::result::Result; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Error { + PrefixTooLong(usize), + PrefixIncludesNull(NulError), + Utf8(Utf8Error), +} + +pub fn utoa(value: u64, config: Config, prefix: Option<&str>) -> Result { + let prefix = if let Some(prefix) = prefix { + if prefix.len() > MAX_PREFIX_LEN { + return Err(Error::PrefixTooLong(prefix.len())); + } + Some(CString::new(prefix)?) + } else { + None + }; + + utoac(value, config, prefix) +} + +pub fn itoa(value: i64, config: Config, prefix: Option<&str>) -> Result { + let prefix = if let Some(prefix) = prefix { + if prefix.len() > MAX_PREFIX_LEN { + return Err(Error::PrefixTooLong(prefix.len())); + } + Some(CString::new(prefix)?) + } else { + None + }; + + itoac(value, config, prefix) +} + +fn utoac(value: u64, config: Config, prefix: Option) -> Result { + let mut buffer: [i8; UTOA_MIN_BUFFER_SIZE + MAX_PREFIX_LEN] = + [0; UTOA_MIN_BUFFER_SIZE + MAX_PREFIX_LEN]; + + unsafe { + kernaux_utoa( + value, + buffer.as_mut_ptr(), + config.to_c_int(), + prefix + .as_ref() + .map(|prefix| prefix.as_ptr()) + .unwrap_or(null()), + ); + }; + + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str()?; + Ok(String::from(result)) +} + +fn itoac(value: i64, config: Config, prefix: Option) -> Result { + let mut buffer: [i8; ITOA_MIN_BUFFER_SIZE + MAX_PREFIX_LEN] = + [0; ITOA_MIN_BUFFER_SIZE + MAX_PREFIX_LEN]; + + unsafe { + kernaux_itoa( + value, + buffer.as_mut_ptr(), + config.to_c_int(), + prefix + .as_ref() + .map(|prefix| prefix.as_ptr()) + .unwrap_or(null()), + ); + }; + + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str()?; + Ok(String::from(result)) +} + +pub fn utoa2(value: u64) -> String { + let mut buffer: [i8; UTOA2_BUFFER_SIZE] = [0; UTOA2_BUFFER_SIZE]; + unsafe { kernaux_utoa2(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn itoa2(value: i64) -> String { + let mut buffer: [i8; ITOA2_BUFFER_SIZE] = [0; ITOA2_BUFFER_SIZE]; + unsafe { kernaux_itoa2(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn utoa8(value: u64) -> String { + let mut buffer: [i8; UTOA8_BUFFER_SIZE] = [0; UTOA8_BUFFER_SIZE]; + unsafe { kernaux_utoa8(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn itoa8(value: i64) -> String { + let mut buffer: [i8; ITOA8_BUFFER_SIZE] = [0; ITOA8_BUFFER_SIZE]; + unsafe { kernaux_itoa8(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn utoa10(value: u64) -> String { + let mut buffer: [i8; UTOA10_BUFFER_SIZE] = [0; UTOA10_BUFFER_SIZE]; + unsafe { kernaux_utoa10(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn itoa10(value: i64) -> String { + let mut buffer: [i8; ITOA10_BUFFER_SIZE] = [0; ITOA10_BUFFER_SIZE]; + unsafe { kernaux_itoa10(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn utoa16(value: u64) -> String { + let mut buffer: [i8; UTOA16_BUFFER_SIZE] = [0; UTOA16_BUFFER_SIZE]; + unsafe { kernaux_utoa16(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +pub fn itoa16(value: i64) -> String { + let mut buffer: [i8; ITOA16_BUFFER_SIZE] = [0; ITOA16_BUFFER_SIZE]; + unsafe { kernaux_itoa16(value, buffer.as_mut_ptr()) }; + let result = unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + String::from(result) +} + +impl Default for Config { + fn default() -> Self { + Self { + base: 10, + uppercase: false, + } + } +} + +impl Config { + pub fn base(&self) -> u8 { + self.base + } + + pub fn uppercase(&self) -> bool { + self.uppercase + } + + fn to_c_int(self) -> c_int { + if self.uppercase { + (-(self.base as i8)).into() + } else { + self.base.into() + } + } +} + +impl From for Error { + fn from(nul_error: NulError) -> Self { + Self::PrefixIncludesNull(nul_error) + } +} + +impl From for Error { + fn from(utf8_error: Utf8Error) -> Self { + Self::Utf8(utf8_error) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: char) -> std::result::Result { + let base: i64 = match value { + 'b' | 'B' => 2, + 'o' | 'O' => 8, + 'd' | 'D' => 10, + 'h' | 'x' => 16, + 'H' | 'X' => -16, + _ => return Err(()), + }; + + base.try_into() + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: u64) -> std::result::Result { + if (2..=36).contains(&value) { + Ok(Self { + base: value as u8, + uppercase: false, + }) + } else { + Err(()) + } + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: i64) -> std::result::Result { + let uppercase = value < 0; + let value = if value < 0 { -value } else { value }; + + if (2..=36).contains(&value) { + Ok(Self { + base: value as u8, + uppercase, + }) + } else { + Err(()) + } + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: u32) -> std::result::Result { + Self::try_from(value as u64) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: i32) -> std::result::Result { + Self::try_from(value as i64) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: u16) -> std::result::Result { + Self::try_from(value as u64) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: i16) -> std::result::Result { + Self::try_from(value as i64) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: u8) -> std::result::Result { + Self::try_from(value as u64) + } +} + +impl TryFrom for Config { + type Error = (); + + fn try_from(value: i8) -> std::result::Result { + Self::try_from(value as i64) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_utoa_prefix() { + assert_eq!( + utoa(123, Default::default(), Some("foo")), + Ok("foo123".into()), + ); + assert_eq!( + utoa( + 123, + Default::default(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + ), + Ok("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123".into()), + ); + assert_eq!( + utoa( + 123, + Default::default(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + ), + Err(Error::PrefixTooLong(101)), + ); + } + + #[test] + fn test_itoa_prefix() { + assert_eq!( + itoa(123, Default::default(), Some("foo")), + Ok("foo123".into()), + ); + assert_eq!( + itoa(-123, Default::default(), Some("foo")), + Ok("-foo123".into()), + ); + assert_eq!( + itoa( + 123, + Default::default(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + ), + Ok("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa123".into()), + ); + assert_eq!( + itoa( + 123, + Default::default(), + Some("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), + ), + Err(Error::PrefixTooLong(101)), + ); + } + + #[test] + fn test_utoa() { + // binary + assert_eq!( + utoa(0b10110, 'b'.try_into().unwrap(), None), + Ok("10110".into()), + ); + assert_eq!( + utoa(0b10110, 'B'.try_into().unwrap(), None), + Ok("10110".into()), + ); + assert_eq!( + utoa(0b10110, 2.try_into().unwrap(), None), + Ok("10110".into()), + ); + assert_eq!( + utoa(0b10110, (-2).try_into().unwrap(), None), + Ok("10110".into()), + ); + + // octal + assert_eq!( + utoa(0o123, 'o'.try_into().unwrap(), None), + Ok("123".into()), + ); + assert_eq!( + utoa(0o123, 'O'.try_into().unwrap(), None), + Ok("123".into()), + ); + assert_eq!(utoa(0o123, 8.try_into().unwrap(), None), Ok("123".into())); + assert_eq!( + utoa(0o123, (-8).try_into().unwrap(), None), + Ok("123".into()), + ); + + // decimal + assert_eq!(utoa(123, Default::default(), None), Ok("123".into())); + assert_eq!(utoa(123, 'd'.try_into().unwrap(), None), Ok("123".into())); + assert_eq!(utoa(123, 'D'.try_into().unwrap(), None), Ok("123".into())); + assert_eq!(utoa(123, 10.try_into().unwrap(), None), Ok("123".into())); + assert_eq!( + utoa(123, (-10).try_into().unwrap(), None), + Ok("123".into()), + ); + + // hexadecimal + assert_eq!( + utoa(0x123cafe, 'x'.try_into().unwrap(), None), + Ok("123cafe".into()), + ); + assert_eq!( + utoa(0x123cafe, 16.try_into().unwrap(), None), + Ok("123cafe".into()), + ); + assert_eq!( + utoa(0x123cafe, 'X'.try_into().unwrap(), None), + Ok("123CAFE".into()), + ); + assert_eq!( + utoa(0x123cafe, (-16).try_into().unwrap(), None), + Ok("123CAFE".into()), + ); + + // random base: 14 + assert_eq!( + utoa(123456, 14.try_into().unwrap(), None), + Ok("32dc4".into()), + ); + assert_eq!( + utoa(123456, (-14).try_into().unwrap(), None), + Ok("32DC4".into()), + ); + } + + #[test] + fn test_itoa() { + // binary + assert_eq!( + itoa(0b10110, 'b'.try_into().unwrap(), None), + Ok("10110".into()), + ); + assert_eq!( + itoa(-0b10110, 'B'.try_into().unwrap(), None), + Ok("-10110".into()), + ); + assert_eq!( + itoa(-0b10110, 2.try_into().unwrap(), None), + Ok("-10110".into()), + ); + assert_eq!( + itoa(0b10110, (-2).try_into().unwrap(), None), + Ok("10110".into()), + ); + + // octal + assert_eq!( + itoa(-0o123, 'o'.try_into().unwrap(), None), + Ok("-123".into()), + ); + assert_eq!( + itoa(0o123, 'O'.try_into().unwrap(), None), + Ok("123".into()), + ); + assert_eq!(itoa(0o123, 8.try_into().unwrap(), None), Ok("123".into())); + assert_eq!( + itoa(-0o123, (-8).try_into().unwrap(), None), + Ok("-123".into()), + ); + + // decimal + assert_eq!(itoa(123, Default::default(), None), Ok("123".into())); + assert_eq!( + itoa(-123, 'd'.try_into().unwrap(), None), + Ok("-123".into()), + ); + assert_eq!( + itoa(-123, 'D'.try_into().unwrap(), None), + Ok("-123".into()), + ); + assert_eq!(itoa(123, 10.try_into().unwrap(), None), Ok("123".into())); + assert_eq!( + itoa(123, (-10).try_into().unwrap(), None), + Ok("123".into()), + ); + + // hexadecimal + assert_eq!( + itoa(-0x123cafe, 'x'.try_into().unwrap(), None), + Ok("-123cafe".into()), + ); + assert_eq!( + itoa(0x123cafe, 16.try_into().unwrap(), None), + Ok("123cafe".into()), + ); + assert_eq!( + itoa(0x123cafe, 'X'.try_into().unwrap(), None), + Ok("123CAFE".into()), + ); + assert_eq!( + itoa(-0x123cafe, (-16).try_into().unwrap(), None), + Ok("-123CAFE".into()), + ); + + // random base: 14 + assert_eq!( + itoa(123456, 14.try_into().unwrap(), None), + Ok("32dc4".into()), + ); + assert_eq!( + itoa(-123456, (-14).try_into().unwrap(), None), + Ok("-32DC4".into()), + ); + assert_eq!( + itoa(-123456, 14.try_into().unwrap(), None), + Ok("-32dc4".into()), + ); + assert_eq!( + itoa(123456, (-14).try_into().unwrap(), None), + Ok("32DC4".into()), + ); + } + + #[test] + fn test_utoa2() { + assert_eq!(utoa2(0), "0b0"); + assert_eq!(utoa2(1), "0b1"); + assert_eq!(utoa2(123), "0b1111011"); + assert_eq!( + utoa2(u64::MAX), + "0b1111111111111111111111111111111111111111111111111111111111111111", + ); + } + + #[test] + fn test_itoa2() { + assert_eq!(itoa2(0), "0b0"); + assert_eq!(itoa2(1), "0b1"); + assert_eq!(itoa2(-1), "-0b1"); + assert_eq!(itoa2(123), "0b1111011"); + assert_eq!(itoa2(-123), "-0b1111011"); + assert_eq!( + itoa2(i64::MAX), + "0b111111111111111111111111111111111111111111111111111111111111111", + ); + assert_eq!( + itoa2(i64::MIN), + "-0b1000000000000000000000000000000000000000000000000000000000000000", + ); + } + + #[test] + fn test_utoa8() { + assert_eq!(utoa8(0), "0o0"); + assert_eq!(utoa8(1), "0o1"); + assert_eq!(utoa8(0o123), "0o123"); + assert_eq!(utoa8(u64::MAX), "0o1777777777777777777777"); + } + + #[test] + fn test_itoa8() { + assert_eq!(itoa8(0), "0o0"); + assert_eq!(itoa8(1), "0o1"); + assert_eq!(itoa8(-1), "-0o1"); + assert_eq!(itoa8(0o123), "0o123"); + assert_eq!(itoa8(-0o123), "-0o123"); + assert_eq!(itoa8(i64::MAX), "0o777777777777777777777"); + assert_eq!(itoa8(i64::MIN), "-0o1000000000000000000000"); + } + + #[test] + fn test_utoa10() { + assert_eq!(utoa10(0), "0"); + assert_eq!(utoa10(1), "1"); + assert_eq!(utoa10(123), "123"); + assert_eq!(utoa10(u64::MAX), "18446744073709551615"); + } + + #[test] + fn test_itoa10() { + assert_eq!(itoa10(0), "0"); + assert_eq!(itoa10(1), "1"); + assert_eq!(itoa10(-1), "-1"); + assert_eq!(itoa10(123), "123"); + assert_eq!(itoa10(-123), "-123"); + assert_eq!(itoa10(i64::MAX), "9223372036854775807"); + assert_eq!(itoa10(i64::MIN), "-9223372036854775808"); + } + + #[test] + fn test_utoa16() { + assert_eq!(utoa16(0), "0x0"); + assert_eq!(utoa16(1), "0x1"); + assert_eq!(utoa16(0x123), "0x123"); + assert_eq!(utoa16(u64::MAX), "0xffffffffffffffff"); + } + + #[test] + fn test_itoa16() { + assert_eq!(itoa16(0), "0x0"); + assert_eq!(itoa16(1), "0x1"); + assert_eq!(itoa16(-1), "-0x1"); + assert_eq!(itoa16(0x123), "0x123"); + assert_eq!(itoa16(-0x123), "-0x123"); + assert_eq!(itoa16(i64::MAX), "0x7fffffffffffffff"); + assert_eq!(itoa16(i64::MIN), "-0x8000000000000000"); + } + + #[test] + fn test_config_default() { + assert_eq!( + Config::default(), + Config { + base: 10, + uppercase: false, + }, + ); + } + + #[test] + fn test_try_config_from_unsigned() { + assert_eq!(Config::try_from(12u8), Config::try_from(12u64)); + assert_eq!(Config::try_from(12i8), Config::try_from(12i64)); + assert_eq!(Config::try_from(12u16), Config::try_from(12u64)); + assert_eq!(Config::try_from(12i16), Config::try_from(12i64)); + assert_eq!(Config::try_from(12u32), Config::try_from(12u64)); + assert_eq!(Config::try_from(12i32), Config::try_from(12i64)); + } + + #[test] + fn test_try_config_from_u64() { + assert_eq!(Config::try_from(0u64), Err(())); + assert_eq!(Config::try_from(1u64), Err(())); + assert_eq!(Config::try_from(37u64), Err(())); + assert_eq!( + Config::try_from(2u64), + Ok(Config { + base: 2, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from(10u64), + Ok(Config { + base: 10, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from(36u64), + Ok(Config { + base: 36, + uppercase: false, + }), + ); + } + + #[test] + fn test_try_config_from_i64() { + assert_eq!(Config::try_from(0i64), Err(())); + assert_eq!(Config::try_from(1i64), Err(())); + assert_eq!(Config::try_from(-1i64), Err(())); + assert_eq!(Config::try_from(37i64), Err(())); + assert_eq!(Config::try_from(-37i64), Err(())); + assert_eq!( + Config::try_from(2i64), + Ok(Config { + base: 2, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from(-2i64), + Ok(Config { + base: 2, + uppercase: true, + }), + ); + assert_eq!( + Config::try_from(10i64), + Ok(Config { + base: 10, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from(-10i64), + Ok(Config { + base: 10, + uppercase: true, + }), + ); + assert_eq!( + Config::try_from(36i64), + Ok(Config { + base: 36, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from(-36i64), + Ok(Config { + base: 36, + uppercase: true, + }), + ); + } + + #[test] + fn test_try_config_from_char() { + assert_eq!(Config::try_from('a'), Err(())); + assert_eq!(Config::try_from('A'), Err(())); + assert_eq!(Config::try_from('z'), Err(())); + assert_eq!(Config::try_from('Z'), Err(())); + assert_eq!( + Config::try_from('b'), + Ok(Config { + base: 2, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('B'), + Ok(Config { + base: 2, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('o'), + Ok(Config { + base: 8, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('O'), + Ok(Config { + base: 8, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('d'), + Ok(Config { + base: 10, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('D'), + Ok(Config { + base: 10, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('h'), + Ok(Config { + base: 16, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('H'), + Ok(Config { + base: 16, + uppercase: true, + }), + ); + assert_eq!( + Config::try_from('x'), + Ok(Config { + base: 16, + uppercase: false, + }), + ); + assert_eq!( + Config::try_from('X'), + Ok(Config { + base: 16, + uppercase: true, + }), + ); + } +} diff --git a/bindings/rust/kernaux/src/version.rs b/bindings/rust/kernaux/src/version.rs new file mode 120000 index 0000000..a9ed635 --- /dev/null +++ b/bindings/rust/kernaux/src/version.rs @@ -0,0 +1 @@ +../../kernaux-sys/src/version.rs \ No newline at end of file diff --git a/bindings/rust/rustfmt.toml b/bindings/rust/rustfmt.toml new file mode 100644 index 0000000..df99c69 --- /dev/null +++ b/bindings/rust/rustfmt.toml @@ -0,0 +1 @@ +max_width = 80 diff --git a/build/.keep b/build/.keep new file mode 100644 index 0000000..e69de29 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..00709d9 --- /dev/null +++ b/configure.ac @@ -0,0 +1,413 @@ +############################ +# Specify program versions # +############################ + +AC_PREREQ([2.70]) +LT_PREREQ([2.4.6]) + + + +################################## +# Initialize Autoconf & Automake # +################################## + +AC_INIT([libkernaux], + m4_normalize(m4_include([VERSION])), + [https://github.com/tailix/libkernaux/issues], + [libkernaux], + [https://github.com/tailix/libkernaux]) + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST + +AC_CONFIG_MACRO_DIRS([m4]) +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_SRCDIR([src/runtime.c]) +AC_CONFIG_FILES([ + Makefile + libkernaux.pc + examples/Makefile + fixtures/Makefile + fixtures/multiboot2_bin_examples_gen.c + include/Makefile + libc/Makefile + libc/include/Makefile + include/kernaux/version.h + tests/Makefile + tests/test_multiboot2_header_print.c + tests/test_multiboot2_info_print.c +]) + +AM_INIT_AUTOMAKE([1.16 subdir-objects]) + +AC_SUBST([PACKAGE_DESCR], ['Auxiliary library for kernel development']) +AC_SUBST([PACKAGE_VERSION_SO], m4_normalize(m4_include([VERSION_SO]))) + + + +############### +# Define args # +############### + +dnl Features (enabled by default) +AC_ARG_ENABLE([assert], AS_HELP_STRING([--disable-assert], [disable assertions])) +AC_ARG_ENABLE([float], AS_HELP_STRING([--disable-float], [disable floating-point arithmetic])) +AC_ARG_ENABLE([werror], AS_HELP_STRING([--disable-werror], [disable -Werror])) + +dnl Features (disabled by default) +AC_ARG_ENABLE([fixtures], AS_HELP_STRING([--enable-fixtures], [enable fixtures for tests and bindings])) +AC_ARG_ENABLE([freestanding], AS_HELP_STRING([--enable-freestanding], [build for freestanding environment])) +AC_ARG_ENABLE([checks], AS_HELP_STRING([--enable-checks], [enable usual tests and examples])) +AC_ARG_ENABLE([checks-all], AS_HELP_STRING([--enable-checks-all], [enable all checks])) +AC_ARG_ENABLE([checks-cppcheck], AS_HELP_STRING([--enable-checks-cppcheck], [enable cppcheck])) +AC_ARG_ENABLE([checks-pthreads], AS_HELP_STRING([--enable-checks-pthreads], [enable tests that require pthreads])) +AC_ARG_ENABLE([checks-python], AS_HELP_STRING([--enable-checks-python], [enable tests that require Python 3 with YAML and Jinja2])) +AC_ARG_ENABLE([split-libc], AS_HELP_STRING([--enable-split-libc], [split off libc])) + +dnl Features (with parameter) +AC_ARG_ENABLE([pkg-config], AS_HELP_STRING([--enable-pkg-config@<:@=PATH@:>@], [install pkg-config files @<:@PATH='${libdir}/pkgconfig'@:>@])) + +dnl Packages (enabled by default) +AC_ARG_WITH( [all], AS_HELP_STRING([--without-all], [without all default packages])) +AC_ARG_WITH( [arch-all], AS_HELP_STRING([--without-arch-all], [without all architectures])) +AC_ARG_WITH( [arch-i386], AS_HELP_STRING([--without-arch-i386], [without architecture i386])) +AC_ARG_WITH( [arch-riscv64], AS_HELP_STRING([--without-arch-riscv64], [without architecture riscv64])) +AC_ARG_WITH( [arch-x86-64], AS_HELP_STRING([--without-arch-x86-64], [without architecture x86-64])) +AC_ARG_WITH( [asm], AS_HELP_STRING([--without-asm], [without kernel assembler helpers])) +AC_ARG_WITH( [cmdline], AS_HELP_STRING([--without-cmdline], [without command line parser])) +AC_ARG_WITH( [elf], AS_HELP_STRING([--without-elf], [without ELF utils])) +AC_ARG_WITH( [free-list], AS_HELP_STRING([--without-free-list], [without free list memory allocator])) +AC_ARG_WITH( [mbr], AS_HELP_STRING([--without-mbr], [without MBR utils])) +AC_ARG_WITH( [memmap], AS_HELP_STRING([--without-memmap], [without memory map])) +AC_ARG_WITH( [multiboot2], AS_HELP_STRING([--without-multiboot2], [without Multiboot 2 utils])) +AC_ARG_WITH( [ntoa], AS_HELP_STRING([--without-ntoa], [without itoa/ftoa])) +AC_ARG_WITH( [pfa], AS_HELP_STRING([--without-pfa], [without Page Frame Allocator])) +AC_ARG_WITH( [printf], AS_HELP_STRING([--without-printf], [without printf])) +AC_ARG_WITH( [printf-fmt], AS_HELP_STRING([--without-printf-fmt], [without printf format parser])) +AC_ARG_WITH( [units], AS_HELP_STRING([--without-units], [without measurement units utils])) + +dnl Packages (disabled by default) +AC_ARG_WITH( [libc], AS_HELP_STRING([--with-libc], [with libc replacement])) + + + +################ +# Default args # +################ + +AC_DEFUN([do_enable_checks_all], +[ +if test -z "$enable_checks"; then enable_checks=yes; fi +if test -z "$enable_checks_cppcheck"; then enable_checks_cppcheck=yes; fi +if test -z "$enable_checks_pthreads"; then enable_checks_pthreads=yes; fi +if test -z "$enable_checks_python"; then enable_checks_python=yes; fi +]) +AS_IF([test "$enable_checks_all" = yes], do_enable_checks_all) + +AC_DEFUN([do_without_arch_all], +[ +if test -z "$with_arch_i386"; then with_arch_i386=no; fi +if test -z "$with_arch_riscv64"; then with_arch_riscv64=no; fi +if test -z "$with_arch_x86_64"; then with_arch_x86_64=no; fi +]) +AS_IF([test "$with_arch_all" = no], do_without_arch_all) + +AC_DEFUN([do_without_all], +[ +if test -z "$with_arch_i386"; then with_arch_i386=no; fi +if test -z "$with_arch_riscv64"; then with_arch_riscv64=no; fi +if test -z "$with_arch_x86_64"; then with_arch_x86_64=no; fi +if test -z "$with_asm"; then with_asm=no; fi +if test -z "$with_cmdline"; then with_cmdline=no; fi +if test -z "$with_elf"; then with_elf=no; fi +if test -z "$with_free_list"; then with_free_list=no; fi +if test -z "$with_mbr"; then with_mbr=no; fi +if test -z "$with_memmap"; then with_memmap=no; fi +if test -z "$with_multiboot2"; then with_multiboot2=no; fi +if test -z "$with_ntoa"; then with_ntoa=no; fi +if test -z "$with_pfa"; then with_pfa=no; fi +if test -z "$with_printf"; then with_printf=no; fi +if test -z "$with_printf_fmt"; then with_printf_fmt=no; fi +if test -z "$with_units"; then with_units=no; fi +]) +AS_IF([test "$with_all" = no], do_without_all) + + + +################## +# Normalize args # +################## + +dnl Features (enabled by default) +AS_IF([test "$enable_assert" = no ], [enable_assert=no], [enable_assert=yes]) +AS_IF([test "$enable_float" = no ], [enable_float=no], [enable_float=yes]) +AS_IF([test "$enable_werror" = no ], [enable_werror=no], [enable_werror=yes]) + +dnl Features (disabled by default) +AS_IF([test "$enable_fixtures" = yes], [enable_fixtures=yes], [enable_fixtures=no]) +AS_IF([test "$enable_freestanding" = yes], [enable_freestanding=yes], [enable_freestanding=no]) +AS_IF([test "$enable_checks" = yes], [enable_checks=yes], [enable_checks=no]) +AS_IF([test "$enable_checks_all" = yes], [enable_checks_all=yes], [enable_checks_all=no]) +AS_IF([test "$enable_checks_cppcheck" = yes], [enable_checks_cppcheck=yes], [enable_checks_cppcheck=no]) +AS_IF([test "$enable_checks_pthreads" = yes], [enable_checks_pthreads=yes], [enable_checks_pthreads=no]) +AS_IF([test "$enable_checks_python" = yes], [enable_checks_python=yes], [enable_checks_python=no]) +AS_IF([test "$enable_split_libc" = yes], [enable_split_libc=yes], [enable_split_libc=no]) + +dnl Features (with parameter) +AS_IF([test "$enable_pkg_config" = yes], [enable_pkg_config='${libdir}/pkgconfig']) +AS_IF([test "$enable_pkg_config" = no ], [enable_pkg_config='']) + +dnl Packages (enabled by default) +AS_IF([test "$with_all" = no ], [with_all=no], [with_all=yes]) +AS_IF([test "$with_arch_all" = no ], [with_arch_all=no], [with_arch_all=yes]) +AS_IF([test "$with_arch_i386" = no ], [with_arch_i386=no], [with_arch_i386=yes]) +AS_IF([test "$with_arch_riscv64" = no ], [with_arch_riscv64=no], [with_arch_riscv64=yes]) +AS_IF([test "$with_arch_x86_64" = no ], [with_arch_x86_64=no], [with_arch_x86_64=yes]) +AS_IF([test "$with_asm" = no ], [with_asm=no], [with_asm=yes]) +AS_IF([test "$with_cmdline" = no ], [with_cmdline=no], [with_cmdline=yes]) +AS_IF([test "$with_elf" = no ], [with_elf=no], [with_elf=yes]) +AS_IF([test "$with_free_list" = no ], [with_free_list=no], [with_free_list=yes]) +AS_IF([test "$with_mbr" = no ], [with_mbr=no], [with_mbr=yes]) +AS_IF([test "$with_memmap" = no ], [with_memmap=no], [with_memmap=yes]) +AS_IF([test "$with_multiboot2" = no ], [with_multiboot2=no], [with_multiboot2=yes]) +AS_IF([test "$with_ntoa" = no ], [with_ntoa=no], [with_ntoa=yes]) +AS_IF([test "$with_pfa" = no ], [with_pfa=no], [with_pfa=yes]) +AS_IF([test "$with_printf" = no ], [with_printf=no], [with_printf=yes]) +AS_IF([test "$with_printf_fmt" = no ], [with_printf_fmt=no], [with_printf_fmt=yes]) +AS_IF([test "$with_units" = no ], [with_units=no], [with_units=yes]) + +dnl Packages (disabled by default) +AS_IF([test "$with_libc" = yes], [with_libc=yes], [with_libc=no]) + + + +############# +# Test args # +############# + +AS_IF([test "$enable_checks" = yes -a "$enable_freestanding" = yes], AC_MSG_ERROR([can not build freestanding tests])) +AS_IF([test "$enable_fixtures" = yes -a "$enable_freestanding" = yes], AC_MSG_ERROR([can not build freestanding fixtures])) +AS_IF([test "$enable_checks" = yes -a "$with_libc" = yes], AC_MSG_ERROR([can not use package `libc' with tests])) +AS_IF([test "$enable_fixtures" = yes -a "$with_libc" = yes], AC_MSG_ERROR([can not use package `libc' with fixtures])) + +AS_IF([test "$with_printf" = yes -a "$with_ntoa" = no], AC_MSG_ERROR([package `printf' requires package `ntoa'])) +AS_IF([test "$with_printf" = yes -a "$with_printf_fmt" = no], AC_MSG_ERROR([package `printf' requires package `printf-fmt'])) +AS_IF([test "$with_units" = yes -a "$with_ntoa" = no], AC_MSG_ERROR([package `units' requires package `ntoa'])) + + + +######################### +# Automake conditionals # +######################### + +dnl Architecture +AM_CONDITIONAL([ASM_I386], [test "$host_cpu" = i386]) +AM_CONDITIONAL([ASM_RISCV64], [test "$host_cpu" = riscv64]) +AM_CONDITIONAL([ASM_X86_64], [test "$host_cpu" = x86_64]) + +dnl Architecture (additional) +AM_CONDITIONAL([ASM_X86], [test "$host_cpu" = i386 -o "$host_cpu" = x86_64]) + +dnl Features (enabled by default) +AM_CONDITIONAL([ENABLE_ASSERT], [test "$enable_assert" = yes]) +AM_CONDITIONAL([ENABLE_FLOAT], [test "$enable_float" = yes]) +AM_CONDITIONAL([ENABLE_WERROR], [test "$enable_werror" = yes]) + +dnl Features (disabled by default) +AM_CONDITIONAL([ENABLE_FIXTURES], [test "$enable_fixtures" = yes]) +AM_CONDITIONAL([ENABLE_FREESTANDING], [test "$enable_freestanding" = yes]) +AM_CONDITIONAL([ENABLE_CHECKS], [test "$enable_checks" = yes]) +AM_CONDITIONAL([ENABLE_CHECKS_CPPCHECK], [test "$enable_checks_cppcheck" = yes]) +AM_CONDITIONAL([ENABLE_CHECKS_PTHREADS], [test "$enable_checks_pthreads" = yes]) +AM_CONDITIONAL([ENABLE_CHECKS_PYTHON], [test "$enable_checks_python" = yes]) +AM_CONDITIONAL([ENABLE_SPLIT_LIBC], [test "$enable_split_libc" = yes]) + +dnl Features (with parameter) +AM_CONDITIONAL([ENABLE_PKG_CONFIG], [test ! -z "$enable_pkg_config"]) + +dnl Packages (enabled by default) +AM_CONDITIONAL([WITH_ARCH_I386], [test "$with_arch_i386" = yes]) +AM_CONDITIONAL([WITH_ARCH_RISCV64], [test "$with_arch_riscv64" = yes]) +AM_CONDITIONAL([WITH_ARCH_X86_64], [test "$with_arch_x86_64" = yes]) +AM_CONDITIONAL([WITH_ASM], [test "$with_asm" = yes]) +AM_CONDITIONAL([WITH_CMDLINE], [test "$with_cmdline" = yes]) +AM_CONDITIONAL([WITH_ELF], [test "$with_elf" = yes]) +AM_CONDITIONAL([WITH_FREE_LIST], [test "$with_free_list" = yes]) +AM_CONDITIONAL([WITH_MBR], [test "$with_mbr" = yes]) +AM_CONDITIONAL([WITH_MEMMAP], [test "$with_memmap" = yes]) +AM_CONDITIONAL([WITH_MULTIBOOT2], [test "$with_multiboot2" = yes]) +AM_CONDITIONAL([WITH_NTOA], [test "$with_ntoa" = yes]) +AM_CONDITIONAL([WITH_PFA], [test "$with_pfa" = yes]) +AM_CONDITIONAL([WITH_PRINTF], [test "$with_printf" = yes]) +AM_CONDITIONAL([WITH_PRINTF_FMT], [test "$with_printf_fmt" = yes]) +AM_CONDITIONAL([WITH_UNITS], [test "$with_units" = yes]) + +dnl Packages (disabled by default) +AM_CONDITIONAL([WITH_LIBC], [test "$with_libc" = yes]) + +dnl Packages (virtual) +AM_CONDITIONAL([WITH_ARCH_X86], [test "$with_arch_i386" = yes -o "$with_arch_x86_64" = yes]) + + + +#################### +# Autoconf defines # +#################### + +dnl Architecture +AS_IF([test "$host_cpu" = i386], [AC_DEFINE([ASM_I386], [1], [architecture is i386])]) +AS_IF([test "$host_cpu" = riscv64], [AC_DEFINE([ASM_RISCV64], [1], [architecture is RISC-V 64-bit])]) +AS_IF([test "$host_cpu" = x86_64], [AC_DEFINE([ASM_X86_64], [1], [architecture is x86_64])]) + +dnl Architecture (additional) +AS_IF([test "$host_cpu" = i386], [AC_DEFINE([ASM_X86], [1], [architecture is x86])]) +AS_IF([test "$host_cpu" = x86_64], [AC_DEFINE([ASM_X86], [1], [architecture is x86])]) + +dnl Features (enabled by default) +AS_IF([test "$enable_assert" = yes], [AC_DEFINE([ENABLE_ASSERT], [1], [enabled assertions])]) +AS_IF([test "$enable_float" = yes], [AC_DEFINE([ENABLE_FLOAT], [1], [enabled floating-point arithmetic])]) +AS_IF([test "$enable_werror" = yes], [AC_DEFINE([ENABLE_WERROR], [1], [enabled -Werror])]) + +dnl Features (disabled by default) +AS_IF([test "$enable_fixtures" = yes], [AC_DEFINE([ENABLE_FIXTURES], [1], [enabled fixtures for tests and bindings])]) +AS_IF([test "$enable_freestanding" = yes], [AC_DEFINE([ENABLE_FREESTANDING], [1], [build for freestanding environment])]) +AS_IF([test "$enable_checks" = yes], [AC_DEFINE([ENABLE_CHECKS], [1], [enabled usual tests and examples])]) +AS_IF([test "$enable_checks_cppcheck" = yes], [AC_DEFINE([ENABLE_CHECKS_CPPCHECK], [1], [enabled cppcheck])]) +AS_IF([test "$enable_checks_pthreads" = yes], [AC_DEFINE([ENABLE_CHECKS_PTHREADS], [1], [enabled tests that require pthreads])]) +AS_IF([test "$enable_checks_python" = yes], [AC_DEFINE([ENABLE_CHECKS_PYTHON], [1], [enabled tests that require Python 3 with YAML and Jinja2])]) +AS_IF([test "$enable_split_libc" = yes], [AC_DEFINE([ENABLE_SPLIT_LIBC], [1], [split off libc])]) + +dnl Packages (enabled by default) +AS_IF([test "$with_arch_i386" = yes], [AC_DEFINE([WITH_ARCH_I386], [1], [with architecture i386])]) +AS_IF([test "$with_arch_riscv64" = yes], [AC_DEFINE([WITH_ARCH_RISCV64], [1], [with architecture riscv64])]) +AS_IF([test "$with_arch_x86_64" = yes], [AC_DEFINE([WITH_ARCH_X86_64], [1], [with architecture x86_64])]) +AS_IF([test "$with_asm" = yes], [AC_DEFINE([WITH_ASM], [1], [with kernel assembler helpers])]) +AS_IF([test "$with_cmdline" = yes], [AC_DEFINE([WITH_CMDLINE], [1], [with command line parser])]) +AS_IF([test "$with_elf" = yes], [AC_DEFINE([WITH_ELF], [1], [with ELF utils])]) +AS_IF([test "$with_free_list" = yes], [AC_DEFINE([WITH_FREE_LIST], [1], [with free list memory allocator])]) +AS_IF([test "$with_mbr" = yes], [AC_DEFINE([WITH_MBR], [1], [with MBR utils])]) +AS_IF([test "$with_memmap" = yes], [AC_DEFINE([WITH_MEMMAP], [1], [with memory map])]) +AS_IF([test "$with_multiboot2" = yes], [AC_DEFINE([WITH_MULTIBOOT2], [1], [with Multiboot 2 utils])]) +AS_IF([test "$with_ntoa" = yes], [AC_DEFINE([WITH_NTOA], [1], [with ntoa])]) +AS_IF([test "$with_pfa" = yes], [AC_DEFINE([WITH_PFA], [1], [with Page Frame Allocator])]) +AS_IF([test "$with_printf" = yes], [AC_DEFINE([WITH_PRINTF], [1], [with printf])]) +AS_IF([test "$with_printf_fmt" = yes], [AC_DEFINE([WITH_PRINTF_FMT], [1], [with printf format parser])]) +AS_IF([test "$with_units", = yes], [AC_DEFINE([WITH_UNITS], [1], [with measurement units utils])]) + +dnl Packages (disabled by default) +AS_IF([test "$with_libc" = yes], [AC_DEFINE([WITH_LIBC], [1], [with libc replacement])]) + +dnl Packages (virtual) +AS_IF([test "$with_arch_i386" = yes], [AC_DEFINE([WITH_ARCH_X86], [1], [with architecture x86])]) +AS_IF([test "$with_arch_x86_64" = yes], [AC_DEFINE([WITH_ARCH_X86], [1], [with architecture x86])]) + + + +########################## +# Autoconf substitutions # +########################## + +dnl Features (with parameter) +AC_SUBST([pkgconfdir], [$enable_pkg_config]) + +dnl Packages (enabled by default) +AS_IF([test "$with_arch_i386" = no], [AC_SUBST([comment_line_arch_i386], [//])]) +AS_IF([test "$with_arch_riscv64" = no], [AC_SUBST([comment_line_arch_riscv64], [//])]) +AS_IF([test "$with_arch_x86_64" = no], [AC_SUBST([comment_line_arch_x86_64], [//])]) +AS_IF([test "$with_cmdline" = no], [AC_SUBST([comment_line_cmdline], [//])]) +AS_IF([test "$with_elf" = no], [AC_SUBST([comment_line_elf], [//])]) +AS_IF([test "$with_free_list" = no], [AC_SUBST([comment_line_free_list], [//])]) +AS_IF([test "$with_mbr" = no], [AC_SUBST([comment_line_mbr], [//])]) +AS_IF([test "$with_memmap" = no], [AC_SUBST([comment_line_memmap], [//])]) +AS_IF([test "$with_multiboot2" = no], [AC_SUBST([comment_line_multiboot2], [//])]) +AS_IF([test "$with_ntoa" = no], [AC_SUBST([comment_line_ntoa], [//])]) +AS_IF([test "$with_pfa" = no], [AC_SUBST([comment_line_pfa], [//])]) +AS_IF([test "$with_printf" = no], [AC_SUBST([comment_line_printf], [//])]) +AS_IF([test "$with_printf_fmt" = no], [AC_SUBST([comment_line_printf_fmt], [//])]) +AS_IF([test "$with_units" = no], [AC_SUBST([comment_line_units], [//])]) + + + +################### +# Set build flags # +################### + +AS_IF([test "$enable_freestanding" = yes], + [CFLAGS="$CFLAGS -nostdlib -ffreestanding -fno-pic -fno-stack-protector "], + [CFLAGS="$CFLAGS -fpic "]) + + + +############## +# Run checks # +############## + +AC_LANG([C]) +AM_PROG_AS + +AC_C_INLINE + +AC_HEADER_STDBOOL +AS_IF([test "$enable_checks" = yes -a "$ac_cv_header_stdbool_h" != yes], + [AC_MSG_ERROR([the header is required])]) + +AC_CHECK_HEADERS([limits.h stdarg.h stddef.h stdint.h],, + [AC_MSG_ERROR([the headers are required])]) + +AS_IF([test "$enable_checks" = yes], + [AC_CHECK_HEADERS([assert.h setjmp.h stdio.h stdlib.h string.h],, + [AC_MSG_ERROR([the headers are required])])]) + +AS_IF([test "$enable_checks" = yes -a "$enable_checks_pthreads" = yes], + [AC_CHECK_HEADERS([pthread.h],, + [AC_MSG_ERROR([the header is required])])]) + +AM_PATH_PYTHON([3.8],, [:]) +AS_IF([test "$enable_checks_python" = yes -a "$enable_checks" = yes -a "$PYTHON" = ':'], + [AC_MSG_ERROR([Python is required])]) + +AC_PATH_PROGS([CPPCHECK], [cppcheck], [:]) +AS_IF([test "$enable_checks_cppcheck" = yes -a "$CPPCHECK" = ':'], + [AC_MSG_ERROR([cppcheck is required])]) + +AC_CACHE_CHECK([for Python YAML], + [libkernaux_cv_shell_pythonyaml_exists], + [libkernaux_cv_shell_pythonyaml_exists=no + PYTHON_YAML=no + if test "$PYTHON" != ':'; then + if test "$($PYTHON -c 'import yaml; print(yaml.__package__)')" = 'yaml'; then + libkernaux_cv_shell_pythonyaml_exists=yes + PYTHON_YAML=yes + fi + fi]) +AS_IF([test "$enable_checks_python" = yes -a "$enable_checks" = yes -a "$PYTHON_YAML" != 'yes'], + [AC_MSG_ERROR([Python YAML is required])]) + +AC_CACHE_CHECK([for Python Jinja 2], + [libkernaux_cv_shell_pythonjinja2_exists], + [libkernaux_cv_shell_pythonjinja2_exists=no + PYTHON_JINJA2=no + if test "$PYTHON" != ':'; then + if test "$($PYTHON -c 'import jinja2; print(jinja2.__package__)')" = 'jinja2'; then + libkernaux_cv_shell_pythonjinja2_exists=yes + PYTHON_JINJA2=yes + fi + fi]) +AS_IF([test "$enable_checks_python" = yes -a "$enable_checks" = yes -a "$PYTHON_JINJA2" != 'yes'], + [AC_MSG_ERROR([Python Jinja 2 is required])]) + + + +###################### +# Initialize Libtool # +###################### + +LT_INIT + + + +########## +# Finish # +########## + +AC_OUTPUT diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..fea0c55 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,19 @@ +/cmdline +/generic_display +/generic_malloc +/generic_mutex +/macro_bits +/macro_cast +/macro_container_of +/macro_packing +/macro_static_test +/memmap +/multiboot2_header_macro +/ntoa +/pfa +/printf_file +/printf_file_va +/printf_fmt +/printf_str +/printf_str_va +/units_human diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..76411d5 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,180 @@ +include $(top_srcdir)/make/shared.am + +TESTS = +noinst_PROGRAMS = $(TESTS) + +########### +# cmdline # +########### + +if WITH_CMDLINE +TESTS += cmdline +cmdline_LDADD = $(top_builddir)/libkernaux.la +cmdline_SOURCES = main.c cmdline.c +endif + +################### +# generic_display # +################### + +TESTS += generic_display +generic_display_LDADD = $(top_builddir)/libkernaux.la +generic_display_SOURCES = main.c generic_display.c + +################## +# generic_malloc # +################## + +TESTS += generic_malloc +generic_malloc_LDADD = $(top_builddir)/libkernaux.la +generic_malloc_SOURCES = main.c generic_malloc.c + +################# +# generic_mutex # +################# + +if ENABLE_CHECKS_PTHREADS +TESTS += generic_mutex +generic_mutex_LDADD = $(top_builddir)/libkernaux.la +generic_mutex_SOURCES = main.c generic_mutex.c +endif + +############## +# macro_bits # +############## + +TESTS += macro_bits +macro_bits_LDADD = $(top_builddir)/libkernaux.la +macro_bits_SOURCES = main.c macro_bits.c + +############## +# macro_cast # +############## + +TESTS += macro_cast +macro_cast_LDADD = $(top_builddir)/libkernaux.la +macro_cast_SOURCES = main.c macro_cast.c + +###################### +# macro_container_of # +###################### + +TESTS += macro_container_of +macro_container_of_LDADD = $(top_builddir)/libkernaux.la +macro_container_of_SOURCES = main.c macro_container_of.c + +################# +# macro_packing # +################# + +TESTS += macro_packing +macro_packing_LDADD = $(top_builddir)/libkernaux.la +macro_packing_SOURCES = main.c macro_packing.c + +##################### +# macro_static_test # +##################### + +TESTS += macro_static_test +macro_static_test_LDADD = $(top_builddir)/libkernaux.la +macro_static_test_SOURCES = main.c macro_static_test.c + +########## +# memmap # +########## + +if WITH_MEMMAP +TESTS += memmap +memmap_LDADD = $(top_builddir)/libkernaux.la +memmap_SOURCES = main.c memmap.c +endif + +########################### +# multiboot2_header_macro # +########################### + +if WITH_MULTIBOOT2 +TESTS += multiboot2_header_macro +multiboot2_header_macro_LDADD = $(top_builddir)/libkernaux.la +multiboot2_header_macro_SOURCES = main.c multiboot2_header_macro.c +endif + +######## +# ntoa # +######## + +if WITH_NTOA +TESTS += ntoa +ntoa_LDADD = $(top_builddir)/libkernaux.la +ntoa_SOURCES = main.c ntoa.c +endif + +####### +# pfa # +####### + +if WITH_PFA +TESTS += pfa +pfa_LDADD = $(top_builddir)/libkernaux.la +pfa_SOURCES = main.c pfa.c +endif + +############### +# printf_file # +############### + +if WITH_PRINTF +TESTS += printf_file +printf_file_LDADD = $(top_builddir)/libkernaux.la +printf_file_SOURCES = main.c printf_file.c +endif + +################## +# printf_file_va # +################## + +if WITH_PRINTF +TESTS += printf_file_va +printf_file_va_LDADD = $(top_builddir)/libkernaux.la +printf_file_va_SOURCES = main.c printf_file_va.c +endif + +############## +# printf_fmt # +############## + +if WITH_PRINTF_FMT +TESTS += printf_fmt +printf_fmt_LDADD = $(top_builddir)/libkernaux.la +printf_fmt_SOURCES = main.c printf_fmt.c +endif + +############## +# printf_str # +############## + +if WITH_PRINTF +TESTS += printf_str +printf_str_LDADD = $(top_builddir)/libkernaux.la +printf_str_SOURCES = main.c printf_str.c +endif + +################# +# printf_str_va # +################# + +if WITH_PRINTF +TESTS += printf_str_va +printf_str_va_LDADD = $(top_builddir)/libkernaux.la +printf_str_va_SOURCES = main.c printf_str_va.c +endif + +############### +# units_human # +############### + +if WITH_UNITS +TESTS += units_human +units_human_LDADD = $(top_builddir)/libkernaux.la +units_human_SOURCES = main.c units_human.c +endif diff --git a/examples/cmdline.c b/examples/cmdline.c new file mode 100644 index 0000000..83732d7 --- /dev/null +++ b/examples/cmdline.c @@ -0,0 +1,34 @@ +#include + +#include +#include +#include + +#define ARG_COUNT_MAX 100 +#define BUFFER_SIZE 4096 + +static const char *const cmdline = "foo bar\\ baz \"car cdr\""; + +void example_main() +{ + char error_msg[KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX]; + size_t argc; + char *argv[ARG_COUNT_MAX]; + char buffer[BUFFER_SIZE]; + + assert(kernaux_cmdline( + cmdline, + error_msg, + &argc, + argv, + buffer, + ARG_COUNT_MAX, + BUFFER_SIZE + )); + + assert(strcmp(error_msg, "") == 0); + assert(argc == 3); + assert(strcmp(argv[0], "foo") == 0); + assert(strcmp(argv[1], "bar baz") == 0); + assert(strcmp(argv[2], "car cdr") == 0); +} diff --git a/examples/generic_display.c b/examples/generic_display.c new file mode 100644 index 0000000..44da528 --- /dev/null +++ b/examples/generic_display.c @@ -0,0 +1,115 @@ +//============== +// my_display.c +//============== + +// To not always use macro "KERNAUX_PROTECTED_FIELD" around the names of +// structure fields you may define "KERNAUX_ACCESS_PROTECTED" before including +// any other headers, but ONLY in the file where you implement a generic type. +// +#define KERNAUX_ACCESS_PROTECTED + +//============== +// my_display.h +//============== + +#include +#include + +typedef struct MyDisplay { + struct KernAux_Display display; + char *buffer, *cursor; + size_t capacity; +} *MyDisplay; + +struct MyDisplay MyDisplay_create(); + +//============== +// my_display.c +//============== + +#include +#include +#include +#include +#include +#include +#include + +#define CAP_FREE (1024) +#define CAP_INCR (2 * (CAP_FREE)) + +static void MyDisplay_putc(void *display, char c); +void MyDisplay_vprintf(void *display, const char *format, va_list va); + +static void MyDisplay_realloc(MyDisplay my_display); + +struct MyDisplay MyDisplay_create() +{ + char *const buffer = malloc(CAP_INCR); + if (!buffer) abort(); + + return (struct MyDisplay){ + .display = { + .putc = MyDisplay_putc, + .vprintf = MyDisplay_vprintf, + }, + .buffer = buffer, + .cursor = buffer, + .capacity = CAP_INCR, + }; +} + +void MyDisplay_putc(void *display, char c) +{ + const MyDisplay my_display = display; + MyDisplay_realloc(my_display); + *(my_display->cursor++) = c; +} + +void MyDisplay_vprintf(void *display, const char *format, va_list va) +{ + const MyDisplay my_display = display; + MyDisplay_realloc(my_display); + size_t left = + my_display->buffer + my_display->capacity - my_display->cursor; + const int delta = vsnprintf(my_display->cursor, left, format, va); + if (delta < 0) abort(); + my_display->cursor += delta; +} + +void MyDisplay_realloc(const MyDisplay my_display) +{ + size_t left = + my_display->buffer + my_display->capacity - my_display->cursor; + + if (left < CAP_FREE) { + my_display->buffer = + realloc(my_display->buffer, my_display->capacity + CAP_INCR); + if (!my_display->buffer) abort(); + my_display->capacity += CAP_INCR; + } +} + +//======== +// main.c +//======== + +void example_main() +{ + struct MyDisplay my_display = MyDisplay_create(); + + KernAux_Display_putc(&my_display.display, '@'); + KernAux_Display_print(&my_display.display, "print"); + KernAux_Display_println(&my_display.display, "println"); + KernAux_Display_write(&my_display.display, "write!!!", 5); + KernAux_Display_writeln(&my_display.display, "writeln!!!", 7); + KernAux_Display_printf(&my_display.display, "printf(%d)", 123); + KernAux_Display_printlnf(&my_display.display, "printfln(%d)", 123); + + assert( + strcmp( + my_display.buffer, + "@printprintln\nwritewriteln\nprintf(123)printfln(123)\n" + ) == 0 + ); +} diff --git a/examples/generic_malloc.c b/examples/generic_malloc.c new file mode 100644 index 0000000..839d78d --- /dev/null +++ b/examples/generic_malloc.c @@ -0,0 +1,93 @@ +//============= +// my_malloc.c +//============= + +// To not always use macro "KERNAUX_PROTECTED_FIELD" around the names of +// structure fields you may define "KERNAUX_ACCESS_PROTECTED" before including +// any other headers, but ONLY in the file where you implement a generic type. +// +#define KERNAUX_ACCESS_PROTECTED + +//============= +// my_malloc.h +//============= + +#include + +typedef struct MyMalloc { + struct KernAux_Malloc malloc; +} *MyMalloc; + +struct MyMalloc MyMalloc_create(); + +//============= +// my_malloc.c +//============= + +#include +#include +#include + +static void *MyMalloc_calloc (void *malloc, size_t nmemb, size_t size); +static void MyMalloc_free (void *malloc, void *ptr); +static void *MyMalloc_malloc (void *malloc, size_t size); +static void *MyMalloc_realloc(void *malloc, void *ptr, size_t size); + +struct MyMalloc MyMalloc_create() +{ + return (struct MyMalloc){ + .malloc = { + .calloc = MyMalloc_calloc, + .free = MyMalloc_free, + .malloc = MyMalloc_malloc, + .realloc = MyMalloc_realloc, + }, + }; +} + +void *MyMalloc_calloc(void *const malloc, const size_t nmemb, const size_t size) +{ + (void)malloc; // unused + return calloc(nmemb, size); +} + +void MyMalloc_free(void *const malloc, void *const ptr) +{ + (void)malloc; // unused + free(ptr); +} + +void *MyMalloc_malloc(void *const malloc_, const size_t size) +{ + (void)malloc_; // unused + return malloc(size); +} + +void *MyMalloc_realloc(void *const malloc, void *const ptr, const size_t size) +{ + (void)malloc; // unused + return realloc(ptr, size); +} + +//======== +// main.c +//======== + +#include +#include + +void example_main() +{ + // Create memory allocator + struct MyMalloc my_malloc = MyMalloc_create(); + + // Allocate memory + void *ptr = KernAux_Malloc_malloc(&my_malloc.malloc, 1000); + + // Use memory + strcpy(ptr, "Hello, World!"); + assert(strcmp(ptr, "Hello, World!") == 0); + + // Free memory + KernAux_Malloc_free(&my_malloc.malloc, ptr); +} diff --git a/examples/generic_mutex.c b/examples/generic_mutex.c new file mode 100644 index 0000000..f728781 --- /dev/null +++ b/examples/generic_mutex.c @@ -0,0 +1,79 @@ +//============ +// my_mutex.c +//============ + +// To not always use macro "KERNAUX_PROTECTED_FIELD" around the names of +// structure fields you may define "KERNAUX_ACCESS_PROTECTED" before including +// any other headers, but ONLY in the file where you implement a generic type. +// +#define KERNAUX_ACCESS_PROTECTED + +//============ +// my_mutex.h +//============ + +#include +#include + +typedef struct MyMutex { + struct KernAux_Mutex mutex; + pthread_mutex_t pthread_mutex; +} *MyMutex; + +struct MyMutex MyMytex_create(); + +//============ +// my_mutex.c +//============ + +#include +#include +#include + +static void MyMutex_lock (void *mutex); +static void MyMutex_unlock(void *mutex); + +struct MyMutex MyMutex_create() +{ + struct MyMutex my_mutex = { + .mutex = { + .lock = MyMutex_lock, + .unlock = MyMutex_unlock, + }, + }; + if (pthread_mutex_init(&my_mutex.pthread_mutex, NULL) != 0) abort(); + return my_mutex; +} + +void MyMutex_lock(void *const mutex) +{ + const MyMutex my_mutex = mutex; + pthread_mutex_lock(&my_mutex->pthread_mutex); +} + +void MyMutex_unlock(void *const mutex) +{ + const MyMutex my_mutex = mutex; + pthread_mutex_unlock(&my_mutex->pthread_mutex); +} + +//======== +// main.c +//======== + +static int shared_counter = 0; + +void example_main() +{ + // Create mutex + struct MyMutex my_mutex = MyMutex_create(); + + // Lock mutex + KernAux_Mutex_lock(&my_mutex.mutex); + + // Access shared data + ++shared_counter; + + // Unlock mutex + KernAux_Mutex_unlock(&my_mutex.mutex); +} diff --git a/examples/macro_bits.c b/examples/macro_bits.c new file mode 100644 index 0000000..c470a7a --- /dev/null +++ b/examples/macro_bits.c @@ -0,0 +1,21 @@ +#include + +#include + +void example_main() +{ + assert(KERNAUX_BITS(0) == 0x1); + assert(KERNAUX_BITS(31) == 0x80000000); + + assert(KERNAUX_BITS8(0) == 0x1); + assert(KERNAUX_BITS8(7) == 0x80); + + assert(KERNAUX_BITS16(0) == 0x1); + assert(KERNAUX_BITS16(15) == 0x8000); + + assert(KERNAUX_BITS32(0) == 0x1); + assert(KERNAUX_BITS32(31) == 0x80000000); + + assert(KERNAUX_BITS64(0) == 0x1); + assert(KERNAUX_BITS64(63) == 0x8000000000000000); +} diff --git a/examples/macro_cast.c b/examples/macro_cast.c new file mode 100644 index 0000000..11023a2 --- /dev/null +++ b/examples/macro_cast.c @@ -0,0 +1,25 @@ +#include + +#include +#include + +void example_main() +{ + const uint32_t value = 123; + + // const unsigned long ul = (unsigned long)value; + KERNAUX_CAST_CONST(unsigned long, ul, value); + assert(ul == 123); + + // unsigned long long ull = (unsigned long long)value; + KERNAUX_CAST_VAR(unsigned long long, ull, value); + assert(ull == 123); + + // const [signed] long sl = ([signed] long)value; + KERNAUX_CAST_CONST(long, sl, value); + assert(sl == 123); + + // [signed] long long sll = ([signed] long long)value; + KERNAUX_CAST_VAR(long long, sll, value); + assert(sll == 123); +} diff --git a/examples/macro_container_of.c b/examples/macro_container_of.c new file mode 100644 index 0000000..397e76f --- /dev/null +++ b/examples/macro_container_of.c @@ -0,0 +1,35 @@ +#include + +#include +#include + +struct Foo { + const char *hello; +}; + +struct Bar { + unsigned char a; + unsigned int b; + struct Foo foo; + unsigned short c; +}; + +static const struct Bar bar = { + .a = 143, + .b = 820794098, + .foo = { + .hello = "Hello, World!", + }, + .c = 10981, +}; + +void example_main() +{ + const struct Bar *const bar_ptr = + KERNAUX_CONTAINER_OF(&bar.foo, struct Bar, foo); + + assert(bar_ptr->a == 143); + assert(bar_ptr->b == 820794098); + assert(strcmp(bar_ptr->foo.hello, "Hello, World!") == 0); + assert(bar_ptr->c == 10981); +} diff --git a/examples/macro_packing.c b/examples/macro_packing.c new file mode 100644 index 0000000..eaf38bd --- /dev/null +++ b/examples/macro_packing.c @@ -0,0 +1,25 @@ +#include + +#include +#include + +struct Foo { + uint8_t a; + uint32_t b; +}; + +#include + +struct Bar { + uint8_t a; + uint32_t b; +} +KERNAUX_PACKED; + +#include + +void example_main() +{ + assert(sizeof(struct Foo) > 5); + assert(sizeof(struct Bar) == 5); +} diff --git a/examples/macro_static_test.c b/examples/macro_static_test.c new file mode 100644 index 0000000..c3bc054 --- /dev/null +++ b/examples/macro_static_test.c @@ -0,0 +1,28 @@ +#include + +#include + +KERNAUX_STATIC_TEST(uint8_t_size, sizeof(uint8_t) == 1); +KERNAUX_STATIC_TEST(uint16_t_size, sizeof(uint16_t) == 2); +KERNAUX_STATIC_TEST(uint32_t_size, sizeof(uint32_t) == 4); +KERNAUX_STATIC_TEST(uint64_t_size, sizeof(uint64_t) == 8); + +#include + +struct Foo { + uint8_t a; + uint32_t b; +} KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(Foo, 5); + +union Bar { + uint8_t a; + uint16_t b; +} KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_UNION_SIZE(Bar, 2); + +#include + +void example_main() {} diff --git a/examples/main.c b/examples/main.c new file mode 100644 index 0000000..50eed31 --- /dev/null +++ b/examples/main.c @@ -0,0 +1,26 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +void example_main(); + +static void assert_cb( + const char *const file, + const int line, + const char *const msg +) { + fprintf(stderr, "%s:%d:%s\n", file, line, msg); + abort(); +} + +int main() +{ + kernaux_assert_cb = assert_cb; + example_main(); + exit(EXIT_SUCCESS); +} diff --git a/examples/memmap.c b/examples/memmap.c new file mode 100644 index 0000000..66b7c75 --- /dev/null +++ b/examples/memmap.c @@ -0,0 +1,60 @@ +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include +#include + +#include +#include +#include + +static char malloc_memory[8192]; + +static void my_putc(void *const display KERNAUX_UNUSED, const char c) +{ + putchar(c); +} + +static void my_vprintf( + void *const display KERNAUX_UNUSED, + const char *const format, + va_list va +) { + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void example_main() +{ + struct KernAux_FreeList malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, malloc_memory, sizeof(malloc_memory)); + + KernAux_Memmap_Builder memmap_builder = + KernAux_Memmap_Builder_new(&malloc.malloc); + assert(memmap_builder); + + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x0, 654336, "available")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x9fc00, 1024, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0xf0000, 65536, "reserved")); + KernAux_Memmap_Node kernel_node = + KernAux_Memmap_Builder_add (memmap_builder, NULL, 0x100000, 133038080, "available"); + assert(kernel_node); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0x7fe0000, 131072, "reserved")); + assert(KernAux_Memmap_Builder_add(memmap_builder, NULL, 0xfffc0000, 262144, "reserved")); + + assert(KernAux_Memmap_Builder_add(memmap_builder, kernel_node, 0x400000, 8192, "kernel code")); + assert(KernAux_Memmap_Builder_add(memmap_builder, kernel_node, 0x402000, 4096, "kernel data")); + + KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(memmap_builder); + assert(memmap); + + KernAux_Memmap_print(memmap, &display); + + KERNAUX_MEMMAP_FREE(memmap); +} diff --git a/examples/multiboot2_header_macro.c b/examples/multiboot2_header_macro.c new file mode 100644 index 0000000..41426aa --- /dev/null +++ b/examples/multiboot2_header_macro.c @@ -0,0 +1,104 @@ +#include + +#include + +#include + +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_HEADER_ALIGN) +static const struct { + struct KernAux_Multiboot2_Header header; + // This macro may be used to create the tag + // of type "KernAux_Multiboot2_HTag_InfoReq" + // when the number of requested information + // tag types is even (n % 2 == 0). + // + // cppcheck-suppress unknownMacro + KERNAUX_MULTIBOOT2_HFIELDS_INFO_REQ_EVEN( + // This is the name of the structure field. + tag_info_req_even, + // This is the number of requested information tag types. + // IT MUST BE EVEN!!! (n % 2 == 0) + 2 + ) + // This macro may be used to create the tag + // of type "KernAux_Multiboot2_HTag_InfoReq" + // when the number of requested information + // tag types is odd (n % 2 == 1). + // + // cppcheck-suppress unknownMacro + KERNAUX_MULTIBOOT2_HFIELDS_INFO_REQ_ODD( + // This is the name of the structure field. + tag_info_req_odd, + // This is the number of requested information tag types. + // IT MUST BE ODD!!! (n % 2 == 1) + 1, + // This is the name of the additional structure field + // which will be used to align the following tags properly. + // You may keep it unassigned. + _align1 + ) + // This macro may be used for all other header tag types. + // + // cppcheck-suppress unknownMacro + KERNAUX_MULTIBOOT2_HFIELDS_COMMON( + // This is the name of the structure field. + tag_none, + // This is the type of the structure field + // without the "KernAux_Multiboot2_HTag_" prefix. + None + ) +} +KERNAUX_PACKED +multiboot2_header = { + .header = { + .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC, + .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + .total_size = sizeof(multiboot2_header), + // This macro helps to calculate the checksum. + .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM( + KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + sizeof(multiboot2_header) + ), + }, + .tag_info_req_even = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_INFO_REQ, + .flags = KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL, + .size = sizeof(multiboot2_header.tag_info_req_even), + }, + }, + .mbi_tag_types = { + KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + }, + }, + .tag_info_req_odd = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_INFO_REQ, + .flags = KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL, + .size = sizeof(multiboot2_header.tag_info_req_odd), + }, + }, + .mbi_tag_types = { + KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS, + }, + }, + .tag_none = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_NONE, + .flags = 0, + .size = sizeof(multiboot2_header.tag_none), + }, + }, + }, +}; + +#include + +void example_main() +{ + assert(KernAux_Multiboot2_Header_is_valid(&multiboot2_header.header)); +} diff --git a/examples/ntoa.c b/examples/ntoa.c new file mode 100644 index 0000000..93a435a --- /dev/null +++ b/examples/ntoa.c @@ -0,0 +1,281 @@ +#include + +#include +#include +#include + +static const char *str_end(const char *str) +{ + for (;; ++str) if (*str == '\0') return str; +} + +void example_main() +{ + // kernaux_utoa + { + char buffer[KERNAUX_UTOA_MIN_BUFFER_SIZE + 3]; // more space for prefix + const char *end; + + // decimal: 10 + // no prefix (NULL) + end = kernaux_utoa(123, buffer, 10, NULL); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + // decimal: 10 + // with prefix ("foo") + end = kernaux_utoa(123, buffer, 10, "foo"); + assert(strcmp(buffer, "foo123") == 0); + assert(end == str_end(buffer)); + + // decimal, character alias: 'd' + // no prefix (NULL) + end = kernaux_utoa(123, buffer, 'd', NULL); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + // binary: 2 + // no prefix (NULL) + end = kernaux_utoa(123, buffer, 2, NULL); + assert(strcmp(buffer, "1111011") == 0); + assert(end == str_end(buffer)); + + // binary: 2 + // with prefix ("0b") + end = kernaux_utoa(123, buffer, 2, "0b"); + assert(strcmp(buffer, "0b1111011") == 0); + assert(end == str_end(buffer)); + + // binary, character alias: 'b' + // no prefix (NULL) + end = kernaux_utoa(123, buffer, 'b', NULL); + assert(strcmp(buffer, "1111011") == 0); + assert(end == str_end(buffer)); + + // binary, character alias: 'b' + // with prefix ("0b") + end = kernaux_utoa(123, buffer, 'b', "0b"); + assert(strcmp(buffer, "0b1111011") == 0); + assert(end == str_end(buffer)); + + // octal: 8 + // no prefix (NULL) + end = kernaux_utoa(0123, buffer, 8, NULL); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + // octal: 8 + // with prefix ("0o") + end = kernaux_utoa(0123, buffer, 8, "0o"); + assert(strcmp(buffer, "0o123") == 0); + assert(end == str_end(buffer)); + + // octal, character alias: 'o' + // no prefix (NULL) + end = kernaux_utoa(0123, buffer, 'o', NULL); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + // octal, character alias: 'o' + // with prefix ("0o") + end = kernaux_utoa(0123, buffer, 'o', "0o"); + assert(strcmp(buffer, "0o123") == 0); + assert(end == str_end(buffer)); + + // hex: 16 + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, 16, NULL); + assert(strcmp(buffer, "123cafe") == 0); + assert(end == str_end(buffer)); + + // hex: 16 + // with prefix ("0x") + end = kernaux_utoa(0x123cafe, buffer, 16, "0x"); + assert(strcmp(buffer, "0x123cafe") == 0); + assert(end == str_end(buffer)); + + // hex, character alias: 'x' + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, 'x', NULL); + assert(strcmp(buffer, "123cafe") == 0); + assert(end == str_end(buffer)); + + // hex, character alias: 'x' + // with prefix ("0x") + end = kernaux_utoa(0x123cafe, buffer, 'x', "0x"); + assert(strcmp(buffer, "0x123cafe") == 0); + assert(end == str_end(buffer)); + + // hex, character alias: 'h' + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, 'h', NULL); + assert(strcmp(buffer, "123cafe") == 0); + assert(end == str_end(buffer)); + + // hex, uppercase: -16 + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, -16, NULL); + assert(strcmp(buffer, "123CAFE") == 0); + assert(end == str_end(buffer)); + + // hex, uppercase, character alias: 'X' + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, 'X', NULL); + assert(strcmp(buffer, "123CAFE") == 0); + assert(end == str_end(buffer)); + + // hex, uppercase, character alias: 'H' + // no prefix (NULL) + end = kernaux_utoa(0x123cafe, buffer, 'H', NULL); + assert(strcmp(buffer, "123CAFE") == 0); + assert(end == str_end(buffer)); + + // random base: 14 + // no prefix (NULL) + end = kernaux_utoa(123456, buffer, 14, NULL); + assert(strcmp(buffer, "32dc4") == 0); + assert(end == str_end(buffer)); + + // random base: 14 + // with prefix ("foo") + end = kernaux_utoa(123456, buffer, 14, "foo"); + assert(strcmp(buffer, "foo32dc4") == 0); + assert(end == str_end(buffer)); + + // random base, uppercase: -14 + // no prefix (NULL) + end = kernaux_utoa(123456, buffer, -14, NULL); + assert(strcmp(buffer, "32DC4") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_itoa - similar to kernaux_utoa + { + char buffer[KERNAUX_ITOA_MIN_BUFFER_SIZE + 3]; // more space for prefix + const char *end; + + // sign: + + // decimal: 10 + // no prefix (NULL) + end = kernaux_itoa(123, buffer, 10, NULL); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + // sign: - + // decimal: 10 + // no prefix (NULL) + end = kernaux_itoa(-123, buffer, 10, NULL); + assert(strcmp(buffer, "-123") == 0); + assert(end == str_end(buffer)); + + // sign: + + // decimal: 10 + // with prefix ("foo") + end = kernaux_itoa(123, buffer, 10, "foo"); + assert(strcmp(buffer, "foo123") == 0); + assert(end == str_end(buffer)); + + // sign: - + // decimal: 10 + // with prefix ("foo") + end = kernaux_itoa(-123, buffer, 10, "foo"); + assert(strcmp(buffer, "-foo123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_utoa2 + { + char buffer[KERNAUX_UTOA2_BUFFER_SIZE]; + const char *end; + + end = kernaux_utoa2(123, buffer); + assert(strcmp(buffer, "0b1111011") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_itoa2 + { + char buffer[KERNAUX_ITOA2_BUFFER_SIZE]; + const char *end; + + end = kernaux_itoa2(123, buffer); + assert(strcmp(buffer, "0b1111011") == 0); + assert(end == str_end(buffer)); + + end = kernaux_itoa2(-123, buffer); + assert(strcmp(buffer, "-0b1111011") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_utoa8 + { + char buffer[KERNAUX_UTOA8_BUFFER_SIZE]; + const char *end; + + end = kernaux_utoa8(0123, buffer); + assert(strcmp(buffer, "0o123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_itoa8 + { + char buffer[KERNAUX_ITOA8_BUFFER_SIZE]; + const char *end; + + end = kernaux_itoa8(0123, buffer); + assert(strcmp(buffer, "0o123") == 0); + assert(end == str_end(buffer)); + + end = kernaux_itoa8(-0123, buffer); + assert(strcmp(buffer, "-0o123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_utoa10 + { + char buffer[KERNAUX_UTOA10_BUFFER_SIZE]; + const char *end; + + end = kernaux_utoa10(123, buffer); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_itoa10 + { + char buffer[KERNAUX_ITOA10_BUFFER_SIZE]; + const char *end; + + end = kernaux_itoa10(123, buffer); + assert(strcmp(buffer, "123") == 0); + assert(end == str_end(buffer)); + + end = kernaux_itoa10(-123, buffer); + assert(strcmp(buffer, "-123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_utoa16 + { + char buffer[KERNAUX_UTOA16_BUFFER_SIZE]; + const char *end; + + end = kernaux_utoa16(0x123, buffer); + assert(strcmp(buffer, "0x123") == 0); + assert(end == str_end(buffer)); + } + + // kernaux_itoa16 + { + char buffer[KERNAUX_ITOA16_BUFFER_SIZE]; + const char *end; + + end = kernaux_itoa16(0x123, buffer); + assert(strcmp(buffer, "0x123") == 0); + assert(end == str_end(buffer)); + + end = kernaux_itoa16(-0x123, buffer); + assert(strcmp(buffer, "-0x123") == 0); + assert(end == str_end(buffer)); + } +} diff --git a/examples/pfa.c b/examples/pfa.c new file mode 100644 index 0000000..a55826f --- /dev/null +++ b/examples/pfa.c @@ -0,0 +1,71 @@ +#include + +#include + +// Create PFA in some static memory area because typically you don't have memory +// management in kernel without PFA. +struct KernAux_PFA pfa; + +void example_main() +{ + // In the earliest stage of kernel initialization mark all pages as + // unavailable because you don't have memory map yet. + KernAux_PFA_initialize(&pfa); + + // Let's assume that you have the following memory map now: + // * [0 B; 64 KiB) + // * [1 MiB; 4 MiB) + // * [6 MiB; 128 MiB) + // Mark these pages as available. Note that page is identified by any + // address inside it. This is why you can subtract 1, not only + // KERNAUX_PFA_PAGE_SIZE. + KernAux_PFA_mark_available(&pfa, 0, 1024 * 64 - 1); + KernAux_PFA_mark_available(&pfa, 1024 * 1024, 1024 * 1024 * 4 - 1); + KernAux_PFA_mark_available(&pfa, 1024 * 1024 * 6, 1024 * 1024 * 128 - 1); + + // You can test page availability. + assert(KernAux_PFA_is_available(&pfa, 1024 * 32)); // 32 KiB + assert(!KernAux_PFA_is_available(&pfa, 1024 * 64)); // 64 KiB + assert(KernAux_PFA_is_available(&pfa, 1024 * 1024)); // 1 MiB + assert(!KernAux_PFA_is_available(&pfa, 1024 * 1024 * 6 // 6 MiB - 4 KiB + - KERNAUX_PFA_PAGE_SIZE)); + assert(KernAux_PFA_is_available(&pfa, 1024 * 1024 * 128 // 128 MiB - 4 KiB + - KERNAUX_PFA_PAGE_SIZE)); + assert(!KernAux_PFA_is_available(&pfa, 1024 * 1024 * 128)); // 128 MiB + + // When you have configured PFA, you can use it to allocate and free pages. + { + const size_t page_addr = + KernAux_PFA_alloc_pages(&pfa, KERNAUX_PFA_PAGE_SIZE); + assert(!KernAux_PFA_is_available(&pfa, page_addr)); + KernAux_PFA_free_pages(&pfa, page_addr, KERNAUX_PFA_PAGE_SIZE); + assert(KernAux_PFA_is_available(&pfa, page_addr)); + } + + // You can also allocate multiple continuous pages. + { + const size_t page_addr = + KernAux_PFA_alloc_pages(&pfa, 10 * KERNAUX_PFA_PAGE_SIZE); + + for (size_t index = 0, addr = page_addr; index < 10; ++index) { + assert(!KernAux_PFA_is_available(&pfa, addr)); + addr += KERNAUX_PFA_PAGE_SIZE; + } + + KernAux_PFA_free_pages(&pfa, page_addr, 10 * KERNAUX_PFA_PAGE_SIZE); + + for (size_t index = 0, addr = page_addr; index < 10; ++index) { + assert(KernAux_PFA_is_available(&pfa, addr)); + addr += KERNAUX_PFA_PAGE_SIZE; + } + } + + // You can also request amounts of memory which are not factors of page + // size. + { + const size_t page_addr = KernAux_PFA_alloc_pages(&pfa, 123); + assert(!KernAux_PFA_is_available(&pfa, page_addr)); + KernAux_PFA_free_pages(&pfa, page_addr, 123); + assert(KernAux_PFA_is_available(&pfa, page_addr)); + } +} diff --git a/examples/printf_file.c b/examples/printf_file.c new file mode 100644 index 0000000..0cef182 --- /dev/null +++ b/examples/printf_file.c @@ -0,0 +1,33 @@ +#include + +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +static const char *const data = "foobar"; + +static char buffer[BUFFER_SIZE]; +static size_t buffer_index = 0; + +static void my_putchar(const char chr, void *const arg) +{ + assert(arg == data); + if (buffer_index >= BUFFER_SIZE) abort(); + buffer[buffer_index++] = chr; +} + +void example_main() +{ + const int result = kernaux_fprintf( + my_putchar, + (void*)data, + "Hello, %s! Session ID: %u.", + "Alex", + 123 + ); + assert((size_t)result == strlen(buffer)); + assert(strcmp(buffer, "Hello, Alex! Session ID: 123.") == 0); +} diff --git a/examples/printf_file_va.c b/examples/printf_file_va.c new file mode 100644 index 0000000..d8dd5c6 --- /dev/null +++ b/examples/printf_file_va.c @@ -0,0 +1,36 @@ +#include + +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +static const char *const data = "foobar"; + +static char buffer[BUFFER_SIZE]; +static size_t buffer_index = 0; + +static void my_putchar(const char chr, void *const arg) +{ + assert(arg == data); + if (buffer_index >= BUFFER_SIZE) abort(); + buffer[buffer_index++] = chr; +} + +static int my_printf(const char *const format, ...) +{ + va_list va; + va_start(va, format); + const int result = kernaux_vfprintf(my_putchar, (void*)data, format, va); + va_end(va); + return result; +} + +void example_main() +{ + const int result = my_printf("Hello, %s! Session ID: %u.", "Alex", 123); + assert((size_t)result == strlen(buffer)); + assert(strcmp(buffer, "Hello, Alex! Session ID: 123.") == 0); +} diff --git a/examples/printf_fmt.c b/examples/printf_fmt.c new file mode 100644 index 0000000..6de4dc4 --- /dev/null +++ b/examples/printf_fmt.c @@ -0,0 +1,101 @@ +#include + +#include +#include + +void example_main() +{ + { + const char *const format = "s"; + + struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create(format); + + if (spec.set_width) { + // Actually this line won't be executed. + KernAux_PrintfFmt_Spec_set_width(&spec, 0); + } + if (spec.set_precision) { + // Actually this line won't be executed. + KernAux_PrintfFmt_Spec_set_precision(&spec, 0); + } + + assert(spec.format_start == format); + assert(spec.format_limit == &format[1]); + + assert(spec.flags == 0); + assert(spec.width == 0); + assert(spec.precision == 0); + assert(spec.type == KERNAUX_PRINTF_FMT_TYPE_STR); + assert(spec.base == 0); + + assert(!spec.set_width); + assert(!spec.set_precision); + } + + { + const char *format = "012.34f"; + + struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create_out(&format); + + if (spec.set_width) { + // Actually this line won't be executed. + KernAux_PrintfFmt_Spec_set_width(&spec, 0); + } + if (spec.set_precision) { + // Actually this line won't be executed. + KernAux_PrintfFmt_Spec_set_precision(&spec, 0); + } + + assert(spec.format_start == &format[-7]); + assert(spec.format_limit == format); + + assert( + spec.flags == + ( + KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD | + KERNAUX_PRINTF_FMT_FLAGS_PRECISION + ) + ); + assert(spec.width == 12); + assert(spec.precision == 34); + assert(spec.type == KERNAUX_PRINTF_FMT_TYPE_FLOAT); + assert(spec.base == 0); + + assert(!spec.set_width); + assert(!spec.set_precision); + } + + { + const char *const format = " *.*ld"; + const char *new_format = NULL; + + struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create_out_new(format, &new_format); + + if (spec.set_width) { + KernAux_PrintfFmt_Spec_set_width(&spec, 12); + } + if (spec.set_precision) { + KernAux_PrintfFmt_Spec_set_precision(&spec, 34); + } + + assert(spec.format_start == format); + assert(spec.format_limit == &format[6]); + assert(new_format == &format[6]); + + assert( + spec.flags == + ( + KERNAUX_PRINTF_FMT_FLAGS_SPACE | + KERNAUX_PRINTF_FMT_FLAGS_LONG | + KERNAUX_PRINTF_FMT_FLAGS_PRECISION + ) + ); + assert(spec.width == 12); + assert(spec.precision == 34); + assert(spec.type == KERNAUX_PRINTF_FMT_TYPE_INT); + assert(spec.base == 10); + + assert(spec.set_width); + assert(spec.set_precision); + } +} diff --git a/examples/printf_str.c b/examples/printf_str.c new file mode 100644 index 0000000..674f195 --- /dev/null +++ b/examples/printf_str.c @@ -0,0 +1,21 @@ +#include + +#include +#include + +#define BUFFER_SIZE 1024 + +static char buffer[BUFFER_SIZE]; + +void example_main() +{ + const int result = kernaux_snprintf( + buffer, + sizeof(buffer), + "Hello, %s! Session ID: %u.", + "Alex", + 123 + ); + assert((size_t)result == strlen(buffer)); + assert(strcmp(buffer, "Hello, Alex! Session ID: 123.") == 0); +} diff --git a/examples/printf_str_va.c b/examples/printf_str_va.c new file mode 100644 index 0000000..48a63a7 --- /dev/null +++ b/examples/printf_str_va.c @@ -0,0 +1,24 @@ +#include + +#include +#include + +#define BUFFER_SIZE 1024 + +static char buffer[BUFFER_SIZE]; + +static int my_snprintf(const char *const format, ...) +{ + va_list va; + va_start(va, format); + const int result = kernaux_vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + return result; +} + +void example_main() +{ + const int result = my_snprintf("Hello, %s! Session ID: %u.", "Alex", 123); + assert((size_t)result == strlen(buffer)); + assert(strcmp(buffer, "Hello, Alex! Session ID: 123.") == 0); +} diff --git a/examples/units_human.c b/examples/units_human.c new file mode 100644 index 0000000..76d52de --- /dev/null +++ b/examples/units_human.c @@ -0,0 +1,45 @@ +#include + +#include +#include + +void example_main() +{ + char buffer[256]; + + kernaux_units_human_raw(123, KERNAUX_UNIT_BIT, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 bit") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 kbit") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Kibit") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Mbit") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Mibit") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Gbit") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Gibit") == 0); + + kernaux_units_human_raw(123, KERNAUX_UNIT_BYTE, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 Byte") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 kB") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 KiB") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 MB") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 MiB") == 0); + + kernaux_units_human_dec(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 GB") == 0); + kernaux_units_human_bin(123, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, buffer, sizeof(buffer)); + assert(strcmp(buffer, "123 GiB") == 0); +} diff --git a/fixtures/.gitignore b/fixtures/.gitignore new file mode 100644 index 0000000..248bdd9 --- /dev/null +++ b/fixtures/.gitignore @@ -0,0 +1,8 @@ +/multiboot2_bin_examples_gen +/multiboot2_bin_examples_gen.c +/multiboot2_header_example0.bin +/multiboot2_header_example1.bin +/multiboot2_header_example2.bin +/multiboot2_info_example0.bin +/multiboot2_info_example1.bin +/multiboot2_info_example2.bin diff --git a/fixtures/Makefile.am b/fixtures/Makefile.am new file mode 100644 index 0000000..060b2b2 --- /dev/null +++ b/fixtures/Makefile.am @@ -0,0 +1,61 @@ +include $(top_srcdir)/make/shared.am + +noinst_PROGRAMS = +nodist_noinst_DATA = + +EXTRA_DIST = \ + multiboot2_header_example0.txt \ + multiboot2_header_example1.txt \ + multiboot2_header_example2.txt \ + multiboot2_info_example0.txt \ + multiboot2_info_example1.txt \ + multiboot2_info_example2.txt + +######################################### +# multiboot2_(header|info)_example*.bin # +######################################### + +if WITH_MULTIBOOT2 +nodist_noinst_DATA += \ + multiboot2_header_example0.bin \ + multiboot2_header_example1.bin \ + multiboot2_header_example2.bin \ + multiboot2_info_example0.bin \ + multiboot2_info_example1.bin \ + multiboot2_info_example2.bin +endif + +multiboot2_header_example0.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen header 0 + +multiboot2_header_example1.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen header 1 + +multiboot2_header_example2.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen header 2 + +multiboot2_info_example0.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen info 0 + +multiboot2_info_example1.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen info 1 + +multiboot2_info_example2.bin: multiboot2_bin_examples_gen + ./multiboot2_bin_examples_gen info 2 + +############################### +# multiboot2_bin_examples_gen # +############################### + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_bin_examples_gen +multiboot2_bin_examples_gen_LDADD = $(top_builddir)/libkernaux.la +nodist_multiboot2_bin_examples_gen_SOURCES = multiboot2_bin_examples_gen.c +multiboot2_bin_examples_gen_SOURCES = \ + multiboot2_header_example0.h \ + multiboot2_header_example1.h \ + multiboot2_header_example2.h \ + multiboot2_info_example0.h \ + multiboot2_info_example1.h \ + multiboot2_info_example2.h +endif diff --git a/fixtures/cmdline.yml b/fixtures/cmdline.yml new file mode 100644 index 0000000..1ebef83 --- /dev/null +++ b/fixtures/cmdline.yml @@ -0,0 +1,241 @@ +# TODO: move here test cases from tests/test_cmdline.c + +- cmdline: '' + result: [] +- cmdline: ' ' + result: [] +- cmdline: 'foo' + result: ['foo'] +- cmdline: 'foo bar' + result: ['foo', 'bar'] +- cmdline: 'foo bar ' + result: ['foo', 'bar'] +- cmdline: ' foo bar ' + result: ['foo', 'bar'] +- cmdline: 'foo bar' + result: ['foo', 'bar'] +- cmdline: ' foo bar' + result: ['foo', 'bar'] +- cmdline: 'foo bar ' + result: ['foo', 'bar'] +- cmdline: ' foo bar ' + result: ['foo', 'bar'] +- cmdline: 'foo bar car' + result: ['foo', 'bar', 'car'] + +- cmdline: '\"\"' + result: [''] +- cmdline: '\"\" \"\"' + result: ['', ''] +- cmdline: '\"\" \"\" \"\"' + result: ['', '', ''] +- cmdline: '\"foo\"' + result: ['foo'] +- cmdline: '\"foo\" \"bar\"' + result: ['foo', 'bar'] +- cmdline: ' \"foo\" \"bar\"' + result: ['foo', 'bar'] +- cmdline: '\"foo\" \"bar\" ' + result: ['foo', 'bar'] +- cmdline: ' \"foo\" \"bar\" ' + result: ['foo', 'bar'] +- cmdline: '\"foo\" \"bar\"' + result: ['foo', 'bar'] +- cmdline: '\"foo\" \"bar\" ' + result: ['foo', 'bar'] +- cmdline: ' \"foo\" \"bar\"' + result: ['foo', 'bar'] +- cmdline: ' \"foo\" \"bar\" ' + result: ['foo', 'bar'] +- cmdline: '\"foo\" \"bar\" \"car\"' + result: ['foo', 'bar', 'car'] + +- cmdline: 'foo bar car' + arg_count_max: 3 + result: ['foo', 'bar', 'car'] +- cmdline: 'foo bar car' + buffer_size: 12 + result: ['foo', 'bar', 'car'] +- cmdline: 'foo bar car' + arg_count_max: 3 + buffer_size: 12 + result: ['foo', 'bar', 'car'] + +- cmdline: 'foo bar car' + arg_count_max: 2 + error: 'too many args' +- cmdline: 'foo bar car' + buffer_size: 11 + error: 'EOF or buffer overflow' +- cmdline: 'foo bar car' + arg_count_max: 2 + buffer_size: 11 + error: 'too many args' + +- cmdline: '\"foo\" \"bar\" \"car\"' + arg_count_max: 3 + result: ['foo', 'bar', 'car'] +- cmdline: '\"foo\" \"bar\" \"car\"' + buffer_size: 12 + result: ['foo', 'bar', 'car'] +- cmdline: '\"foo\" \"bar\" \"car\"' + arg_count_max: 3 + buffer_size: 12 + result: ['foo', 'bar', 'car'] + +- cmdline: '\"foo\" \"bar\" \"car\"' + arg_count_max: 2 + error: 'too many args' +- cmdline: '\"foo\" \"bar\" \"car\"' + buffer_size: 11 + error: 'EOF or buffer overflow' +- cmdline: '\"foo\" \"bar\" \"car\"' + arg_count_max: 2 + buffer_size: 11 + error: 'too many args' + +- cmdline: '\\ ' + result: [' '] +- cmdline: '\"\\ \"' + result: [' '] +- cmdline: '\\\\' + result: ['\\'] +- cmdline: '\"\\\\\"' + result: ['\\'] +- cmdline: '\\\"' + result: ['\"'] +- cmdline: '\"\\\"\"' + result: ['\"'] +- cmdline: 'foo\\ ' + result: ['foo '] +- cmdline: '\"foo\\ \"' + result: ['foo '] +- cmdline: 'foo\\\\' + result: ['foo\\'] +- cmdline: '\"foo\\\\\"' + result: ['foo\\'] +- cmdline: 'foo\\\"' + result: ['foo\"'] +- cmdline: '\"foo\\\"\"' + result: ['foo\"'] +- cmdline: '\\ foo' + result: [' foo'] +- cmdline: '\"\\ foo\"' + result: [' foo'] +- cmdline: '\\\\foo' + result: ['\\foo'] +- cmdline: '\"\\\\foo\"' + result: ['\\foo'] +- cmdline: '\\\"foo' + result: ['\"foo'] +- cmdline: '\"\\\"foo\"' + result: ['\"foo'] +- cmdline: '\\ foo\\ ' + result: [' foo '] +- cmdline: '\"\\ foo\\ \"' + result: [' foo '] +- cmdline: '\\\\foo\\\\' + result: ['\\foo\\'] +- cmdline: '\"\\\\foo\\\\\"' + result: ['\\foo\\'] +- cmdline: '\\\"foo\\\"' + result: ['\"foo\"'] +- cmdline: '\"\\\"foo\\\"\"' + result: ['\"foo\"'] +- cmdline: 'foo\\ bar' + result: ['foo bar'] +- cmdline: '\"foo\\ bar\"' + result: ['foo bar'] +- cmdline: 'foo\\\\bar' + result: ['foo\\bar'] +- cmdline: '\"foo\\\\bar\"' + result: ['foo\\bar'] +- cmdline: 'foo\\\"bar' + result: ['foo\"bar'] +- cmdline: '\"foo\\\"bar\"' + result: ['foo\"bar'] +- cmdline: '\\ foo bar' + result: [' foo', 'bar'] +- cmdline: '\"\\ foo\" bar' + result: [' foo', 'bar'] +- cmdline: '\\\\foo bar' + result: ['\\foo', 'bar'] +- cmdline: '\"\\\\foo\" bar' + result: ['\\foo', 'bar'] +- cmdline: '\\\"foo bar' + result: ['\"foo', 'bar'] +- cmdline: '\"\\\"foo\" bar' + result: ['\"foo', 'bar'] +- cmdline: 'foo\\ bar' + result: ['foo ', 'bar'] +- cmdline: '\"foo\\ \" bar' + result: ['foo ', 'bar'] +- cmdline: 'foo\\\\ bar' + result: ['foo\\', 'bar'] +- cmdline: '\"foo\\\\\" bar' + result: ['foo\\', 'bar'] +- cmdline: 'foo\\\" bar' + result: ['foo\"', 'bar'] +- cmdline: '\"foo\\\"\" bar' + result: ['foo\"', 'bar'] +- cmdline: '\\ foo\\ bar' + result: [' foo ', 'bar'] +- cmdline: '\"\\ foo\\ \" bar' + result: [' foo ', 'bar'] +- cmdline: '\\\\foo\\\\ bar' + result: ['\\foo\\', 'bar'] +- cmdline: '\"\\\\foo\\\\\" bar' + result: ['\\foo\\', 'bar'] +- cmdline: '\\\"foo\\\" bar' + result: ['\"foo\"', 'bar'] +- cmdline: '\"\\\"foo\\\"\" bar' + result: ['\"foo\"', 'bar'] +- cmdline: 'foo \\ bar' + result: ['foo', ' bar'] +- cmdline: 'foo \"\\ bar\"' + result: ['foo', ' bar'] +- cmdline: 'foo \\\\bar' + result: ['foo', '\\bar'] +- cmdline: 'foo \"\\\\bar\"' + result: ['foo', '\\bar'] +- cmdline: 'foo \\\"bar' + result: ['foo', '\"bar'] +- cmdline: 'foo \"\\\"bar\"' + result: ['foo', '\"bar'] +- cmdline: 'foo bar\\ ' + result: ['foo', 'bar '] +- cmdline: 'foo \"bar\\ \"' + result: ['foo', 'bar '] +- cmdline: 'foo bar\\\\' + result: ['foo', 'bar\\'] +- cmdline: 'foo \"bar\\\\\"' + result: ['foo', 'bar\\'] +- cmdline: 'foo bar\\\"' + result: ['foo', 'bar\"'] +- cmdline: 'foo \"bar\\\"\"' + result: ['foo', 'bar\"'] +- cmdline: 'foo \\ bar\\ ' + result: ['foo', ' bar '] +- cmdline: 'foo \"\\ bar\\ \"' + result: ['foo', ' bar '] +- cmdline: 'foo \\\\bar\\\\' + result: ['foo', '\\bar\\'] +- cmdline: 'foo \"\\\\bar\\\\\"' + result: ['foo', '\\bar\\'] +- cmdline: 'foo \\\"bar\\\"' + result: ['foo', '\"bar\"'] +- cmdline: 'foo \"\\\"bar\\\"\"' + result: ['foo', '\"bar\"'] + +- cmdline: 'foo\\ bar car' + result: ["foo bar", "car"] +- cmdline: '\"foo bar\" car' + result: ["foo bar", "car"] +- cmdline: '\"foo bar\" \"car\"' + result: ["foo bar", "car"] +- cmdline: 'foo bar\\ car' + result: ['foo', 'bar car'] +- cmdline: 'foo \"bar car\"' + result: ['foo', 'bar car'] +- cmdline: '\"foo\" \"bar car\"' + result: ['foo', 'bar car'] diff --git a/fixtures/multiboot2_bin_examples_gen.c.in b/fixtures/multiboot2_bin_examples_gen.c.in new file mode 100644 index 0000000..5f7feab --- /dev/null +++ b/fixtures/multiboot2_bin_examples_gen.c.in @@ -0,0 +1,84 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include + +#include "multiboot2_header_example0.h" +#include "multiboot2_header_example1.h" +#include "multiboot2_header_example2.h" +#include "multiboot2_info_example0.h" +#include "multiboot2_info_example1.h" +#include "multiboot2_info_example2.h" + +static void assert_cb( + const char *const file, + const int line, + const char *const msg +) { + fprintf(stderr, "%s:%d:%s\n", file, line, msg); + abort(); +} + +#define EXAMPLE(type, number) do { \ + static const char *const filename = \ + "@abs_top_builddir@/fixtures/multiboot2_"#type"_example"#number".bin"; \ + FILE *const file = fopen(filename, "w"); \ + assert(file); \ + assert( \ + fwrite( \ + &multiboot2_##type##_example##number, \ + 1, \ + sizeof(multiboot2_##type##_example##number), \ + file \ + ) == sizeof(multiboot2_##type##_example##number) \ + ); \ + assert(fclose(file) == 0); \ +} while (0) + +int main(const int argc, const char *const *const argv) +{ + kernaux_assert_cb = assert_cb; + + assert(argc == 3); + + const char *const type = argv[1]; + const char *const number = argv[2]; + + if (strcmp(type, "header") == 0) { + if (strcmp(number, "0") == 0) { + EXAMPLE(header, 0); + } + else if (strcmp(number, "1") == 0) { + EXAMPLE(header, 1); + } + else if (strcmp(number, "2") == 0) { + EXAMPLE(header, 2); + } + else { + abort(); + } + } else if (strcmp(type, "info") == 0) { + if (strcmp(number, "0") == 0) { + EXAMPLE(info, 0); + } + else if (strcmp(number, "1") == 0) { + EXAMPLE(info, 1); + } + else if (strcmp(number, "2") == 0) { + EXAMPLE(info, 2); + } + else { + abort(); + } + } else { + abort(); + } + + exit(EXIT_SUCCESS); +} diff --git a/fixtures/multiboot2_header_example0.h b/fixtures/multiboot2_header_example0.h new file mode 100644 index 0000000..f0b0f57 --- /dev/null +++ b/fixtures/multiboot2_header_example0.h @@ -0,0 +1,32 @@ +#include +#include + +#include + +// Minimal example +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_HEADER_ALIGN) +static const struct { + struct KernAux_Multiboot2_Header multiboot2_header; + struct KernAux_Multiboot2_HTag_None tag_none; +} +KERNAUX_PACKED +multiboot2_header_example0 = { + .multiboot2_header = { + .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC, + .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + .total_size = sizeof(multiboot2_header_example0), + .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM( + KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + sizeof(multiboot2_header_example0) + ), + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_NONE, + .flags = 0, + .size = sizeof(multiboot2_header_example0.tag_none), + }, + }, +}; + +#include diff --git a/fixtures/multiboot2_header_example0.txt b/fixtures/multiboot2_header_example0.txt new file mode 100644 index 0000000..4a9ceda --- /dev/null +++ b/fixtures/multiboot2_header_example0.txt @@ -0,0 +1,11 @@ +Multiboot 2 header { + u32 magic: 0xe85250d6 + u32 arch: 0 (i386) + u32 size: 24 + u32 checksum: 0x17adaf12 +} +Multiboot 2 header tag { + u16 type: 0 (none) + u16 flags: 0x0 () + u32 size: 8 +} diff --git a/fixtures/multiboot2_header_example1.h b/fixtures/multiboot2_header_example1.h new file mode 100644 index 0000000..5d86a00 --- /dev/null +++ b/fixtures/multiboot2_header_example1.h @@ -0,0 +1,96 @@ +#include +#include + +#include + +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_HEADER_ALIGN) +static const struct { + struct KernAux_Multiboot2_Header multiboot2_header; + + struct KernAux_Multiboot2_HTag_Flags tag_flags1; + uint8_t _align1[4]; + + struct KernAux_Multiboot2_HTag_Flags tag_flags2; + uint8_t _align2[4]; + + struct KernAux_Multiboot2_HTag_Flags tag_flags3; + uint8_t _align3[4]; + + struct KernAux_Multiboot2_HTag_Flags tag_flags4; + uint8_t _align4[4]; + + struct { + struct KernAux_Multiboot2_HTag_InfoReq tag; + uint32_t mbi_tag_types[1]; + } tag_info_req; + uint8_t _align5[4]; + + struct KernAux_Multiboot2_HTag_None tag_none; +} +KERNAUX_PACKED +multiboot2_header_example1 = { + .multiboot2_header = { + .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC, + .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32, + .total_size = sizeof(multiboot2_header_example1), + .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM( + KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32, + sizeof(multiboot2_header_example1) + ), + }, + .tag_flags1 = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS, + .flags = 0, + .size = sizeof(multiboot2_header_example1.tag_flags1), + }, + .console_flags = 0, + }, + .tag_flags2 = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS, + .flags = 0, + .size = sizeof(multiboot2_header_example1.tag_flags2), + }, + .console_flags = KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE, + }, + .tag_flags3 = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS, + .flags = 0, + .size = sizeof(multiboot2_header_example1.tag_flags3), + }, + .console_flags = KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT, + }, + .tag_flags4 = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS, + .flags = 0, + .size = sizeof(multiboot2_header_example1.tag_flags4), + }, + .console_flags = + KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE | + KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT, + }, + .tag_info_req = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_INFO_REQ, + .flags = KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL, + .size = sizeof(multiboot2_header_example1.tag_info_req), + }, + }, + .mbi_tag_types = { + KERNAUX_MULTIBOOT2_ITAG_NONE, + }, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_NONE, + .flags = 0, + .size = sizeof(multiboot2_header_example1.tag_none), + }, + }, +}; + +#include diff --git a/fixtures/multiboot2_header_example1.txt b/fixtures/multiboot2_header_example1.txt new file mode 100644 index 0000000..eb8385e --- /dev/null +++ b/fixtures/multiboot2_header_example1.txt @@ -0,0 +1,52 @@ +Multiboot 2 header { + u32 magic: 0xe85250d6 + u32 arch: 4 (MIPS32) + u32 size: 104 + u32 checksum: 0x17adaebe +} +Multiboot 2 header tag { + u16 type: 4 (flags) + u16 flags: 0x0 () + u32 size: 12 + u32 console_flags: 0x0 () +} +Multiboot 2 header tag { + u16 type: 4 (flags) + u16 flags: 0x0 () + u32 size: 12 + u32 console_flags: 0x1 ( + REQUIRE_CONSOLE + ) +} +Multiboot 2 header tag { + u16 type: 4 (flags) + u16 flags: 0x0 () + u32 size: 12 + u32 console_flags: 0x2 ( + EGA_SUPPORT + ) +} +Multiboot 2 header tag { + u16 type: 4 (flags) + u16 flags: 0x0 () + u32 size: 12 + u32 console_flags: 0x3 ( + REQUIRE_CONSOLE | + EGA_SUPPORT + ) +} +Multiboot 2 header tag { + u16 type: 1 (information request) + u16 flags: 0x1 ( + OPTIONAL + ) + u32 size: 12 + u32 mbi_tag_types[]: [ + 0 (none) + ] +} +Multiboot 2 header tag { + u16 type: 0 (none) + u16 flags: 0x0 () + u32 size: 8 +} diff --git a/fixtures/multiboot2_header_example2.h b/fixtures/multiboot2_header_example2.h new file mode 100644 index 0000000..704458d --- /dev/null +++ b/fixtures/multiboot2_header_example2.h @@ -0,0 +1,172 @@ +#include +#include + +#include + +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_HEADER_ALIGN) +static const struct { + struct KernAux_Multiboot2_Header multiboot2_header; + + struct { + struct KernAux_Multiboot2_HTag_InfoReq tag; + uint32_t mbi_tag_types[22]; + } tag_info_req; + + struct KernAux_Multiboot2_HTag_Addr tag_addr; + + struct KernAux_Multiboot2_HTag_EntryAddr tag_entry_addr; + uint8_t _align1[4]; + + struct KernAux_Multiboot2_HTag_Flags tag_flags; + uint8_t _align2[4]; + + struct KernAux_Multiboot2_HTag_Framebuffer tag_framebuffer; + uint8_t _align3[4]; + + struct KernAux_Multiboot2_HTag_ModuleAlign tag_module_align; + + struct KernAux_Multiboot2_HTag_EFIBootServices tag_efi_boot_services; + + struct KernAux_Multiboot2_HTag_EFII386EntryAddr tag_efi_i386_entry_addr; + uint8_t _align4[4]; + + struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr tag_efi_amd64_entry_addr; + uint8_t _align5[4]; + + struct KernAux_Multiboot2_HTag_RelocatableHeader tag_relocatable_header; + + struct KernAux_Multiboot2_HTag_None tag_none; +} +KERNAUX_PACKED +multiboot2_header_example2 = { + .multiboot2_header = { + .magic = KERNAUX_MULTIBOOT2_HEADER_MAGIC, + .arch = KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + .total_size = sizeof(multiboot2_header_example2), + .checksum = KERNAUX_MULTIBOOT2_HEADER_CHECKSUM( + KERNAUX_MULTIBOOT2_HEADER_ARCH_I386, + sizeof(multiboot2_header_example2) + ), + }, + .tag_info_req = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_INFO_REQ, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_info_req), + }, + }, + .mbi_tag_types = { + KERNAUX_MULTIBOOT2_ITAG_NONE, + KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + KERNAUX_MULTIBOOT2_ITAG_MODULE, + KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + KERNAUX_MULTIBOOT2_ITAG_VBE_INFO, + KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO, + KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS, + KERNAUX_MULTIBOOT2_ITAG_APM_TABLE, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR, + KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES, + KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP, + KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP, + KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO, + KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP, + KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR, + KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR, + }, + }, + .tag_addr = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_ADDR, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_addr), + }, + .header_addr = 0xcafebabe, + .load_addr = 0xdeadbeaf, + .load_end_addr = 0xdeadbabe, + .bss_end_addr = 0xcafebeaf, + }, + .tag_entry_addr = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_entry_addr), + }, + .entry_addr = 0xcafebabe, + }, + .tag_flags = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FLAGS, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_flags), + }, + .console_flags = 0, + }, + .tag_framebuffer = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_framebuffer), + }, + .width = 80, + .height = 25, + .depth = 8, + }, + .tag_module_align = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_module_align), + }, + }, + .tag_efi_boot_services = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_efi_boot_services), + }, + }, + .tag_efi_i386_entry_addr = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_efi_i386_entry_addr), + }, + .entry_addr = 0xcafebabe, + }, + .tag_efi_amd64_entry_addr = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_efi_amd64_entry_addr), + }, + .entry_addr = 0xdeadbeaf, + }, + .tag_relocatable_header = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_relocatable_header), + }, + .min_addr = 0xcafebabe, + .max_addr = 0xdeadbeaf, + .align = 8, + .preference = + KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_HTAG_NONE, + .flags = 0, + .size = sizeof(multiboot2_header_example2.tag_none), + }, + }, +}; + +#include diff --git a/fixtures/multiboot2_header_example2.txt b/fixtures/multiboot2_header_example2.txt new file mode 100644 index 0000000..7d66af3 --- /dev/null +++ b/fixtures/multiboot2_header_example2.txt @@ -0,0 +1,100 @@ +Multiboot 2 header { + u32 magic: 0xe85250d6 + u32 arch: 0 (i386) + u32 size: 272 + u32 checksum: 0x17adae1a +} +Multiboot 2 header tag { + u16 type: 1 (information request) + u16 flags: 0x0 () + u32 size: 96 + u32 mbi_tag_types[]: [ + 0 (none) + 1 (boot cmd line) + 2 (boot loader name) + 3 (module) + 4 (basic memory info) + 5 (BIOS boot device) + 6 (memory map) + 7 (VBE info) + 8 (framebuffer info) + 9 (ELF symbols) + 10 (APM table) + 11 (EFI 32bit system table ptr) + 12 (EFI 64bit system table ptr) + 13 (SMBIOS tables) + 14 (ACPI old RSDP) + 15 (ACPI new RSDP) + 16 (networking info) + 17 (EFI memory map) + 18 (EFI boot services not terminated) + 19 (EFI 32bit image handle ptr) + 20 (EFI 64bit image handle ptr) + 21 (image load base phys addr) + ] +} +Multiboot 2 header tag { + u16 type: 2 (address) + u16 flags: 0x0 () + u32 size: 24 + u32 header_addr: 0xcafebabe + u32 load_addr: 0xdeadbeaf + u32 load_end_addr: 0xdeadbabe + u32 bss_end_addr: 0xcafebeaf +} +Multiboot 2 header tag { + u16 type: 3 (entry address) + u16 flags: 0x0 () + u32 size: 12 + u32 entry_addr: 0xcafebabe +} +Multiboot 2 header tag { + u16 type: 4 (flags) + u16 flags: 0x0 () + u32 size: 12 + u32 console_flags: 0x0 () +} +Multiboot 2 header tag { + u16 type: 5 (framebuffer) + u16 flags: 0x0 () + u32 size: 20 + u32 width: 80 + u32 height: 25 + u32 depth: 8 +} +Multiboot 2 header tag { + u16 type: 6 (module alignment) + u16 flags: 0x0 () + u32 size: 8 +} +Multiboot 2 header tag { + u16 type: 7 (EFI boot services) + u16 flags: 0x0 () + u32 size: 8 +} +Multiboot 2 header tag { + u16 type: 8 (EFI i386 entry address) + u16 flags: 0x0 () + u32 size: 12 + u32 entry_addr: 0xcafebabe +} +Multiboot 2 header tag { + u16 type: 9 (EFI amd64 entry address) + u16 flags: 0x0 () + u32 size: 12 + u32 entry_addr: 0xdeadbeaf +} +Multiboot 2 header tag { + u16 type: 10 (relocatable header) + u16 flags: 0x0 () + u32 size: 24 + u32 min_addr: 0xcafebabe + u32 max_addr: 0xdeadbeaf + u32 align: 8 + u32 preference: 1 (lowest) +} +Multiboot 2 header tag { + u16 type: 0 (none) + u16 flags: 0x0 () + u32 size: 8 +} diff --git a/fixtures/multiboot2_info_example0.h b/fixtures/multiboot2_info_example0.h new file mode 100644 index 0000000..ec33ee0 --- /dev/null +++ b/fixtures/multiboot2_info_example0.h @@ -0,0 +1,26 @@ +#include +#include + +#include + +// Minimal example +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_INFO_ALIGN) +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_None tag_none; +} +KERNAUX_PACKED +multiboot2_info_example0 = { + .multiboot2_info = { + .total_size = sizeof(multiboot2_info_example0), + .reserved = 0, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = sizeof(multiboot2_info_example0.tag_none), + }, + }, +}; + +#include diff --git a/fixtures/multiboot2_info_example0.txt b/fixtures/multiboot2_info_example0.txt new file mode 100644 index 0000000..9fbe289 --- /dev/null +++ b/fixtures/multiboot2_info_example0.txt @@ -0,0 +1,8 @@ +Multiboot 2 info { + u32 size: 16 + u32 reserved: 0x0 +} +Multiboot 2 info tag { + u32 type: 0 (none) + u32 size: 8 +} diff --git a/fixtures/multiboot2_info_example1.h b/fixtures/multiboot2_info_example1.h new file mode 100644 index 0000000..66e0943 --- /dev/null +++ b/fixtures/multiboot2_info_example1.h @@ -0,0 +1,60 @@ +#include +#include + +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_INFO_ALIGN) +static const uint8_t multiboot2_info_example1[864] = { + 96, 3, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 12, 0, 0, 0, + 0, 0, 64, 0, 0, 0, 232, 133, 1, 0, 0, 0, 21, 0, 0, 0, + 104, 101, 108, 108, 111, 32, 107, 101, 114, 110, 101, 108, 0, 139, 69, 228, + 2, 0, 0, 0, 30, 0, 0, 0, 71, 82, 85, 66, 32, 50, 46, 48, + 50, 45, 50, 117, 98, 117, 110, 116, 117, 56, 46, 50, 48, 0, 0, 0, + 10, 0, 0, 0, 28, 0, 0, 0, 2, 1, 0, 240, 207, 212, 0, 0, + 0, 240, 0, 240, 3, 0, 240, 255, 240, 255, 240, 255, 1, 0, 0, 0, + 3, 0, 0, 0, 29, 0, 0, 0, 0, 32, 16, 0, 156, 50, 16, 0, + 104, 101, 108, 108, 111, 32, 109, 111, 100, 117, 108, 101, 0, 131, 255, 16, + 3, 0, 0, 0, 17, 0, 0, 0, 0, 64, 16, 0, 84, 82, 16, 0, + 0, 232, 99, 9, 0, 0, 235, 53, 6, 0, 0, 0, 160, 0, 0, 0, + 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 252, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 252, 9, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 238, 7, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 7, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 255, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 9, 0, 0, 0, 164, 1, 0, 0, + 10, 0, 0, 0, 40, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 27, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 64, 0, 0, 16, 0, 0, 150, 53, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 0, 0, 0, 64, 64, 0, 0, 80, 0, 0, + 8, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 41, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 8, 75, 64, 0, 8, 91, 0, 0, 132, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 1, 0, 0, 0, 3, 0, 0, 0, 0, 96, 64, 0, 0, 112, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 57, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, + 0, 112, 64, 0, 1, 112, 0, 0, 64, 169, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 1, 0, 0, 0, 48, 0, 0, 0, 0, 0, 16, 0, 1, 112, 0, 0, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 20, 0, 16, 0, 20, 112, 0, 0, 176, 12, 0, 0, 8, 0, 0, 0, + 72, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 9, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 196, 12, 16, 0, 196, 124, 0, 0, + 208, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 17, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 148, 26, 16, 0, 148, 138, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 217, 255, 255, 137, + 4, 0, 0, 0, 16, 0, 0, 0, 127, 2, 0, 0, 128, 251, 1, 0, + 5, 0, 0, 0, 20, 0, 0, 0, 224, 0, 0, 0, 255, 255, 255, 255, + 255, 255, 255, 255, 208, 137, 69, 212, 8, 0, 0, 0, 32, 0, 0, 0, + 0, 128, 11, 0, 0, 0, 0, 0, 160, 0, 0, 0, 80, 0, 0, 0, + 25, 0, 0, 0, 16, 2, 0, 0, 14, 0, 0, 0, 28, 0, 0, 0, + 82, 83, 68, 32, 80, 84, 82, 32, 60, 66, 79, 67, 72, 83, 32, 0, + 252, 21, 254, 7, 236, 0, 0, 235, 0, 0, 0, 0, 8, 0, 0, 0, +}; diff --git a/fixtures/multiboot2_info_example1.txt b/fixtures/multiboot2_info_example1.txt new file mode 100644 index 0000000..fcf7b20 --- /dev/null +++ b/fixtures/multiboot2_info_example1.txt @@ -0,0 +1,264 @@ +Multiboot 2 info { + u32 size: 864 + u32 reserved: 0x0 +} +Multiboot 2 info tag { + u32 type: 21 (image load base phys addr) + u32 size: 12 + u32 load_base_addr: 0x400000 +} +Multiboot 2 info tag { + u32 type: 1 (boot cmd line) + u32 size: 21 + char cmdline[]: "hello kernel" +} +Multiboot 2 info tag { + u32 type: 2 (boot loader name) + u32 size: 30 + char name[]: "GRUB 2.02-2ubuntu8.20" +} +Multiboot 2 info tag { + u32 type: 10 (APM table) + u32 size: 28 + u16 version: 258 + u16 cseg: 61440 + u32 offset: 54479 + u16 cseg_16: 61440 + u16 dseg: 61440 + u16 flags: 3 + u16 cseg_len: 65520 + u16 cseg_16_len: 65520 + u16 dseg_len: 65520 +} +Multiboot 2 info tag { + u32 type: 3 (module) + u32 size: 29 + u32 mod_start: 0x102000 + u32 mod_end: 0x10329c + char cmdline[]: "hello module" +} +Multiboot 2 info tag { + u32 type: 3 (module) + u32 size: 17 + u32 mod_start: 0x104000 + u32 mod_end: 0x105254 + char cmdline[]: "" +} +Multiboot 2 info tag { + u32 type: 6 (memory map) + u32 size: 160 + u32 entry_size: 24 + u32 entry_version: 0 + varies(entry_size) entries[]: [ + [0]: { + u64 base_addr: 0x0 + u64 length: 654336 + u32 type: 1 (available) + u32 reserved: 0x0 + } + [1]: { + u64 base_addr: 0x9fc00 + u64 length: 1024 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [2]: { + u64 base_addr: 0xf0000 + u64 length: 65536 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [3]: { + u64 base_addr: 0x100000 + u64 length: 133038080 + u32 type: 1 (available) + u32 reserved: 0x0 + } + [4]: { + u64 base_addr: 0x7fe0000 + u64 length: 131072 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [5]: { + u64 base_addr: 0xfffc0000 + u64 length: 262144 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + ] +} +Multiboot 2 info tag { + u32 type: 9 (ELF symbols) + u32 size: 420 + u32 num: 10 + u32 entsize: 40 + u32 shndx: 9 + varies(entsize) section_headers[]: [ + [0]: { + name: 0 + type: 0 (NULL) + flags: 0x0 () + addr: 0x0 + offset: 0x0 + size: 0 + link: 0 + info: 0 + addralign: 0 + entsize: 0 + } + [1]: { + name: 27 + type: 1 (PROGBITS) + flags: 0x6 ( + ALLOC | + EXECINSTR + ) + addr: 0x400000 + offset: 0x1000 + size: 13718 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [2]: { + name: 33 + type: 1 (PROGBITS) + flags: 0x2 ( + ALLOC + ) + addr: 0x404000 + offset: 0x5000 + size: 2824 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [3]: { + name: 41 + type: 1 (PROGBITS) + flags: 0x2 ( + ALLOC + ) + addr: 0x404b08 + offset: 0x5b08 + size: 2692 + link: 0 + info: 0 + addralign: 4 + entsize: 0 + } + [4]: { + name: 51 + type: 1 (PROGBITS) + flags: 0x3 ( + WRITE | + ALLOC + ) + addr: 0x406000 + offset: 0x7000 + size: 1 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [5]: { + name: 57 + type: 8 (NOBITS) + flags: 0x3 ( + WRITE | + ALLOC + ) + addr: 0x407000 + offset: 0x7001 + size: 43328 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [6]: { + name: 62 + type: 1 (PROGBITS) + flags: 0x30 () + addr: 0x100000 + offset: 0x7001 + size: 17 + link: 0 + info: 0 + addralign: 1 + entsize: 1 + } + [7]: { + name: 1 + type: 2 (SYMTAB) + flags: 0x0 () + addr: 0x100014 + offset: 0x7014 + size: 3248 + link: 8 + info: 72 + addralign: 4 + entsize: 16 + } + [8]: { + name: 9 + type: 3 (STRTAB) + flags: 0x0 () + addr: 0x100cc4 + offset: 0x7cc4 + size: 3536 + link: 0 + info: 0 + addralign: 1 + entsize: 0 + } + [9]: { + name: 17 + type: 3 (STRTAB) + flags: 0x0 () + addr: 0x101a94 + offset: 0x8a94 + size: 71 + link: 0 + info: 0 + addralign: 1 + entsize: 0 + } + ] +} +Multiboot 2 info tag { + u32 type: 4 (basic memory info) + u32 size: 16 + u32 mem_lower: 639 + u32 mem_upper: 129920 +} +Multiboot 2 info tag { + u32 type: 5 (BIOS boot device) + u32 size: 20 + u32 biosdev: 224 + u32 partition: 4294967295 + u32 sub_partition: 4294967295 +} +Multiboot 2 info tag { + u32 type: 8 (framebuffer info) + u32 size: 32 + u64 framebuffer_addr: 0xb8000 + u32 framebuffer_pitch: 160 + u32 framebuffer_width: 80 + u32 framebuffer_height: 25 + u8 framebuffer_bpp: 16 + u8 framebuffer_type: 2 + u16 reserved: 0x0 +} +Multiboot 2 info tag { + u32 type: 14 (ACPI old RSDP) + u32 size: 28 +} +Multiboot 2 info tag { + u32 type: 0 (none) + u32 size: 8 +} diff --git a/fixtures/multiboot2_info_example2.h b/fixtures/multiboot2_info_example2.h new file mode 100644 index 0000000..9b204c9 --- /dev/null +++ b/fixtures/multiboot2_info_example2.h @@ -0,0 +1,393 @@ +#include +#include + +#include + +KERNAUX_ALIGNED(KERNAUX_MULTIBOOT2_INFO_ALIGN) +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + + struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[15]; + } tag_boot_cmd_line; + uint8_t _align1[1]; + + struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[22]; + } tag_boot_loader_name; + uint8_t _align2[2]; + + struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[17]; + } tag_module1; + uint8_t _align3[7]; + + struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[17]; + } tag_module2; + uint8_t _align4[7]; + + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + + struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device; + uint8_t _align5[4]; + + struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + + uint8_t data[160 - sizeof(struct KernAux_Multiboot2_ITag_MemoryMap)]; + } tag_memory_map; + + struct KernAux_Multiboot2_ITag_VBEInfo tag_vbe_info; + + struct { + struct KernAux_Multiboot2_ITag_FramebufferInfo tag; + uint8_t data[8]; + } tag_framebuffer_info; + + struct { + struct KernAux_Multiboot2_ITag_ELFSymbols tag; + + uint8_t data[420 - sizeof(struct KernAux_Multiboot2_ITag_ELFSymbols)]; + } tag_elf_symbols; + uint8_t _align7[4]; + + struct KernAux_Multiboot2_ITag_APMTable tag_apm_table; + uint8_t _align8[4]; + + struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr + tag_efi_32bit_system_table_ptr; + uint8_t _align9[4]; + + struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr + tag_efi_64bit_system_table_ptr; + + struct { + struct KernAux_Multiboot2_ITag_SMBIOSTables tag; + uint8_t data[8]; + } tag_smbios_tables; + + struct { + struct KernAux_Multiboot2_ITag_ACPIOldRSDP tag; + uint8_t data[8]; + } tag_acpi_old_rsdp; + + struct { + struct KernAux_Multiboot2_ITag_ACPINewRSDP tag; + uint8_t data[8]; + } tag_acpi_new_rsdp; + + struct { + struct KernAux_Multiboot2_ITag_NetworkingInfo tag; + uint8_t data[8]; + } tag_networking_info; + + struct { + struct KernAux_Multiboot2_ITag_EFIMemoryMap tag; + uint8_t data[8]; + } tag_efi_memory_map; + + struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated + tag_efi_boot_services_not_terminated; + + struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr + tag_efi_32bit_image_handle_ptr; + uint8_t _align10[4]; + + struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr + tag_efi_64bit_image_handle_ptr; + + struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr + tag_image_load_base_phys_addr; + uint8_t _align11[4]; + + struct KernAux_Multiboot2_ITag_None tag_none; +} +KERNAUX_PACKED +multiboot2_info_example2 = { + .multiboot2_info = { + .total_size = sizeof(multiboot2_info_example2), + .reserved = 0, + }, + .tag_boot_cmd_line = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = sizeof(multiboot2_info_example2.tag_boot_cmd_line), + }, + }, + .cmdline = "Hello, Kernel!\0", + }, + .tag_boot_loader_name = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + .size = sizeof(multiboot2_info_example2.tag_boot_loader_name), + }, + }, + .name = "GRUB 2.02-2ubuntu8.20\0", + }, + .tag_module1 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = sizeof(multiboot2_info_example2.tag_module1), + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "Hello, Module 1!\0", + }, + .tag_module2 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = sizeof(multiboot2_info_example2.tag_module2), + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "Hello, Module 2!\0", + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = sizeof(multiboot2_info_example2.tag_basic_memory_info), + }, + .mem_lower = 123, + .mem_upper = 456, + }, + .tag_bios_boot_device = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = sizeof(multiboot2_info_example2.tag_bios_boot_device), + }, + .biosdev = 0, + .partition = 1, + .sub_partition = 2, + }, + .tag_memory_map = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = sizeof(multiboot2_info_example2.tag_memory_map), + }, + .entry_size = 24, + .entry_version = 0, + }, + .data = { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 252, 9, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + 0, 252, 9, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 238, 7, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 7, 0, 0, 0, 0, + 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 252, 255, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, + 2, 0, 0, 0, 0, 0, 0, 0, + }, + }, + .tag_vbe_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_VBE_INFO, + .size = sizeof(multiboot2_info_example2.tag_vbe_info), + }, + .vbe_mode = 0, + .vbe_interface_seg = 123, + .vbe_interface_off = 456, + .vbe_interface_len = 789, + .vbe_control_info = { + [0] = 0, + [16] = 1, + [32] = 12, + [48] = 123, + [79] = 1, + [95] = 12, + [111] = 123, + }, + .vbe_mode_info = { + [0] = 0, + [16] = 3, + [32] = 32, + [48] = 255, + [79] = 3, + [95] = 32, + [111] = 255, + }, + }, + .tag_framebuffer_info = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO, + .size = sizeof(multiboot2_info_example2.tag_framebuffer_info), + }, + .framebuffer_addr = 123, + .framebuffer_pitch = 456, + .framebuffer_width = 123, + .framebuffer_height = 456, + .framebuffer_bpp = 8, + .framebuffer_type = 1, + }, + .data = {0, 1, 2, 3, 4, 5, 6, 7}, + }, + .tag_elf_symbols = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS, + .size = sizeof(multiboot2_info_example2.tag_elf_symbols), + }, + .num = 10, + .entsize = 40, + .shndx = 9, + }, + .data = { + 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 27, 0, 0, 0, 1, 0, 0, 0, 6, 0, 0, 0, + 0, 0, 64, 0, 0, 16, 0, 0, 150, 53, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 0, 0, 0, 64, 64, 0, 0, 80, 0, 0, + 8, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 41, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, + 8, 75, 64, 0, 8, 91, 0, 0, 132, 10, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, + 1, 0, 0, 0, 3, 0, 0, 0, 0, 96, 64, 0, 0, 112, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 57, 0, 0, 0, 8, 0, 0, 0, 3, 0, 0, 0, + 0, 112, 64, 0, 1, 112, 0, 0, 64, 169, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, + 1, 0, 0, 0, 48, 0, 0, 0, 0, 0, 16, 0, 1, 112, 0, 0, + 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, + 20, 0, 16, 0, 20, 112, 0, 0, 176, 12, 0, 0, 8, 0, 0, 0, + 72, 0, 0, 0, 4, 0, 0, 0, 16, 0, 0, 0, 9, 0, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 196, 12, 16, 0, 196, 124, 0, 0, + 208, 13, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 17, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, + 148, 26, 16, 0, 148, 138, 0, 0, 71, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, + }, + }, + .tag_apm_table = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_APM_TABLE, + .size = sizeof(multiboot2_info_example2.tag_apm_table), + }, + .version = 0, + .cseg = 123, + .offset = 456, + .cseg_16 = 789, + .dseg = 123, + .flags = 1, + .cseg_len = 456, + .cseg_16_len = 789, + .dseg_len = 123, + }, + .tag_efi_32bit_system_table_ptr = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR, + .size = sizeof(multiboot2_info_example2.tag_efi_32bit_system_table_ptr), + }, + .pointer = 0, + }, + .tag_efi_64bit_system_table_ptr = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR, + .size = sizeof(multiboot2_info_example2.tag_efi_64bit_system_table_ptr), + }, + .pointer = 0, + }, + .tag_smbios_tables = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES, + .size = sizeof(multiboot2_info_example2.tag_smbios_tables), + }, + .major = 1, + .minor = 2, + .reserved = {0, 0, 0, 0, 0, 0}, + }, + .data = {0, 0, 0, 0, 0, 0, 0, 0}, + }, + .tag_acpi_old_rsdp = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP, + .size = sizeof(multiboot2_info_example2.tag_acpi_old_rsdp), + }, + }, + .data = {0, 0, 0, 0, 0, 0, 0, 0}, + }, + .tag_acpi_new_rsdp = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP, + .size = sizeof(multiboot2_info_example2.tag_acpi_new_rsdp), + }, + }, + .data = {0, 0, 0, 0, 0, 0, 0, 0}, + }, + .tag_networking_info = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO, + .size = sizeof(multiboot2_info_example2.tag_networking_info), + }, + }, + .data = {0, 0, 0, 0, 0, 0, 0, 0}, + }, + .tag_efi_memory_map = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP, + .size = sizeof(multiboot2_info_example2.tag_efi_memory_map), + }, + .descriptor_size = 123, + .descriptor_version = 1, + }, + .data = {0, 0, 0, 0, 0, 0, 0, 0}, + }, + .tag_efi_boot_services_not_terminated = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED, + .size = sizeof( + multiboot2_info_example2.tag_efi_boot_services_not_terminated + ), + }, + }, + .tag_efi_32bit_image_handle_ptr = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR, + .size = sizeof(multiboot2_info_example2.tag_efi_32bit_image_handle_ptr), + }, + .pointer = 0, + }, + .tag_efi_64bit_image_handle_ptr = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR, + .size = sizeof(multiboot2_info_example2.tag_efi_64bit_image_handle_ptr), + }, + .pointer = 0, + }, + .tag_image_load_base_phys_addr = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR, + .size = sizeof(multiboot2_info_example2.tag_image_load_base_phys_addr), + }, + .load_base_addr = 123, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = sizeof(multiboot2_info_example2.tag_none), + }, + }, +}; + +#include diff --git a/fixtures/multiboot2_info_example2.txt b/fixtures/multiboot2_info_example2.txt new file mode 100644 index 0000000..c4eca9e --- /dev/null +++ b/fixtures/multiboot2_info_example2.txt @@ -0,0 +1,369 @@ +Multiboot 2 info { + u32 size: 1816 + u32 reserved: 0x0 +} +Multiboot 2 info tag { + u32 type: 1 (boot cmd line) + u32 size: 23 + char cmdline[]: "Hello, Kernel!" +} +Multiboot 2 info tag { + u32 type: 2 (boot loader name) + u32 size: 30 + char name[]: "GRUB 2.02-2ubuntu8.20" +} +Multiboot 2 info tag { + u32 type: 3 (module) + u32 size: 33 + u32 mod_start: 0x7b + u32 mod_end: 0x1c8 + char cmdline[]: "Hello, Module 1!" +} +Multiboot 2 info tag { + u32 type: 3 (module) + u32 size: 33 + u32 mod_start: 0x7b + u32 mod_end: 0x1c8 + char cmdline[]: "Hello, Module 2!" +} +Multiboot 2 info tag { + u32 type: 4 (basic memory info) + u32 size: 16 + u32 mem_lower: 123 + u32 mem_upper: 456 +} +Multiboot 2 info tag { + u32 type: 5 (BIOS boot device) + u32 size: 20 + u32 biosdev: 0 + u32 partition: 1 + u32 sub_partition: 2 +} +Multiboot 2 info tag { + u32 type: 6 (memory map) + u32 size: 160 + u32 entry_size: 24 + u32 entry_version: 0 + varies(entry_size) entries[]: [ + [0]: { + u64 base_addr: 0x0 + u64 length: 654336 + u32 type: 1 (available) + u32 reserved: 0x0 + } + [1]: { + u64 base_addr: 0x9fc00 + u64 length: 1024 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [2]: { + u64 base_addr: 0xf0000 + u64 length: 65536 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [3]: { + u64 base_addr: 0x100000 + u64 length: 133038080 + u32 type: 1 (available) + u32 reserved: 0x0 + } + [4]: { + u64 base_addr: 0x7fe0000 + u64 length: 131072 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + [5]: { + u64 base_addr: 0xfffc0000 + u64 length: 262144 + u32 type: 2 (reserved) + u32 reserved: 0x0 + } + ] +} +Multiboot 2 info tag { + u32 type: 7 (VBE info) + u32 size: 784 + u16 vbe_mode: 0 + u16 vbe_interface_seg: 123 + u16 vbe_interface_off: 456 + u16 vbe_interface_len: 789 + u8 vbe_control_info[]: [ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 12 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 123 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 12 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 123 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ] + u8 vbe_mode_info[]: [ + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 32 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 255 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 32 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 255 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 + ] +} +Multiboot 2 info tag { + u32 type: 8 (framebuffer info) + u32 size: 40 + u64 framebuffer_addr: 0x7b + u32 framebuffer_pitch: 456 + u32 framebuffer_width: 123 + u32 framebuffer_height: 456 + u8 framebuffer_bpp: 8 + u8 framebuffer_type: 1 + u16 reserved: 0x0 +} +Multiboot 2 info tag { + u32 type: 9 (ELF symbols) + u32 size: 420 + u32 num: 10 + u32 entsize: 40 + u32 shndx: 9 + varies(entsize) section_headers[]: [ + [0]: { + name: 0 + type: 0 (NULL) + flags: 0x0 () + addr: 0x0 + offset: 0x0 + size: 0 + link: 0 + info: 0 + addralign: 0 + entsize: 0 + } + [1]: { + name: 27 + type: 1 (PROGBITS) + flags: 0x6 ( + ALLOC | + EXECINSTR + ) + addr: 0x400000 + offset: 0x1000 + size: 13718 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [2]: { + name: 33 + type: 1 (PROGBITS) + flags: 0x2 ( + ALLOC + ) + addr: 0x404000 + offset: 0x5000 + size: 2824 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [3]: { + name: 41 + type: 1 (PROGBITS) + flags: 0x2 ( + ALLOC + ) + addr: 0x404b08 + offset: 0x5b08 + size: 2692 + link: 0 + info: 0 + addralign: 4 + entsize: 0 + } + [4]: { + name: 51 + type: 1 (PROGBITS) + flags: 0x3 ( + WRITE | + ALLOC + ) + addr: 0x406000 + offset: 0x7000 + size: 1 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [5]: { + name: 57 + type: 8 (NOBITS) + flags: 0x3 ( + WRITE | + ALLOC + ) + addr: 0x407000 + offset: 0x7001 + size: 43328 + link: 0 + info: 0 + addralign: 4096 + entsize: 0 + } + [6]: { + name: 62 + type: 1 (PROGBITS) + flags: 0x30 () + addr: 0x100000 + offset: 0x7001 + size: 17 + link: 0 + info: 0 + addralign: 1 + entsize: 1 + } + [7]: { + name: 1 + type: 2 (SYMTAB) + flags: 0x0 () + addr: 0x100014 + offset: 0x7014 + size: 3248 + link: 8 + info: 72 + addralign: 4 + entsize: 16 + } + [8]: { + name: 9 + type: 3 (STRTAB) + flags: 0x0 () + addr: 0x100cc4 + offset: 0x7cc4 + size: 3536 + link: 0 + info: 0 + addralign: 1 + entsize: 0 + } + [9]: { + name: 17 + type: 3 (STRTAB) + flags: 0x0 () + addr: 0x101a94 + offset: 0x8a94 + size: 71 + link: 0 + info: 0 + addralign: 1 + entsize: 0 + } + ] +} +Multiboot 2 info tag { + u32 type: 10 (APM table) + u32 size: 28 + u16 version: 0 + u16 cseg: 123 + u32 offset: 456 + u16 cseg_16: 789 + u16 dseg: 123 + u16 flags: 1 + u16 cseg_len: 456 + u16 cseg_16_len: 789 + u16 dseg_len: 123 +} +Multiboot 2 info tag { + u32 type: 11 (EFI 32bit system table ptr) + u32 size: 12 + u32 pointer: 0 +} +Multiboot 2 info tag { + u32 type: 12 (EFI 64bit system table ptr) + u32 size: 16 + u64 pointer: 0 +} +Multiboot 2 info tag { + u32 type: 13 (SMBIOS tables) + u32 size: 24 + u8 major: 1 + u8 minor: 2 + u8 reserved[6]: [0x0, 0x0, 0x0, 0x0, 0x0, 0x0] +} +Multiboot 2 info tag { + u32 type: 14 (ACPI old RSDP) + u32 size: 16 +} +Multiboot 2 info tag { + u32 type: 15 (ACPI new RSDP) + u32 size: 16 +} +Multiboot 2 info tag { + u32 type: 16 (networking info) + u32 size: 16 +} +Multiboot 2 info tag { + u32 type: 17 (EFI memory map) + u32 size: 24 + u32 descriptor_size: 123 + u32 descriptor_version: 1 +} +Multiboot 2 info tag { + u32 type: 18 (EFI boot services not terminated) + u32 size: 8 +} +Multiboot 2 info tag { + u32 type: 19 (EFI 32bit image handle ptr) + u32 size: 12 + u32 pointer: 0 +} +Multiboot 2 info tag { + u32 type: 20 (EFI 64bit image handle ptr) + u32 size: 16 + u64 pointer: 0 +} +Multiboot 2 info tag { + u32 type: 21 (image load base phys addr) + u32 size: 12 + u32 load_base_addr: 0x7b +} +Multiboot 2 info tag { + u32 type: 0 (none) + u32 size: 8 +} diff --git a/fixtures/printf.yml b/fixtures/printf.yml new file mode 100644 index 0000000..490d7be --- /dev/null +++ b/fixtures/printf.yml @@ -0,0 +1,131 @@ +- result: '' + args: [] +- result: '' + args: [''] +- result: '' + args: ['', ''] + +- result: 'Hello, World!' + args: ['Hello, World!'] +- result: 'Hello, Alex!' + args: ['Hello, ', ['%s', 'Alex'], '!'] +- result: 'Hello, Alex!' + args: ['Hello, ', ['%c', ['A']], ['%c', ['l']], ['%c', ['e']], ['%c', ['x']], '!'] + +- result: '%' + args: [['%%']] +- result: '%%' + args: [['%%'], ['%%']] +- result: '%%%' + args: [['%%'], ['%%'], ['%%']] + +- result: '%' + args: [['%*%', 20]] + +- result: '123' + args: [['%u', 123]] +- result: '123456' + args: [['%u', 123], ['%u', 456]] +- result: 'foo' + args: [['%s', 'foo']] +- result: 'foobar' + args: [['%s', 'foo'], ['%s', 'bar']] +- result: 'a' + args: [['%c', ['a']]] +- result: 'ab' + args: [['%c', ['a']], ['%c', ['b']]] +- result: 'abc' + args: [['%c', ['a']], ['%c', ['b']], ['%c', ['c']]] + +- result: '%123fooa' + args: [['%%'], ['%u', 123], ['%s', 'foo'], ['%c', ['a']]] +- result: '%123afoo' + args: [['%%'], ['%u', 123], ['%c', ['a']], ['%s', 'foo']] +- result: '%a123foo' + args: [['%%'], ['%c', ['a']], ['%u', 123], ['%s', 'foo']] +- result: '%afoo123' + args: [['%%'], ['%c', ['a']], ['%s', 'foo'], ['%u', 123]] +- result: '%foo123a' + args: [['%%'], ['%s', 'foo'], ['%u', 123], ['%c', ['a']]] +- result: '%fooa123' + args: [['%%'], ['%s', 'foo'], ['%c', ['a']], ['%u', 123]] +- result: '123%fooa' + args: [['%u', 123], ['%%'], ['%s', 'foo'], ['%c', ['a']]] +- result: '123%afoo' + args: [['%u', 123], ['%%'], ['%c', ['a']], ['%s', 'foo']] +- result: 'a%123foo' + args: [['%c', ['a']], ['%%'], ['%u', 123], ['%s', 'foo']] +- result: 'a%foo123' + args: [['%c', ['a']], ['%%'], ['%s', 'foo'], ['%u', 123]] +- result: 'foo%123a' + args: [['%s', 'foo'], ['%%'], ['%u', 123], ['%c', ['a']]] +- result: 'foo%a123' + args: [['%s', 'foo'], ['%%'], ['%c', ['a']], ['%u', 123]] +- result: '123foo%a' + args: [['%u', 123], ['%s', 'foo'], ['%%'], ['%c', ['a']]] +- result: '123a%foo' + args: [['%u', 123], ['%c', ['a']], ['%%'], ['%s', 'foo']] +- result: 'a123%foo' + args: [['%c', ['a']], ['%u', 123], ['%%'], ['%s', 'foo']] +- result: 'afoo%123' + args: [['%c', ['a']], ['%s', 'foo'], ['%%'], ['%u', 123]] +- result: 'foo123%a' + args: [['%s', 'foo'], ['%u', 123], ['%%'], ['%c', ['a']]] +- result: 'fooa%123' + args: [['%s', 'foo'], ['%c', ['a']], ['%%'], ['%u', 123]] +- result: '123fooa%' + args: [['%u', 123], ['%s', 'foo'], ['%c', ['a']], ['%%']] +- result: '123afoo%' + args: [['%u', 123], ['%c', ['a']], ['%s', 'foo'], ['%%']] +- result: 'a123foo%' + args: [['%c', ['a']], ['%u', 123], ['%s', 'foo'], ['%%']] +- result: 'afoo123%' + args: [['%c', ['a']], ['%s', 'foo'], ['%u', 123], ['%%']] +- result: 'foo123a%' + args: [['%s', 'foo'], ['%u', 123], ['%c', ['a']], ['%%']] +- result: 'fooa123%' + args: [['%s', 'foo'], ['%c', ['a']], ['%u', 123], ['%%']] + +- result: '1.200000' + args: [['%f', 1.2]] + float: true +- result: '123.456789' + args: [['%f', 123.456789]] + float: true + +- result: '0.01234568' + args: [['%.8f', 0.0123456789012345678901234567890123456789]] + float: true +- result: '0.012345679' + args: [['%.9f', 0.0123456789012345678901234567890123456789]] + float: true +# Actual precision is no more than 9 +- result: '0.0123456790' + args: [['%.10f', 0.0123456789012345678901234567890123456789]] + float: true +- result: '0.01234567900' + args: [['%.11f', 0.0123456789012345678901234567890123456789]] + float: true +- result: '0.012345679000' + args: [['%.12f', 0.0123456789012345678901234567890123456789]] + float: true +- result: '0.012345679000000000000000000000' + args: [['%.30f', 0.0123456789012345678901234567890123456789]] + float: true +# Actual length is no more than 32 +- result: '0.012345679000000000000000000000' + args: [['%.31f', 0.0123456789012345678901234567890123456789]] + float: true +- result: '0.012345679000000000000000000000' + args: [['%.32f', 0.0123456789012345678901234567890123456789]] + float: true +# Actual length is no more than 32 +- result: '10.01234567900000000000000000000' + args: [['%.32f', 10.0123456789012345678901234567890123456789]] + float: true +- result: '100.0123456790000000000000000000' + args: [['%.32f', 100.0123456789012345678901234567890123456789]] + float: true +- result: '1000.012345679000000000000000000' + args: [['%.32f', 1000.0123456789012345678901234567890123456789]] + float: true diff --git a/fixtures/printf_fmt.yml b/fixtures/printf_fmt.yml new file mode 100644 index 0000000..fbd9cab --- /dev/null +++ b/fixtures/printf_fmt.yml @@ -0,0 +1,340 @@ +- in: + format: 's' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '-s' + width: null + precision: null + out: + flags: [left] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '+s' + width: null + precision: null + out: + flags: [plus] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: ' s' + width: null + precision: null + out: + flags: [space] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '#s' + width: null + precision: null + out: + flags: [hash] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '0s' + width: null + precision: null + out: + flags: [zeropad] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '123s' + width: null + precision: null + out: + flags: [] + width: 123 + precision: 0 + type: str + base: 0 + +- in: + format: '*s' + width: 123 + precision: null + out: + flags: [] + width: 123 + precision: 0 + type: str + base: 0 + +- in: + format: '.123s' + width: null + precision: null + out: + flags: [precision] + width: 0 + precision: 123 + type: str + base: 0 + +- in: + format: '.*s' + width: null + precision: 123 + out: + flags: [precision] + width: 0 + precision: 123 + type: str + base: 0 + +- in: + format: 'ls' + width: null + precision: null + out: + flags: [long] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: 'lls' + width: null + precision: null + out: + flags: [long, long_long] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: 'hs' + width: null + precision: null + out: + flags: [short] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: 'hhs' + width: null + precision: null + out: + flags: [short, char] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: 'd' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: int + base: 10 + +- in: + format: 'i' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: int + base: 10 + +- in: + format: 'u' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: uint + base: 10 + +- in: + format: 'x' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: uint + base: 16 + +- in: + format: 'X' + width: null + precision: null + out: + flags: [uppercase] + width: 0 + precision: 0 + type: uint + base: 16 + +- in: + format: 'o' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: uint + base: 8 + +- in: + format: 'b' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: uint + base: 2 + +- in: + format: 'f' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: float + base: 0 + +- in: + format: 'F' + width: null + precision: null + out: + flags: [uppercase] + width: 0 + precision: 0 + type: float + base: 0 + +- in: + format: 'e' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: exp + base: 0 + +- in: + format: 'E' + width: null + precision: null + out: + flags: [uppercase] + width: 0 + precision: 0 + type: exp + base: 0 + +- in: + format: 'g' + width: null + precision: null + out: + flags: [adapt_exp] + width: 0 + precision: 0 + type: exp + base: 0 + +- in: + format: 'G' + width: null + precision: null + out: + flags: [adapt_exp, uppercase] + width: 0 + precision: 0 + type: exp + base: 0 + +- in: + format: 'c' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: char + base: 0 + +- in: + format: 's' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: str + base: 0 + +- in: + format: '%' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: percent + base: 0 + +- in: + format: '_' + width: null + precision: null + out: + flags: [] + width: 0 + precision: 0 + type: none + base: 0 diff --git a/fixtures/printf_orig.yml b/fixtures/printf_orig.yml new file mode 100644 index 0000000..a1c669c --- /dev/null +++ b/fixtures/printf_orig.yml @@ -0,0 +1,667 @@ +# The code was taken from Marco Paland's printf. + +# Copyright (c) 2014-2019 Marco Paland +# Copyright (c) 2021-2022 Alex Kotov + +# TODO: add remaining tests from +# https://github.com/mpaland/printf/blob/master/test/test_suite.cpp + +- result: ' 4232' + args: [['% d', 4232]] +- result: 'This is a test of 12EF' + args: [['This is a test of %X', 0x12ef]] +- result: '-1000' + args: [['%d', -1000]] +- result: '-1' + args: [['%d', -1]] +- result: '2345' + args: [['%d', 2345]] +- result: '3 -1000 test' + args: [['%d ', 3], ['%d', -1000], [' %s', 'test']] +- result: '3 -1000 test' + args: [['%d ', 3], ['%d ', -1000], ['%s', 'test']] +- result: '3 -1000 test' + args: [['%d', 3], [' %d', -1000], [' %s', 'test']] +- result: '3 -1000 test' + args: [['%d', 3], ' ', ['%d', -1000], ' ', ['%s', 'test']] + +# space flag + +- result: ' 42' + args: [['% d', 42]] +- result: '-42' + args: [['% d', -42]] +- result: ' 42' + args: [['% 5d', 42]] +- result: ' -42' + args: [['% 5d', -42]] +- result: ' 42' + args: [['% 15d', 42]] +- result: ' -42' + args: [['% 15d', -42]] +- result: ' -42.987' + args: [['% 15.3f', -42.987]] + float: true +- result: ' 42.987' + args: [['% 15.3f', 42.987]] + float: true +- result: 'Hello testing' + args: [['% s', 'Hello testing']] +- result: ' 1024' + args: [['% d', 1024]] +- result: '-1024' + args: [['% d', -1024]] +- result: ' 1024' + args: [['% i', 1024]] +- result: '-1024' + args: [['% i', -1024]] +- result: '1024' + args: [['% u', 1024]] +- result: '4294966272' + args: [['% u', 4294966272]] +- result: '777' + args: [['% o', 511]] +- result: '37777777001' + args: [['% o', 4294966785]] +- result: '1234abcd' + args: [['% x', 305441741]] +- result: 'edcb5433' + args: [['% x', 3989525555]] +- result: '1234ABCD' + args: [['% X', 305441741]] +- result: 'EDCB5433' + args: [['% X', 3989525555]] +- result: 'x' + args: [['% c', ['x']]] + +# + flag + +- result: '+42' + args: [['%+d', 42]] +- result: '-42' + args: [['%+d', -42]] +- result: ' +42' + args: [['%+5d', 42]] +- result: ' -42' + args: [['%+5d', -42]] +- result: ' +42' + args: [['%+15d', 42]] +- result: ' -42' + args: [['%+15d', -42]] +- result: 'Hello testing' + args: [['%+s', 'Hello testing']] +- result: '+1024' + args: [['%+d', 1024]] +- result: '-1024' + args: [['%+d', -1024]] +- result: '+1024' + args: [['%+i', 1024]] +- result: '-1024' + args: [['%+i', -1024]] +- result: '1024' + args: [['%+u', 1024]] +- result: '4294966272' + args: [['%+u', 4294966272]] +- result: '777' + args: [['%+o', 511]] +- result: '37777777001' + args: [['%+o', 4294966785]] +- result: '1234abcd' + args: [['%+x', 305441741]] +- result: 'edcb5433' + args: [['%+x', 3989525555]] +- result: '1234ABCD' + args: [['%+X', 305441741]] +- result: 'EDCB5433' + args: [['%+X', 3989525555]] +- result: 'x' + args: [['%+c', ['x']]] +- result: '+' + args: [['%+.0d', 0]] + +# 0 flag + +- result: '42' + args: [['%0d', 42]] +- result: '42' + args: [['%0ld', 42]] +- result: '-42' + args: [['%0d', -42]] +- result: '00042' + args: [['%05d', 42]] +- result: '-0042' + args: [['%05d', -42]] +- result: '000000000000042' + args: [['%015d', 42]] +- result: '-00000000000042' + args: [['%015d', -42]] +- result: '000000000042.12' + args: [['%015.2f', 42.1234]] + float: true +- result: '00000000042.988' + args: [['%015.3f', 42.9876]] + float: true +- result: '-00000042.98760' + args: [['%015.5f', -42.9876]] + float: true + +# - flag + +- result: '42' + args: [['%-d', 42]] +- result: '-42' + args: [['%-d', -42]] +- result: '42 ' + args: [['%-5d', 42]] +- result: '-42 ' + args: [['%-5d', -42]] +- result: '42 ' + args: [['%-15d', 42]] +- result: '-42 ' + args: [['%-15d', -42]] +- result: '42' + args: [['%-0d', 42]] +- result: '-42' + args: [['%-0d', -42]] +- result: '42 ' + args: [['%-05d', 42]] +- result: '-42 ' + args: [['%-05d', -42]] +- result: '42 ' + args: [['%-015d', 42]] +- result: '-42 ' + args: [['%-015d', -42]] +- result: '42' + args: [['%0-d', 42]] +- result: '-42' + args: [['%0-d', -42]] +- result: '42 ' + args: [['%0-5d', 42]] +- result: '-42 ' + args: [['%0-5d', -42]] +- result: '42 ' + args: [['%0-15d', 42]] +- result: '-42 ' + args: [['%0-15d', -42]] +- result: '-4.200e+01 ' + args: [['%0-15.3e', -42.0]] + float: true +- result: '-42.0 ' + args: [['%0-15.3g', -42.0]] + float: true + +# # flag + +- result: '' + args: [['%#.0x', 0]] +- result: '0' + args: [['%#.1x', 0]] +- result: '' + args: [['%#.0llx', ['long long', 0]]] +- result: '0x0000614e' + args: [['%#.8x', 0x614e]] +- result: '0b110' + args: [['%#b', 6]] + +# specifier + +- result: 'Hello testing' + args: ['Hello testing'] +- result: 'Hello testing' + args: [['Hello testing%s', '']] +- result: 'Hello testing' + args: [['%s', 'Hello testing']] +- result: '1024' + args: [['%d', 1024]] +- result: '-1024' + args: [['%d', -1024]] +- result: '1024' + args: [['%i', 1024]] +- result: '-1024' + args: [['%i', -1024]] +- result: '1024' + args: [['%u', 1024]] +- result: '4294966272' + args: [['%u', 4294966272]] +- result: '777' + args: [['%o', 511]] +- result: '37777777001' + args: [['%o', 4294966785]] +- result: '1234abcd' + args: [['%x', 305441741]] +- result: 'edcb5433' + args: [['%x', 3989525555]] +- result: '1234ABCD' + args: [['%X', 305441741]] +- result: 'EDCB5433' + args: [['%X', 3989525555]] +- result: '%' + args: [['%%']] + +# width + +- result: 'Hello testing' + args: [['%1s', 'Hello testing']] +- result: '1024' + args: [['%1d', 1024]] +- result: '-1024' + args: [['%1d', -1024]] +- result: '1024' + args: [['%1i', 1024]] +- result: '-1024' + args: [['%1i', -1024]] +- result: '1024' + args: [['%1u', 1024]] +- result: '4294966272' + args: [['%1u', 4294966272]] +- result: '777' + args: [['%1o', 511]] +- result: '37777777001' + args: [['%1o', 4294966785]] +- result: '1234abcd' + args: [['%1x', 305441741]] +- result: 'edcb5433' + args: [['%1x', 3989525555]] +- result: '1234ABCD' + args: [['%1X', 305441741]] +- result: 'EDCB5433' + args: [['%1X', 3989525555]] +- result: 'x' + args: [['%1c', ['x']]] + +# width 20 + +- result: ' Hello testing' + args: [['%20s', 'Hello testing']] +- result: ' 1024' + args: [['%20d', 1024]] +- result: ' -1024' + args: [['%20d', -1024]] +- result: ' 1024' + args: [['%20i', 1024]] +- result: ' -1024' + args: [['%20i', -1024]] +- result: ' 1024' + args: [['%20u', 1024]] +- result: ' 4294966272' + args: [['%20u', 4294966272]] +- result: ' 777' + args: [['%20o', 511]] +- result: ' 37777777001' + args: [['%20o', 4294966785]] +- result: ' 1234abcd' + args: [['%20x', 305441741]] +- result: ' edcb5433' + args: [['%20x', 3989525555]] +- result: ' 1234ABCD' + args: [['%20X', 305441741]] +- result: ' EDCB5433' + args: [['%20X', 3989525555]] +- result: ' x' + args: [['%20c', ['x']]] + +# width *20 + +- result: ' Hello' + args: [['%*s', 20, 'Hello']] +- result: ' 1024' + args: [['%*d', 20, 1024]] +- result: ' -1024' + args: [['%*d', 20, -1024]] +- result: ' 1024' + args: [['%*i', 20, 1024]] +- result: ' -1024' + args: [['%*i', 20, -1024]] +- result: ' 1024' + args: [['%*u', 20, 1024]] +- result: ' 4294966272' + args: [['%*u', 20, 4294966272]] +- result: ' 777' + args: [['%*o', 20, 511]] +- result: ' 37777777001' + args: [['%*o', 20, 4294966785]] +- result: ' 1234abcd' + args: [['%*x', 20, 305441741]] +- result: ' edcb5433' + args: [['%*x', 20, 3989525555]] +- result: ' 1234ABCD' + args: [['%*X', 20, 305441741]] +- result: ' EDCB5433' + args: [['%*X', 20, 3989525555]] +- result: ' x' + args: [['%*c', 20, ['x']]] + +# width -20 + +- result: 'Hello ' + args: [['%-20s', 'Hello']] +- result: '1024 ' + args: [['%-20d', 1024]] +- result: '-1024 ' + args: [['%-20d', -1024]] +- result: '1024 ' + args: [['%-20i', 1024]] +- result: '-1024 ' + args: [['%-20i', -1024]] +- result: '1024 ' + args: [['%-20u', 1024]] +- result: '1024.1234 ' + args: [['%-20.4f', 1024.1234]] + float: true +- result: '4294966272 ' + args: [['%-20u', 4294966272]] +- result: '777 ' + args: [['%-20o', 511]] +- result: '37777777001 ' + args: [['%-20o', 4294966785]] +- result: '1234abcd ' + args: [['%-20x', 305441741]] +- result: 'edcb5433 ' + args: [['%-20x', 3989525555]] +- result: '1234ABCD ' + args: [['%-20X', 305441741]] +- result: 'EDCB5433 ' + args: [['%-20X', 3989525555]] +- result: 'x ' + args: [['%-20c', ['x']]] +- result: '| 9| |9 | | 9|' + args: [['|%5d| ', 9], ['|%-2d|', 9], [' |%5d|', 9]] +- result: '| 10| |10| | 10|' + args: [['|%5d| ', 10], ['|%-2d|', 10], [' |%5d|', 10]] +- result: '| 9| |9 | | 9|' + args: [['|%5d| ', 9], ['|%-12d|', 9], [' |%5d|', 9]] +- result: '| 10| |10 | | 10|' + args: [['|%5d| ', 10], ['|%-12d|', 10], [' |%5d|', 10]] + +# width 0-20 + +- result: 'Hello ' + args: [['%0-20s', 'Hello']] +- result: '1024 ' + args: [['%0-20d', 1024]] +- result: '-1024 ' + args: [['%0-20d', -1024]] +- result: '1024 ' + args: [['%0-20i', 1024]] +- result: '-1024 ' + args: [['%0-20i', -1024]] +- result: '1024 ' + args: [['%0-20u', 1024]] +- result: '4294966272 ' + args: [['%0-20u', 4294966272]] +- result: '777 ' + args: [['%0-20o', 511]] +- result: '37777777001 ' + args: [['%0-20o', 4294966785]] +- result: '1234abcd ' + args: [['%0-20x', 305441741]] +- result: 'edcb5433 ' + args: [['%0-20x', 3989525555]] +- result: '1234ABCD ' + args: [['%0-20X', 305441741]] +- result: 'EDCB5433 ' + args: [['%0-20X', 3989525555]] +- result: 'x ' + args: [['%0-20c', ['x']]] + +# padding 20 + +- result: '00000000000000001024' + args: [['%020d', 1024]] +- result: '-0000000000000001024' + args: [['%020d', -1024]] +- result: '00000000000000001024' + args: [['%020i', 1024]] +- result: '-0000000000000001024' + args: [['%020i', -1024]] +- result: '00000000000000001024' + args: [['%020u', 1024]] +- result: '00000000004294966272' + args: [['%020u', 4294966272]] +- result: '00000000000000000777' + args: [['%020o', 511]] +- result: '00000000037777777001' + args: [['%020o', 4294966785]] +- result: '0000000000001234abcd' + args: [['%020x', 305441741]] +- result: '000000000000edcb5433' + args: [['%020x', 3989525555]] +- result: '0000000000001234ABCD' + args: [['%020X', 305441741]] +- result: '000000000000EDCB5433' + args: [['%020X', 3989525555]] + +# padding .20 + +- result: '00000000000000001024' + args: [['%.20d', 1024]] +- result: '-00000000000000001024' + args: [['%.20d', -1024]] +- result: '00000000000000001024' + args: [['%.20i', 1024]] +- result: '-00000000000000001024' + args: [['%.20i', -1024]] +- result: '00000000000000001024' + args: [['%.20u', 1024]] +- result: '00000000004294966272' + args: [['%.20u', 4294966272]] +- result: '00000000000000000777' + args: [['%.20o', 511]] +- result: '00000000037777777001' + args: [['%.20o', 4294966785]] +- result: '0000000000001234abcd' + args: [['%.20x', 305441741]] +- result: '000000000000edcb5433' + args: [['%.20x', 3989525555]] +- result: '0000000000001234ABCD' + args: [['%.20X', 305441741]] +- result: '000000000000EDCB5433' + args: [['%.20X', 3989525555]] + +# padding #020 + +- result: '00000000000000001024' + args: [['%#020d', 1024]] +- result: '-0000000000000001024' + args: [['%#020d', -1024]] +- result: '00000000000000001024' + args: [['%#020i', 1024]] +- result: '-0000000000000001024' + args: [['%#020i', -1024]] +- result: '00000000000000001024' + args: [['%#020u', 1024]] +- result: '00000000004294966272' + args: [['%#020u', 4294966272]] +- result: '00000000000000000777' + args: [['%#020o', 511]] +- result: '00000000037777777001' + args: [['%#020o', 4294966785]] +- result: '0x00000000001234abcd' + args: [['%#020x', 305441741]] +- result: '0x0000000000edcb5433' + args: [['%#020x', 3989525555]] +- result: '0X00000000001234ABCD' + args: [['%#020X', 305441741]] +- result: '0X0000000000EDCB5433' + args: [['%#020X', 3989525555]] + +# padding #20 + +- result: ' 1024' + args: [['%#20d', 1024]] +- result: ' -1024' + args: [['%#20d', -1024]] +- result: ' 1024' + args: [['%#20i', 1024]] +- result: ' -1024' + args: [['%#20i', -1024]] +- result: ' 1024' + args: [['%#20u', 1024]] +- result: ' 4294966272' + args: [['%#20u', 4294966272]] +- result: ' 0777' + args: [['%#20o', 511]] +- result: ' 037777777001' + args: [['%#20o', 4294966785]] +- result: ' 0x1234abcd' + args: [['%#20x', 305441741]] +- result: ' 0xedcb5433' + args: [['%#20x', 3989525555]] +- result: ' 0X1234ABCD' + args: [['%#20X', 305441741]] +- result: ' 0XEDCB5433' + args: [['%#20X', 3989525555]] + +# padding 20.5 + +- result: ' 01024' + args: [['%20.5d', 1024]] +- result: ' -01024' + args: [['%20.5d', -1024]] +- result: ' 01024' + args: [['%20.5i', 1024]] +- result: ' -01024' + args: [['%20.5i', -1024]] +- result: ' 01024' + args: [['%20.5u', 1024]] +- result: ' 4294966272' + args: [['%20.5u', 4294966272]] +- result: ' 00777' + args: [['%20.5o', 511]] +- result: ' 37777777001' + args: [['%20.5o', 4294966785]] +- result: ' 1234abcd' + args: [['%20.5x', 305441741]] +- result: ' 00edcb5433' + args: [['%20.10x', 3989525555]] +- result: ' 1234ABCD' + args: [['%20.5X', 305441741]] +- result: ' 00EDCB5433' + args: [['%20.10X', 3989525555]] + +# padding neg numbers + +# space padding +- result: '-5' + args: [['% 1d', -5]] +- result: '-5' + args: [['% 2d', -5]] +- result: ' -5' + args: [['% 3d', -5]] +- result: ' -5' + args: [['% 4d', -5]] +# zero padding +- result: '-5' + args: [['%01d', -5]] +- result: '-5' + args: [['%02d', -5]] +- result: '-05' + args: [['%03d', -5]] +- result: '-005' + args: [['%04d', -5]] + +# float padding neg numbers + +# space padding +- result: '-5.0' + args: [['% 3.1f', -5.0]] + float: true +- result: '-5.0' + args: [['% 4.1f', -5.0]] + float: true +- result: ' -5.0' + args: [['% 5.1f', -5.0]] + float: true +- result: ' -5' + args: [['% 6.1g', -5.0]] + float: true +- result: '-5.0e+00' + args: [['% 6.1e', -5.0]] + float: true +- result: ' -5.0e+00' + args: [['% 10.1e', -5.0]] + float: true +# zero padding +- result: '-5.0' + args: [['%03.1f', -5.0]] + float: true +- result: '-5.0' + args: [['%04.1f', -5.0]] + float: true +- result: '-05.0' + args: [['%05.1f', -5.0]] + float: true +# zero padding no decimal point +- result: '-5' + args: [['%01.0f', -5.0]] + float: true +- result: '-5' + args: [['%02.0f', -5.0]] + float: true +- result: '-05' + args: [['%03.0f', -5.0]] + float: true +- result: '-005.0e+00' + args: [['%010.1e', -5.0]] + float: true +- result: '-05E+00' + args: [['%07.0E', -5.0]] + float: true +- result: '-05' + args: [['%03.0g', -5.0]] + float: true + +# length + +- result: '' + args: [['%.0s', 'Hello testing']] +- result: ' ' + args: [['%20.0s', 'Hello testing']] +- result: '' + args: [['%.s', 'Hello testing']] +- result: ' ' + args: [['%20.s', 'Hello testing']] +- result: ' 1024' + args: [['%20.0d', 1024]] +- result: ' -1024' + args: [['%20.0d', -1024]] +- result: ' ' + args: [['%20.d', 0]] +- result: ' 1024' + args: [['%20.0i', 1024]] +- result: ' -1024' + args: [['%20.i', -1024]] +- result: ' ' + args: [['%20.i', 0]] +- result: ' 1024' + args: [['%20.u', 1024]] +- result: ' 4294966272' + args: [['%20.0u', 4294966272]] +- result: ' ' + args: [['%20.u', 0]] +- result: ' 777' + args: [['%20.o', 511]] +- result: ' 37777777001' + args: [['%20.0o', 4294966785]] +- result: ' ' + args: [['%20.o', 0]] +- result: ' 1234abcd' + args: [['%20.x', 305441741]] +- result: ' 1234abcd' + args: [['%50.x', 305441741]] +- result: ' 1234abcd 12345' + args: [['%50.x', 305441741], ['%10.u', 12345]] +- result: ' edcb5433' + args: [['%20.0x', 3989525555]] +- result: ' ' + args: [['%20.x', 0]] +- result: ' 1234ABCD' + args: [['%20.X', 305441741]] +- result: ' EDCB5433' + args: [['%20.0X', 3989525555]] +- result: ' ' + args: [['%20.X', 0]] +- result: ' ' + args: [['%02.0u', 0]] +- result: ' ' + args: [['%02.0d', 0]] diff --git a/include/.gitignore b/include/.gitignore new file mode 100644 index 0000000..5441d2a --- /dev/null +++ b/include/.gitignore @@ -0,0 +1 @@ +/kernaux/version.h diff --git a/include/Makefile.am b/include/Makefile.am new file mode 100644 index 0000000..9a4f2da --- /dev/null +++ b/include/Makefile.am @@ -0,0 +1,39 @@ +nobase_include_HEADERS = \ + kernaux.h \ + kernaux/arch/i386.h \ + kernaux/arch/i386-idt.h \ + kernaux/arch/riscv64.h \ + kernaux/arch/x86_64.h \ + kernaux/arch/x86.h \ + kernaux/asm/i386.h \ + kernaux/asm/riscv64.h \ + kernaux/asm/x86_64.h \ + kernaux/asm/x86.h \ + kernaux/cmdline.h \ + kernaux/elf.h \ + kernaux/free_list.h \ + kernaux/generic/display.h \ + kernaux/generic/malloc.h \ + kernaux/generic/mutex.h \ + kernaux/macro.h \ + kernaux/macro/packing_end.run \ + kernaux/macro/packing_start.run \ + kernaux/mbr.h \ + kernaux/memmap.h \ + kernaux/multiboot2.h \ + kernaux/multiboot2/header_enums.h \ + kernaux/multiboot2/header_helpers.h \ + kernaux/multiboot2/header_is_valid.h \ + kernaux/multiboot2/header_macro.h \ + kernaux/multiboot2/header_print.h \ + kernaux/multiboot2/info_enums.h \ + kernaux/multiboot2/info_helpers.h \ + kernaux/multiboot2/info_is_valid.h \ + kernaux/multiboot2/info_print.h \ + kernaux/ntoa.h \ + kernaux/pfa.h \ + kernaux/printf.h \ + kernaux/printf_fmt.h \ + kernaux/runtime.h \ + kernaux/units.h \ + kernaux/version.h diff --git a/include/kernaux.h b/include/kernaux.h new file mode 100644 index 0000000..7d3888d --- /dev/null +++ b/include/kernaux.h @@ -0,0 +1,25 @@ +/* + We don't include because they + contain architecture-specific assembly functions. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/include/kernaux/arch/i386-idt.h b/include/kernaux/arch/i386-idt.h new file mode 100644 index 0000000..d9d674e --- /dev/null +++ b/include/kernaux/arch/i386-idt.h @@ -0,0 +1,62 @@ +#ifndef KERNAUX_INCLUDED_ARCH_I386_IDT +#define KERNAUX_INCLUDED_ARCH_I386_IDT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + +/** + * @brief Interrupt Descriptor Table entry + * + * @see https://en.wikibooks.org/wiki/X86_Assembly/Advanced_Interrupts#The_Interrupt_Descriptor_Table + */ +typedef struct KernAux_Arch_I386_IDTE { + uint16_t offset_low; + uint16_t selector; + uint8_t _; + uint8_t flags; + uint16_t offset_high; +} +KERNAUX_PACKED +KERNAUX_ALIGNED(8) +*KernAux_Arch_I386_IDTE; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_IDTE, 8); + +#include + +void KernAux_Arch_I386_IDTE_init_intr( + KernAux_Arch_I386_IDTE idte, + uint32_t offset, + uint16_t cs_selector, + uint8_t dpl +); +void KernAux_Arch_I386_IDTE_init_task( + KernAux_Arch_I386_IDTE idte, + uint16_t tss_selector, + uint8_t dpl +); +void KernAux_Arch_I386_IDTE_init_trap( + KernAux_Arch_I386_IDTE idte, + uint32_t offset, + uint16_t cs_selector, + uint8_t dpl +); + +uint32_t KernAux_Arch_I386_IDTE_offset(KernAux_Arch_I386_IDTE idte); +uint8_t KernAux_Arch_I386_IDTE_dpl (KernAux_Arch_I386_IDTE idte); + +void +KernAux_Arch_I386_IDTE_set_offset(KernAux_Arch_I386_IDTE idte, uint32_t offset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/arch/i386.h b/include/kernaux/arch/i386.h new file mode 100644 index 0000000..dc507a0 --- /dev/null +++ b/include/kernaux/arch/i386.h @@ -0,0 +1,217 @@ +#ifndef KERNAUX_INCLUDED_ARCH_I386 +#define KERNAUX_INCLUDED_ARCH_I386 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include + +#define KERNAUX_ARCH_I386_PAGE_SIZE (1024 * 4) // 4 KiB +#define KERNAUX_ARCH_I386_PAGE_BIG_SIZE (1024 * 1024 * 4) // 4 MiB + +#define KERNAUX_ARCH_I386_PAGE_DIR_ENTRIES_COUNT 1024 +#define KERNAUX_ARCH_I386_PAGE_TABLE_ENTRIES_COUNT 1024 + +#define KERNAUX_ARCH_I386_PAGES_COUNT_MAX (1024 * 1024) + +#define KERNAUX_ARCH_I386_ADDR_TO_PDE_INDEX(addr) \ + ((((uint32_t)addr) & 0xffffffff) >> 22) +#define KERNAUX_ARCH_I386_ADDR_TO_PTE_INDEX(addr) \ + (((((uint32_t)addr) & 0xffffffff) >> 12) & 0x3ff) + +#define KERNAUX_ARCH_I386_ADDR_TO_PDE_ADDR(addr) \ + ((((uint32_t)addr) & 0xffffffff) >> 12) +#define KERNAUX_ARCH_I386_ADDR_TO_PTE_ADDR(addr) \ + KERNAUX_ARCH_I386_ADDR_TO_PDE_ADDR(addr) + +#include + +/** + * @brief CR0 bits + * + * @details + * Contains system control flags that control + * operating mode and states of the processor. + * + * @see https://en.wikipedia.org/wiki/Control_register#CR0 + * @see https://wiki.osdev.org/CPU_Registers_x86#CR0 + */ +KERNAUX_ARCH_X86_DEFINE_CR0(I386, uint32_t); +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_I386_CR0, 4); + +/** + * @brief CR4 bits + * + * @details + * Contains a group of flags that enable several architectural extensions, + * and indicate operating system or executive support for specific processor + * capabilities. + * + * @see https://en.wikipedia.org/wiki/Control_register#CR4 + * @see https://wiki.osdev.org/CPU_Registers_x86#CR4 + */ +KERNAUX_ARCH_X86_DEFINE_CR4(I386, uint32_t); +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_I386_CR4, 4); + +// Global, local or interrupt descriptor table register +// TODO: validate this according to spec +struct KernAux_Arch_I386_DTR { + uint16_t size; + uint32_t offset; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_DTR, 6); + +// Global or local descriptor table entry +// TODO: validate this according to spec +struct KernAux_Arch_I386_DTE { + uint16_t limit_low; + unsigned base_low : 24; + unsigned accessed : 1; + unsigned read_write : 1; + unsigned conforming_expand_down : 1; + unsigned code : 1; + unsigned always_1 : 1; + unsigned DPL : 2; + unsigned present : 1; + unsigned limit_high : 4; + unsigned available : 1; + unsigned always_0 : 1; + unsigned big : 1; + unsigned gran : 1; + uint8_t base_high; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_DTE, 8); + +/** + * @brief Task state segment + * @see The manual, page 132, figure 7-1 + */ +struct KernAux_Arch_I386_TSS { + // 0x00 + uint16_t prev_tss; + uint16_t _zero0; + // 0x04 + uint32_t esp0; + uint16_t ss0; + uint16_t _zero1; + uint32_t esp1; + uint16_t ss1; + uint16_t _zero2; + uint32_t esp2; + uint16_t ss2; + uint16_t _zero3; + // 0x1c + uint32_t cr3; + uint32_t eip; + uint32_t eflags; + uint32_t eax; + uint32_t ecx; + uint32_t edx; + uint32_t ebx; + uint32_t esp; + uint32_t ebp; + uint32_t esi; + uint32_t edi; + // 0x48 + uint16_t es; + uint16_t _zero4; + uint16_t cs; + uint16_t _zero5; + uint16_t ss; + uint16_t _zero6; + uint16_t ds; + uint16_t _zero7; + uint16_t fs; + uint16_t _zero8; + uint16_t gs; + uint16_t _zero9; + uint16_t ldt; + uint16_t _zero10; + // 0x64 + uint16_t _zero11; + uint16_t io_map_base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_TSS, 104); + +// Page directory entry +// TODO: validate this according to spec +union KernAux_Arch_I386_PDE { + uint32_t number; +#ifdef KERNAUX_BITFIELDS + struct { + unsigned present : 1; + unsigned writable : 1; + unsigned user : 1; + unsigned write_through : 1; + unsigned cache_disabled : 1; + unsigned accessed : 1; + unsigned available0 : 1; + unsigned page_size : 1; + unsigned available1 : 4; + unsigned addr : 20; + } KERNAUX_PACKED bitfields; +#endif +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_I386_PDE, 4); + +// Page table entry +// TODO: validate this according to spec +union KernAux_Arch_I386_PTE { + uint32_t number; +#ifdef KERNAUX_BITFIELDS + struct { + unsigned present : 1; + unsigned writable : 1; + unsigned user : 1; + unsigned write_through : 1; + unsigned cache_disabled : 1; + unsigned accessed : 1; + unsigned dirty : 1; + unsigned attr_table : 1; + unsigned global : 1; + unsigned available : 3; + unsigned addr : 20; + } KERNAUX_PACKED bitfields; +#endif +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_I386_PTE, 4); + +// Page directory +struct KernAux_Arch_I386_PageDir { + union KernAux_Arch_I386_PDE pdes[KERNAUX_ARCH_I386_PAGE_DIR_ENTRIES_COUNT]; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_PageDir, KERNAUX_ARCH_I386_PAGE_SIZE); + +// Page table +struct KernAux_Arch_I386_PageTable { + union KernAux_Arch_I386_PTE ptes[KERNAUX_ARCH_I386_PAGE_TABLE_ENTRIES_COUNT]; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Arch_I386_PageTable, KERNAUX_ARCH_I386_PAGE_SIZE); + +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/arch/riscv64.h b/include/kernaux/arch/riscv64.h new file mode 100644 index 0000000..ff5bfb9 --- /dev/null +++ b/include/kernaux/arch/riscv64.h @@ -0,0 +1,12 @@ +#ifndef KERNAUX_INCLUDED_ARCH_RISCV64 +#define KERNAUX_INCLUDED_ARCH_RISCV64 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/arch/x86.h b/include/kernaux/arch/x86.h new file mode 100644 index 0000000..9404a6d --- /dev/null +++ b/include/kernaux/arch/x86.h @@ -0,0 +1,113 @@ +#ifndef KERNAUX_INCLUDED_ARCH_X86 +#define KERNAUX_INCLUDED_ARCH_X86 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#ifndef KERNAUX_BITFIELDS +#define KERNAUX_ARCH_X86_DEFINE_CR0(arch, number_type) \ + union KernAux_Arch_##arch##_CR0 { number_type number; } KERNAUX_PACKED +#else +#define KERNAUX_ARCH_X86_DEFINE_CR0(arch, number_type) \ + union KernAux_Arch_##arch##_CR0 { \ + number_type number; \ + struct { \ + bool pe : 1; /* 0: Protection Enable */ \ + bool mp : 1; /* 1: Monitor Coprocessor */ \ + bool em : 1; /* 2: Emulation (x87 FPU) */ \ + bool ts : 1; /* 3: Task Switched */ \ + bool et : 1; /* 4: Extension Type */ \ + bool ne : 1; /* 5: Numeric Error */ \ + unsigned _0 : 10; \ + bool wp : 1; /* 16: Write Protect */ \ + unsigned _1 : 1; \ + bool am : 1; /* 18: Alignment Mask */ \ + unsigned _2 : 10; \ + bool nw : 1; /* 29: Not Write-trough */ \ + bool cd : 1; /* 30: Cache Disable */ \ + bool pg : 1; /* 31: Paging */ \ + } KERNAUX_PACKED bitfields; \ + } KERNAUX_PACKED +#endif + +#define KERNAUX_ARCH_X86_CR0_PE KERNAUX_BITS32(0) // Protection Enable +#define KERNAUX_ARCH_X86_CR0_MP KERNAUX_BITS32(1) // Monitor Coprocessor +#define KERNAUX_ARCH_X86_CR0_EM KERNAUX_BITS32(2) // Emulation (x87 FPU) +#define KERNAUX_ARCH_X86_CR0_TS KERNAUX_BITS32(3) // Task Switched +#define KERNAUX_ARCH_X86_CR0_ET KERNAUX_BITS32(4) // Extension Type +#define KERNAUX_ARCH_X86_CR0_NE KERNAUX_BITS32(5) // Numeric Error +#define KERNAUX_ARCH_X86_CR0_WP KERNAUX_BITS32(16) // Write Protect +#define KERNAUX_ARCH_X86_CR0_AM KERNAUX_BITS32(18) // Alignment Mask +#define KERNAUX_ARCH_X86_CR0_NW KERNAUX_BITS32(29) // Not Write-trough +#define KERNAUX_ARCH_X86_CR0_CD KERNAUX_BITS32(30) // Cache Disable +#define KERNAUX_ARCH_X86_CR0_PG KERNAUX_BITS32(31) // Paging + +#ifndef KERNAUX_BITFIELDS +#define KERNAUX_ARCH_X86_DEFINE_CR4(arch, number_type) \ + union KernAux_Arch_##arch##_CR4 { number_type number; } KERNAUX_PACKED +#else +#define KERNAUX_ARCH_X86_DEFINE_CR4(arch, number_type) \ + union KernAux_Arch_##arch##_CR4 { \ + number_type number; \ + struct { \ + bool vme : 1; /* 0: Virtual-8086 Mode Extensions */ \ + bool pvi : 1; /* 1: Protected-Mode Virtual Interrupts */ \ + bool tsd : 1; /* 2: Time Stamp Disable */ \ + bool de : 1; /* 3: Debugging Extensions */ \ + bool pse : 1; /* 4: Page Size Extension */ \ + bool pae : 1; /* 5: Physical Address Extension */ \ + bool mce : 1; /* 6: Machine-Check Exception */ \ + bool pge : 1; /* 7: Page Global Enable */ \ + bool pce : 1; /* 8: Performance-Monitoring Counter Enabled */ \ + bool osfxsr : 1; /* 9: Operating System Support for */ \ + /* FXSAVE and FXRSTOR instructions */ \ + bool osxmmexcpt : 1; /* 10: Operating System Support for */ \ + /* Unmasked SIMD Floating-Point Exceptions */ \ + bool umip : 1; /* 11: User-Mode Instruction Prevention */ \ + unsigned _0 : 1; \ + bool vmxe : 1; /* 13: VME (Virtual Machine Extensions) Enable */ \ + bool smxe : 1; /* 14: SME (Safer Mode Extensions) Enable */ \ + unsigned _1 : 1; \ + bool fsgsbase : 1; /* 16: FSGSBASE Enable */ \ + bool pcide : 1; /* 17: PCID Enable */ \ + bool osxsave : 1; /* 18: XSAVE and Processor Extended States Enable */ \ + unsigned _2 : 1; \ + bool smep : 1; /* 20: SMEP (Supervisor Mode Execution Protection) Enable */ \ + bool smap : 1; /* 21: SMAP (Supervisor Mode Access Prevention) Enable */ \ + bool pke : 1; /* 22: Protection Key Enable */ \ + unsigned _3 : 9; \ + } KERNAUX_PACKED bitfields; \ + } KERNAUX_PACKED +#endif + +#define KERNAUX_ARCH_X86_CR4_VME KERNAUX_BITS32(0) // Virtual-8086 Mode Extensions +#define KERNAUX_ARCH_X86_CR4_PVI KERNAUX_BITS32(1) // Protected-Mode Virtual Interrupts +#define KERNAUX_ARCH_X86_CR4_TSD KERNAUX_BITS32(2) // Time Stamp Disable +#define KERNAUX_ARCH_X86_CR4_DE KERNAUX_BITS32(3) // Debugging Extensions +#define KERNAUX_ARCH_X86_CR4_PSE KERNAUX_BITS32(4) // Page Size Extension +#define KERNAUX_ARCH_X86_CR4_PAE KERNAUX_BITS32(5) // Physical Address Extension +#define KERNAUX_ARCH_X86_CR4_MCE KERNAUX_BITS32(6) // Machine-Check Exception +#define KERNAUX_ARCH_X86_CR4_PGE KERNAUX_BITS32(7) // Page Global Enable +#define KERNAUX_ARCH_X86_CR4_PCE KERNAUX_BITS32(8) // Performance-Monitoring Counter Enabled +#define KERNAUX_ARCH_X86_CR4_OSFXSR KERNAUX_BITS32(9) // Operating System Support for FXSAVE and FXRSTOR instructions +#define KERNAUX_ARCH_X86_CR4_OSXMMEXCPT KERNAUX_BITS32(10) // Operating System Support for Unmasked SIMD Floating-Point Exceptions +#define KERNAUX_ARCH_X86_CR4_UMIP KERNAUX_BITS32(11) // User-Mode Instruction Prevention +#define KERNAUX_ARCH_X86_CR4_VMXE KERNAUX_BITS32(13) // VME (Virtual Machine Extensions) Enable +#define KERNAUX_ARCH_X86_CR4_SMXE KERNAUX_BITS32(14) // SME (Safer Mode Extensions) Enable +#define KERNAUX_ARCH_X86_CR4_FSGSBASE KERNAUX_BITS32(16) // FSGSBASE Enable (enable the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE) +#define KERNAUX_ARCH_X86_CR4_PCIDE KERNAUX_BITS32(17) // PCID Enable +#define KERNAUX_ARCH_X86_CR4_OSXSAVE KERNAUX_BITS32(18) // XSAVE and Processor Extended States Enable +#define KERNAUX_ARCH_X86_CR4_SMEP KERNAUX_BITS32(20) // SMEP (Supervisor Mode Execution Protection) Enable +#define KERNAUX_ARCH_X86_CR4_SMAP KERNAUX_BITS32(21) // SMAP (Supervisor Mode Access Prevention) Enable +#define KERNAUX_ARCH_X86_CR4_PKE KERNAUX_BITS32(22) // Protection Key Enable + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/arch/x86_64.h b/include/kernaux/arch/x86_64.h new file mode 100644 index 0000000..ff9f420 --- /dev/null +++ b/include/kernaux/arch/x86_64.h @@ -0,0 +1,41 @@ +#ifndef KERNAUX_INCLUDED_ARCH_X86_64 +#define KERNAUX_INCLUDED_ARCH_X86_64 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @brief CR0 bits + * + * @details + * Contains system control flags that control + * operating mode and states of the processor. + * + * @see https://en.wikipedia.org/wiki/Control_register#CR0 + * @see https://wiki.osdev.org/CPU_Registers_x86#CR0 + */ +KERNAUX_ARCH_X86_DEFINE_CR0(X86_64, uint64_t); +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_X86_64_CR0, 8); + +/** + * @brief CR4 bits + * + * @details + * Contains a group of flags that enable several architectural extensions, + * and indicate operating system or executive support for specific processor + * capabilities. + * + * @see https://en.wikipedia.org/wiki/Control_register#CR4 + * @see https://wiki.osdev.org/CPU_Registers_x86#CR4 + */ +KERNAUX_ARCH_X86_DEFINE_CR4(X86_64, uint64_t); +KERNAUX_STATIC_TEST_UNION_SIZE(KernAux_Arch_X86_64_CR4, 8); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/asm/i386.h b/include/kernaux/asm/i386.h new file mode 100644 index 0000000..a4a46fc --- /dev/null +++ b/include/kernaux/asm/i386.h @@ -0,0 +1,30 @@ +#ifndef KERNAUX_INCLUDED_ASM_I386 +#define KERNAUX_INCLUDED_ASM_I386 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void kernaux_asm_i386_flush_gdt( + volatile uint32_t pointer, + volatile uint32_t data_selector, + volatile uint32_t code_selector +); +void kernaux_asm_i386_flush_idt(volatile uint32_t pointer); +void kernaux_asm_i386_flush_tss(volatile uint16_t selector); + +uint32_t kernaux_asm_i386_read_cr0(); +uint32_t kernaux_asm_i386_read_cr4(); + +void kernaux_asm_i386_write_cr0(volatile uint32_t value); +void kernaux_asm_i386_write_cr3(volatile uint32_t value); +void kernaux_asm_i386_write_cr4(volatile uint32_t value); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/asm/riscv64.h b/include/kernaux/asm/riscv64.h new file mode 100644 index 0000000..4ddaf89 --- /dev/null +++ b/include/kernaux/asm/riscv64.h @@ -0,0 +1,14 @@ +#ifndef KERNAUX_INCLUDED_ASM_RISCV64 +#define KERNAUX_INCLUDED_ASM_RISCV64 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/asm/x86.h b/include/kernaux/asm/x86.h new file mode 100644 index 0000000..f7365e7 --- /dev/null +++ b/include/kernaux/asm/x86.h @@ -0,0 +1,61 @@ +#ifndef KERNAUX_INCLUDED_ASM_X86 +#define KERNAUX_INCLUDED_ASM_X86 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +inline static uint8_t kernaux_asm_x86_inportb(uint16_t port); +inline static uint16_t kernaux_asm_x86_inportw(uint16_t port); +inline static uint32_t kernaux_asm_x86_inportd(uint16_t port); + +inline static void kernaux_asm_x86_outportb(uint16_t port, uint8_t value); +inline static void kernaux_asm_x86_outportw(uint16_t port, uint16_t value); +inline static void kernaux_asm_x86_outportd(uint16_t port, uint32_t value); + +uint8_t kernaux_asm_x86_inportb(const uint16_t port) +{ + register uint8_t result; + KERNAUX_ASM("inb %1, %0" : "=a" (result) : "dN" (port)); + return result; +} + +uint16_t kernaux_asm_x86_inportw(const uint16_t port) +{ + register uint16_t result; + KERNAUX_ASM("inw %1, %0" : "=a" (result) : "dN" (port)); + return result; +} + +uint32_t kernaux_asm_x86_inportd(const uint16_t port) +{ + register uint32_t result; + KERNAUX_ASM("inl %1, %0" : "=a" (result) : "dN" (port)); + return result; +} + +void kernaux_asm_x86_outportb(const uint16_t port, const uint8_t value) +{ + KERNAUX_ASM("outb %1, %0" : : "dN" (port), "a" (value)); +} + +void kernaux_asm_x86_outportw(const uint16_t port, const uint16_t value) +{ + KERNAUX_ASM("outw %1, %0" : : "dN" (port), "a" (value)); +} + +void kernaux_asm_x86_outportd(const uint16_t port, const uint32_t value) +{ + KERNAUX_ASM("outl %1, %0" : : "dN" (port), "a" (value)); +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/asm/x86_64.h b/include/kernaux/asm/x86_64.h new file mode 100644 index 0000000..1220f8b --- /dev/null +++ b/include/kernaux/asm/x86_64.h @@ -0,0 +1,15 @@ +#ifndef KERNAUX_INCLUDED_ASM_X86_64 +#define KERNAUX_INCLUDED_ASM_X86_64 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/cmdline.h b/include/kernaux/cmdline.h new file mode 100644 index 0000000..57149e0 --- /dev/null +++ b/include/kernaux/cmdline.h @@ -0,0 +1,29 @@ +#ifndef KERNAUX_INCLUDED_CMDLINE +#define KERNAUX_INCLUDED_CMDLINE + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX 256 +#define KERNAUX_CMDLINE_ERROR_MSG_SLEN_MAX \ + (KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX - 1) + +bool kernaux_cmdline( + const char *cmdline, + char *error_msg, + size_t *argc, + char **argv, + char *buffer, + size_t arg_count_max, + size_t buffer_size +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/elf.h b/include/kernaux/elf.h new file mode 100644 index 0000000..793cdc3 --- /dev/null +++ b/include/kernaux/elf.h @@ -0,0 +1,181 @@ +#ifndef KERNAUX_INCLUDED_ELF +#define KERNAUX_INCLUDED_ELF + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#include + +struct KernAux_ELF_Ident { + uint8_t magic_0x7f; + uint8_t magic_E; + uint8_t magic_L; + uint8_t magic_F; + uint8_t class_; + uint8_t data; + uint8_t version; + uint8_t unused[9]; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_ELF_Ident, 16); + +// KernAux_ELF_Ident.class_ +#define KERNAUX_ELF_CLASS_NONE 0 // Invalid class +#define KERNAUX_ELF_CLASS_32 1 // 32-bit objects +#define KERNAUX_ELF_CLASS_64 2 // 64-bit objects + +// KernAux_ELF_Ident.data +#define KERNAUX_ELF_DATA_NONE 0 // Invalid data encoding +#define KERNAUX_ELF_DATA_2LSB 1 // 0x01020304 == [0x04, 0x03, 0x02, 0x01] +#define KERNAUX_ELF_DATA_2MSB 2 // 0x01020304 == [0x01, 0x02, 0x03, 0x04] + +struct KernAux_ELF_Header { + struct KernAux_ELF_Ident ident; + uint16_t type; + uint16_t machine; + uint32_t version; + uint32_t entry; + uint32_t phoff; + uint32_t shoff; + uint32_t flags; + uint16_t ehsize; + uint16_t phentsize; + uint16_t phnum; + uint16_t shentsize; + uint16_t shnum; + uint16_t shstrndx; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_ELF_Header, 52); + +// KernAux_ELF_Header.type +#define KERNAUX_ELF_TYPE_NONE 0 // No file type +#define KERNAUX_ELF_TYPE_REL 1 // Relocatable file +#define KERNAUX_ELF_TYPE_EXEC 2 // Executable file +#define KERNAUX_ELF_TYPE_DYN 3 // Shared object file +#define KERNAUX_ELF_TYPE_CORE 4 // Core file +#define KERNAUX_ELF_TYPE_LOPROC 0xff00 // Processor-specific +#define KERNAUX_ELF_TYPE_HIPROC 0xffff // Processor-specific + +// KernAux_ELF_Header.machine +#define KERNAUX_ELF_MACHINE_NONE 0 // No machine +#define KERNAUX_ELF_MACHINE_M32 1 // AT&T WE 32100 +#define KERNAUX_ELF_MACHINE_SPARC 2 // SPARC +#define KERNAUX_ELF_MACHINE_386 3 // Intel 80386 +#define KERNAUX_ELF_MACHINE_68K 4 // Motorola 68000 +#define KERNAUX_ELF_MACHINE_88K 5 // Motorola 88000 +#define KERNAUX_ELF_MACHINE_860 7 // Intel 80860 +#define KERNAUX_ELF_MACHINE_MIPS 8 // MIPS RS3000 + +// KernAux_ELF_Header.version +#define KERNAUX_ELF_VERSION_NONE 0 // Invalid version +#define KERNAUX_ELF_VERSION_CURRENT 1 // Current version + +struct KernAux_ELF_Section { + uint32_t name; + uint32_t type; + uint32_t flags; + uint32_t addr; + uint32_t offset; + uint32_t size; + uint32_t link; + uint32_t info; + uint32_t addralign; + uint32_t entsize; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_ELF_Section, 40); + +#define KERNAUX_ELF_SECT_TYPE_NULL 0 +#define KERNAUX_ELF_SECT_TYPE_PROGBITS 1 +#define KERNAUX_ELF_SECT_TYPE_SYMTAB 2 +#define KERNAUX_ELF_SECT_TYPE_STRTAB 3 +#define KERNAUX_ELF_SECT_TYPE_RELA 4 +#define KERNAUX_ELF_SECT_TYPE_HASH 5 +#define KERNAUX_ELF_SECT_TYPE_DYNAMIC 6 +#define KERNAUX_ELF_SECT_TYPE_NOTE 7 +#define KERNAUX_ELF_SECT_TYPE_NOBITS 8 +#define KERNAUX_ELF_SECT_TYPE_REL 9 +#define KERNAUX_ELF_SECT_TYPE_SHLIB 10 +#define KERNAUX_ELF_SECT_TYPE_DYNSYM 11 +#define KERNAUX_ELF_SECT_TYPE_LOPROC 0x70000000 +#define KERNAUX_ELF_SECT_TYPE_HIPROC 0x7fffffff +#define KERNAUX_ELF_SECT_TYPE_LOUSER 0x80000000 +#define KERNAUX_ELF_SECT_TYPE_HIUSER 0xffffffff + +#define KERNAUX_ELF_SECT_FLAGS_WRITE 0x1 +#define KERNAUX_ELF_SECT_FLAGS_ALLOC 0x2 +#define KERNAUX_ELF_SECT_FLAGS_EXECINSTR 0x4 +#define KERNAUX_ELF_SECT_FLAGS_MASKPROC 0xf0000000 + +const char *KernAux_ELF_Section_Type_to_str(uint32_t type); + +struct KernAux_ELF_Symbol { + uint32_t name; + uint32_t value; + uint32_t size; + uint8_t info; + uint8_t other; + uint16_t shndx; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_ELF_Symbol, 16); + +#define KERNAUX_ELF_SYM_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) +#define KERNAUX_ELF_SYM_BIND(info) ((info) >> 4) +#define KERNAUX_ELF_SYM_TYPE(info) ((into) & 0xf) + +#define KERNAUX_ELF_SYM_BIND_LOCAL 0 +#define KERNAUX_ELF_SYM_BIND_GLOBAL 1 +#define KERNAUX_ELF_SYM_BIND_WEAK 2 +#define KERNAUX_ELF_SYM_BIND_LOPROC 13 +#define KERNAUX_ELF_SYM_BIND_HIPROC 15 + +#define KERNAUX_ELF_SYM_TYPE_NOTYPE 0 +#define KERNAUX_ELF_SYM_TYPE_OBJECT 1 +#define KERNAUX_ELF_SYM_TYPE_FUNC 2 +#define KERNAUX_ELF_SYM_TYPE_SECTION 3 +#define KERNAUX_ELF_SYM_TYPE_FILE 4 +#define KERNAUX_ELF_SYM_TYPE_LOPROC 13 +#define KERNAUX_ELF_SYM_TYPE_HIPROC 15 + +struct KernAux_ELF_Program { + uint32_t type; + uint32_t offset; + uint32_t vaddr; + uint32_t paddr; + uint32_t filesz; + uint32_t memsz; + uint32_t flags; + uint32_t align; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_ELF_Program, 32); + +#define KERNAUX_ELF_PROG_TYPE_NULL 0 +#define KERNAUX_ELF_PROG_TYPE_LOAD 1 +#define KERNAUX_ELF_PROG_TYPE_DYNAMIC 2 +#define KERNAUX_ELF_PROG_TYPE_INTERP 3 +#define KERNAUX_ELF_PROG_TYPE_NOTE 4 +#define KERNAUX_ELF_PROG_TYPE_SHLIB 5 +#define KERNAUX_ELF_PROG_TYPE_PHDR 6 +#define KERNAUX_ELF_PROG_TYPE_LOPROC 0x70000000 +#define KERNAUX_ELF_PROG_TYPE_HIPROC 0x7fffffff + +#include + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/free_list.h b/include/kernaux/free_list.h new file mode 100644 index 0000000..678b39f --- /dev/null +++ b/include/kernaux/free_list.h @@ -0,0 +1,38 @@ +#ifndef KERNAUX_INCLUDED_FREE_LIST +#define KERNAUX_INCLUDED_FREE_LIST + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include + +typedef struct KernAux_FreeList_Node { + void *KERNAUX_PRIVATE_FIELD(orig_ptr); + struct KernAux_FreeList_Node *KERNAUX_PRIVATE_FIELD(next); + struct KernAux_FreeList_Node *KERNAUX_PRIVATE_FIELD(prev); + size_t KERNAUX_PRIVATE_FIELD(size); + char *KERNAUX_PRIVATE_FIELD(block); +} *KernAux_FreeList_Node; + +typedef struct KernAux_FreeList { + struct KernAux_Malloc malloc; + KernAux_Mutex KERNAUX_PRIVATE_FIELD(mutex); + KernAux_FreeList_Node KERNAUX_PRIVATE_FIELD(head); +} *KernAux_FreeList; + +struct KernAux_FreeList KernAux_FreeList_create(KernAux_Mutex mutex); +void KernAux_FreeList_init(KernAux_FreeList free_list, KernAux_Mutex mutex); + +void +KernAux_FreeList_add_zone(KernAux_FreeList free_list, void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/generic/display.h b/include/kernaux/generic/display.h new file mode 100644 index 0000000..cbe1ed8 --- /dev/null +++ b/include/kernaux/generic/display.h @@ -0,0 +1,37 @@ +#ifndef KERNAUX_INCLUDED_DISPLAY +#define KERNAUX_INCLUDED_DISPLAY + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +typedef void (*KernAux_Display_Putc )(void *display, char c); +typedef void (*KernAux_Display_Vprintf)(void *display, const char *format, va_list va); + +typedef const struct KernAux_Display { + KernAux_Display_Putc KERNAUX_PROTECTED_FIELD(putc); + KernAux_Display_Vprintf KERNAUX_PROTECTED_FIELD(vprintf); +} *KernAux_Display; + +void KernAux_Display_putc (KernAux_Display display, char c); +void KernAux_Display_print (KernAux_Display display, const char *s); +void KernAux_Display_println (KernAux_Display display, const char *s); +void KernAux_Display_write (KernAux_Display display, const char *data, size_t size); +void KernAux_Display_writeln (KernAux_Display display, const char *data, size_t size); +KERNAUX_PRINTF(2, 3) +void KernAux_Display_printf (KernAux_Display display, const char *format, ...); +KERNAUX_PRINTF(2, 3) +void KernAux_Display_printlnf (KernAux_Display display, const char *format, ...); +void KernAux_Display_vprintf (KernAux_Display display, const char *format, va_list va); +void KernAux_Display_vprintlnf(KernAux_Display display, const char *format, va_list va); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/generic/malloc.h b/include/kernaux/generic/malloc.h new file mode 100644 index 0000000..ff1092f --- /dev/null +++ b/include/kernaux/generic/malloc.h @@ -0,0 +1,33 @@ +#ifndef KERNAUX_INCLUDED_MALLOC +#define KERNAUX_INCLUDED_MALLOC + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +typedef void *(*KernAux_Malloc_Calloc) (void *malloc, size_t nmemb, size_t size); +typedef void (*KernAux_Malloc_Free) (void *malloc, void *ptr); +typedef void *(*KernAux_Malloc_Malloc) (void *malloc, size_t size); +typedef void *(*KernAux_Malloc_Realloc)(void *malloc, void *ptr, size_t size); + +typedef const struct KernAux_Malloc { + KernAux_Malloc_Calloc KERNAUX_PROTECTED_FIELD(calloc); + KernAux_Malloc_Free KERNAUX_PROTECTED_FIELD(free); + KernAux_Malloc_Malloc KERNAUX_PROTECTED_FIELD(malloc); + KernAux_Malloc_Realloc KERNAUX_PROTECTED_FIELD(realloc); +} *KernAux_Malloc; + +void *KernAux_Malloc_malloc (KernAux_Malloc malloc, size_t size); +void KernAux_Malloc_free (KernAux_Malloc malloc, void *ptr); +void *KernAux_Malloc_calloc (KernAux_Malloc malloc, size_t nmemb, size_t size); +void *KernAux_Malloc_realloc(KernAux_Malloc malloc, void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/generic/mutex.h b/include/kernaux/generic/mutex.h new file mode 100644 index 0000000..ad071e9 --- /dev/null +++ b/include/kernaux/generic/mutex.h @@ -0,0 +1,25 @@ +#ifndef KERNAUX_INCLUDED_MUTEX +#define KERNAUX_INCLUDED_MUTEX + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef void (*KernAux_Mutex_Lock )(void *mutex); +typedef void (*KernAux_Mutex_Unlock)(void *mutex); + +typedef const struct KernAux_Mutex { + KernAux_Mutex_Lock KERNAUX_PROTECTED_FIELD(lock); + KernAux_Mutex_Unlock KERNAUX_PROTECTED_FIELD(unlock); +} *KernAux_Mutex; + +void KernAux_Mutex_lock (KernAux_Mutex mutex); +void KernAux_Mutex_unlock(KernAux_Mutex mutex); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/macro.h b/include/kernaux/macro.h new file mode 100644 index 0000000..4fb1795 --- /dev/null +++ b/include/kernaux/macro.h @@ -0,0 +1,101 @@ +#ifndef KERNAUX_INCLUDED_MACRO +#define KERNAUX_INCLUDED_MACRO + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/********************* + * Language features * + *********************/ + +#define KERNAUX_NORETURN __attribute__((noreturn)) +#define KERNAUX_RETURNS_TWICE __attribute__((returns_twice)) +#define KERNAUX_UNUSED __attribute__((unused)) +#define KERNAUX_USED __attribute__((used)) + +#define KERNAUX_ALIGNED(num) __attribute__((aligned(num))) +#define KERNAUX_PRINTF(fmt, rest) __attribute__((format(printf, fmt, rest))) +#define KERNAUX_SECTION(name) __attribute__((section(name))) + +#ifdef __TINYC__ +# define KERNAUX_PACKED +#else +# define KERNAUX_PACKED __attribute__((packed)) +#endif + +#define KERNAUX_ASM(...) do { __asm__ __volatile__(__VA_ARGS__); } while (0) + +/************** + * Visibility * + **************/ + +#ifdef KERNAUX_ACCESS_PRIVATE +# define KERNAUX_PRIVATE_FIELD(id) id +# define KERNAUX_PROTECTED_FIELD(id) id +#else +# define KERNAUX_PRIVATE_FIELD(id) _private_##id + +# ifdef KERNAUX_ACCESS_PROTECTED +# define KERNAUX_PROTECTED_FIELD(id) id +# else +# define KERNAUX_PROTECTED_FIELD(id) _protected_##id +# endif +#endif // KERNAUX_ACCESS_PRIVATE + +/********************* + * Static assertions * + *********************/ + +#define KERNAUX_STATIC_TEST(name, cond) \ + KERNAUX_UNUSED \ + static const int \ + _kernaux_static_test_##name[(cond) ? 1 : -1] + +#define KERNAUX_STATIC_TEST_STRUCT_SIZE(name, size) \ + KERNAUX_STATIC_TEST(struct_size_##name, sizeof(struct name) == (size)) + +#define KERNAUX_STATIC_TEST_UNION_SIZE(name, size) \ + KERNAUX_STATIC_TEST(union_size_##name, sizeof(union name) == (size)) + +/***************** + * Simple values * + *****************/ + +#define KERNAUX_EOF (-1) + +/********************* + * Calculated values * + *********************/ + +#define KERNAUX_CONTAINER_OF(ptr, type, member) \ + ((type*)((uintptr_t)(ptr) - offsetof(type, member))) + +#define KERNAUX_BITS(n) (1u << (n)) + +#define KERNAUX_BITS8(n) ((uint8_t )(((uint8_t )1) << (n))) +#define KERNAUX_BITS16(n) ((uint16_t)(((uint16_t)1) << (n))) +#define KERNAUX_BITS32(n) ((uint32_t)(((uint32_t)1) << (n))) +#define KERNAUX_BITS64(n) ((uint64_t)(((uint64_t)1) << (n))) + +/********************* + * Safe type casting * + *********************/ + +#define KERNAUX_CAST_VAR(type, name, value) \ + KERNAUX_STATIC_TEST(cast_pos_##name, sizeof(value) <= sizeof(type)); \ + KERNAUX_STATIC_TEST(cast_neg_##name, sizeof(-(value)) <= sizeof(type)); \ + type name = (type)(value); \ + do {} while (0) + +#define KERNAUX_CAST_CONST(type, name, value) \ + KERNAUX_CAST_VAR(const type, name, value) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/macro/packing_end.run b/include/kernaux/macro/packing_end.run new file mode 100644 index 0000000..2254f00 --- /dev/null +++ b/include/kernaux/macro/packing_end.run @@ -0,0 +1,3 @@ +#ifdef __TINYC__ +#pragma pack(pop) +#endif diff --git a/include/kernaux/macro/packing_start.run b/include/kernaux/macro/packing_start.run new file mode 100644 index 0000000..72752fd --- /dev/null +++ b/include/kernaux/macro/packing_start.run @@ -0,0 +1,3 @@ +#ifdef __TINYC__ +#pragma pack(push, 1) +#endif diff --git a/include/kernaux/mbr.h b/include/kernaux/mbr.h new file mode 100644 index 0000000..4ffc15f --- /dev/null +++ b/include/kernaux/mbr.h @@ -0,0 +1,68 @@ +#ifndef KERNAUX_INCLUDED_MBR +#define KERNAUX_INCLUDED_MBR + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include +#include + +#define KERNAUX_MBR_SIZE 512 +#define KERNAUX_MBR_MAGIC 0xaa55 +#define KERNAUX_MBR_ENTRIES 4 + +#define KERNAUX_MBR_BOOTSTRAP_SIZE \ + (KERNAUX_MBR_SIZE - sizeof(struct KernAux_Mbr_Info)) + +#include + +struct KernAux_Mbr_Entry { + uint8_t drive_attributes; + unsigned first_sector_chs_addr : 24; + uint8_t partition_type; + unsigned last_sector_chs_addr : 24; + uint32_t first_sector_lba_addr; + uint32_t sectors_count; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Mbr_Entry, 16); + +struct KernAux_Mbr_Info { + uint32_t disk_id; + uint16_t reserved; + struct KernAux_Mbr_Entry entries[KERNAUX_MBR_ENTRIES]; + uint16_t magic; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Mbr_Info, + 8 + KERNAUX_MBR_ENTRIES * sizeof(struct KernAux_Mbr_Entry) +); + +struct KernAux_Mbr { + uint8_t bootstrap[KERNAUX_MBR_BOOTSTRAP_SIZE]; + struct KernAux_Mbr_Info info; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Mbr, + KERNAUX_MBR_BOOTSTRAP_SIZE + sizeof(struct KernAux_Mbr_Info) +); + +#include + +bool KernAux_Mbr_is_valid(const struct KernAux_Mbr *mbr); +bool KernAux_Mbr_Info_is_valid(const struct KernAux_Mbr_Info *mbr_info); +bool KernAux_Mbr_Entry_is_valid(const struct KernAux_Mbr_Entry *mbr_entry); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/memmap.h b/include/kernaux/memmap.h new file mode 100644 index 0000000..91a3c26 --- /dev/null +++ b/include/kernaux/memmap.h @@ -0,0 +1,67 @@ +#ifndef KERNAUX_INCLUDED_MEMMAP +#define KERNAUX_INCLUDED_MEMMAP + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include +#include +#include + +#define KERNAUX_MEMMAP_FREE(memmap) do { \ + KernAux_Memmap_free(memmap); \ + memmap = NULL; \ +} while (0) + +/********* + * Types * + *********/ + +typedef const struct KernAux_Memmap_Node { + uint64_t mem_start, mem_end, mem_size; + const char *tag; + const struct KernAux_Memmap_Node *next, *children; +} *KernAux_Memmap_Node; + +typedef const struct KernAux_Memmap { + KernAux_Memmap_Node root_node; + + KernAux_Malloc KERNAUX_PRIVATE_FIELD(malloc); +} *KernAux_Memmap; + +typedef struct KernAux_Memmap_Builder { + KernAux_Memmap KERNAUX_PRIVATE_FIELD(memmap); +} *KernAux_Memmap_Builder; + +/************* + * Functions * + *************/ + +KernAux_Memmap_Builder +KernAux_Memmap_Builder_new(KernAux_Malloc malloc); + +KernAux_Memmap_Node +KernAux_Memmap_Builder_add( + KernAux_Memmap_Builder builder, + KernAux_Memmap_Node parent_node, + uint64_t mem_start, + uint64_t mem_size, + const char *tag +); + +KernAux_Memmap +KernAux_Memmap_Builder_finish_and_free(KernAux_Memmap_Builder builder); + +void KernAux_Memmap_free(KernAux_Memmap memmap); +void KernAux_Memmap_print(KernAux_Memmap memmap, KernAux_Display display); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2.h b/include/kernaux/multiboot2.h new file mode 100644 index 0000000..8799930 --- /dev/null +++ b/include/kernaux/multiboot2.h @@ -0,0 +1,577 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2 +#define KERNAUX_INCLUDED_MULTIBOOT2 + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define KERNAUX_MULTIBOOT2_HEADER_MAGIC 0xe85250d6 +#define KERNAUX_MULTIBOOT2_INFO_MAGIC 0x36d76289 + +// @see https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#OS-image-format +#define KERNAUX_MULTIBOOT2_HEADER_ALIGN 8 +// @see https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#Basic-tags-structure +#define KERNAUX_MULTIBOOT2_INFO_ALIGN 8 +// @see https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#Header-tags +// @see https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#Basic-tags-structure +#define KERNAUX_MULTIBOOT2_TAG_ALIGN 8 + +#define KERNAUX_MULTIBOOT2_DATA(ptr) (((uint8_t*)(ptr)) + sizeof(*(ptr))) + +#define KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base) \ + ((struct KernAux_Multiboot2_HTagBase*)KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base)) +#define KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base) \ + ((struct KernAux_Multiboot2_ITagBase*)KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base)) +#define KERNAUX_MULTIBOOT2_TAG_NEXT(tag_base) \ + ((uint8_t*)tag_base + KERNAUX_MULTIBOOT2_TAG_SIZE_ALIGN(tag_base)) +#define KERNAUX_MULTIBOOT2_TAG_SIZE_ALIGN(tag_base) \ + (((tag_base)->size + 7) & ~7) + +#define KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL 1 + +#define KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE KERNAUX_BITS(0) +#define KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT KERNAUX_BITS(1) + +#include + +/********************** + * Header basic types * + **********************/ + +struct KernAux_Multiboot2_Header { + uint32_t magic; + uint32_t arch; + uint32_t total_size; + uint32_t checksum; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_Header, 16); + +struct KernAux_Multiboot2_HTagBase { + uint16_t type; + uint16_t flags; + uint32_t size; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTagBase, 8); + +/*************************** + * Information basic types * + ***************************/ + +struct KernAux_Multiboot2_Info { + uint32_t total_size; + uint32_t reserved; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_Info, 8); + +struct KernAux_Multiboot2_ITagBase { + uint32_t type; + uint32_t size; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITagBase, 8); + +/******************************** + * Information additional types * + ********************************/ + +struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase { + uint64_t base_addr; + uint64_t length; + uint32_t type; + uint32_t reserved; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_MemoryMap_EntryBase, 24); + +/************************* + * Header tag structures * + *************************/ + +struct KernAux_Multiboot2_HTag_None { + // type = 0 + // size = 8 + struct KernAux_Multiboot2_HTagBase base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_None, 8); + +struct KernAux_Multiboot2_HTag_InfoReq { + // type = 1 + // size > 8 + struct KernAux_Multiboot2_HTagBase base; + + // DATA: uint32_t mbi_tag_types[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_InfoReq, 8); + +struct KernAux_Multiboot2_HTag_Addr { + // type = 2 + // size = 24 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t header_addr; + uint32_t load_addr; + uint32_t load_end_addr; + uint32_t bss_end_addr; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_Addr, 24); + +struct KernAux_Multiboot2_HTag_EntryAddr { + // type = 3 + // size = 12 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t entry_addr; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_EntryAddr, 12); + +struct KernAux_Multiboot2_HTag_Flags { + // type = 4 + // size = 12 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t console_flags; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_Flags, 12); + +struct KernAux_Multiboot2_HTag_Framebuffer { + // type = 5 + // size = 20 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t width; + uint32_t height; + uint32_t depth; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_Framebuffer, 20); + +struct KernAux_Multiboot2_HTag_ModuleAlign { + // type = 6 + // size = 8 + struct KernAux_Multiboot2_HTagBase base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_ModuleAlign, 8); + +struct KernAux_Multiboot2_HTag_EFIBootServices { + // type = 7 + // size = 8 + struct KernAux_Multiboot2_HTagBase base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_EFIBootServices, 8); + +struct KernAux_Multiboot2_HTag_EFII386EntryAddr { + // type = 8 + // size = 12 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t entry_addr; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_EFII386EntryAddr, 12); + +struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr { + // type = 9 + // size = 12 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t entry_addr; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_EFIAmd64EntryAddr, 12); + +struct KernAux_Multiboot2_HTag_RelocatableHeader { + // type = 10 + // size = 24 + struct KernAux_Multiboot2_HTagBase base; + + uint32_t min_addr; + uint32_t max_addr; + uint32_t align; + uint32_t preference; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_HTag_RelocatableHeader, 24); + +/****************************** + * Information tag structures * + ******************************/ + +struct KernAux_Multiboot2_ITag_None { + // type = 0 + // size = 8 + struct KernAux_Multiboot2_ITagBase base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_None, 8); + +struct KernAux_Multiboot2_ITag_BootCmdLine { + // type = 1 + // size > 8 + struct KernAux_Multiboot2_ITagBase base; + + // DATA: char cmdline[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_BootCmdLine, 8); + +struct KernAux_Multiboot2_ITag_BootLoaderName { + // type = 2 + // size > 8 + struct KernAux_Multiboot2_ITagBase base; + + // DATA: char name[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_BootLoaderName, 8); + +struct KernAux_Multiboot2_ITag_Module { + // type = 3 + // size > 16 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t mod_start; + uint32_t mod_end; + + // DATA: char cmdline[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_Module, 16); + +struct KernAux_Multiboot2_ITag_BasicMemoryInfo { + // type = 4 + // size = 16 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t mem_lower; + uint32_t mem_upper; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_BasicMemoryInfo, 16); + +struct KernAux_Multiboot2_ITag_BIOSBootDevice { + // type = 5 + // size = 20 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t biosdev; + uint32_t partition; + uint32_t sub_partition; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_BIOSBootDevice, 20); + +struct KernAux_Multiboot2_ITag_MemoryMap { + // type = 6 + // size > 16 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t entry_size; + uint32_t entry_version; + + // DATA: varies(entry_size) KernAux_Multiboot2_ITag_MemoryMap_EntryBase entries[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_MemoryMap, 16); + +struct KernAux_Multiboot2_ITag_VBEInfo { + // type = 7 + // size = 784 + struct KernAux_Multiboot2_ITagBase base; + + uint16_t vbe_mode; + uint16_t vbe_interface_seg; + uint16_t vbe_interface_off; + uint16_t vbe_interface_len; + uint8_t vbe_control_info[512]; + uint8_t vbe_mode_info[256]; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_VBEInfo, 784); + +struct KernAux_Multiboot2_ITag_FramebufferInfo { + // type = 8 + // size > 32 + struct KernAux_Multiboot2_ITagBase base; + + uint64_t framebuffer_addr; + uint32_t framebuffer_pitch; + uint32_t framebuffer_width; + uint32_t framebuffer_height; + uint8_t framebuffer_bpp; + uint8_t framebuffer_type; + + // WARNING: GRUB 2 and Limine don't follow the spec, so we do not too! + // Multiboot 2: https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#Framebuffer-info + // GRUB 2: https://github.com/rhboot/grub2/blob/7259d55ffcf124e32eafb61aa381f9856e98a708/include/multiboot2.h#L288 + // Limine: https://github.com/limine-bootloader/limine/blob/1aba6b3aeb72ac55b177132ca75ea8adfbcb78aa/common/protos/multiboot2.h#L292 + uint16_t reserved; + + // DATA: varies color_info[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_FramebufferInfo, 32); + +// WARNING: GRUB 2 and Limine don't follow the spec, so we do not too! +// Multiboot 2: https://www.gnu.org/software/grub/manual/multiboot2/multiboot.html#ELF_002dSymbols +// GRUB 2: https://github.com/rhboot/grub2/blob/7259d55ffcf124e32eafb61aa381f9856e98a708/include/multiboot2.h#L314-L322 +// Limine: https://github.com/limine-bootloader/limine/blob/1aba6b3aeb72ac55b177132ca75ea8adfbcb78aa/common/protos/multiboot2.h#L318-L326 +struct KernAux_Multiboot2_ITag_ELFSymbols { + // type = 9 + // size > 20 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t num; + uint32_t entsize; + uint32_t shndx; + + // DATA: varies(entsize) section_headers[] +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_ELFSymbols, 20); + +struct KernAux_Multiboot2_ITag_APMTable { + // type = 10 + // size = 28 + struct KernAux_Multiboot2_ITagBase base; + + uint16_t version; + uint16_t cseg; + uint32_t offset; + uint16_t cseg_16; + uint16_t dseg; + uint16_t flags; + uint16_t cseg_len; + uint16_t cseg_16_len; + uint16_t dseg_len; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_APMTable, 28); + +struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr { + // type = 11 + // size = 12 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t pointer; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr, 12); + +struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr { + // type = 12 + // size = 16 + struct KernAux_Multiboot2_ITagBase base; + + uint64_t pointer; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr, 16); + +struct KernAux_Multiboot2_ITag_SMBIOSTables { + // type = 13 + // size > 16 + struct KernAux_Multiboot2_ITagBase base; + + uint8_t major; + uint8_t minor; + uint8_t reserved[6]; + + // TODO: DATA? +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_SMBIOSTables, 16); + +struct KernAux_Multiboot2_ITag_ACPIOldRSDP { + // type = 14 + // size > 8 + struct KernAux_Multiboot2_ITagBase base; + + // TODO: DATA? +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_ACPIOldRSDP, 8); + +struct KernAux_Multiboot2_ITag_ACPINewRSDP { + // type = 15 + // size > 8 + struct KernAux_Multiboot2_ITagBase base; + + // TODO: DATA? +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_ACPINewRSDP, 8); + +struct KernAux_Multiboot2_ITag_NetworkingInfo { + // type = 16 + // size > 8 + struct KernAux_Multiboot2_ITagBase base; + + // TODO: DATA? +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_NetworkingInfo, 8); + +struct KernAux_Multiboot2_ITag_EFIMemoryMap { + // type = 17 + // size > 16 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t descriptor_size; + uint32_t descriptor_version; + + // TODO: DATA? +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE(KernAux_Multiboot2_ITag_EFIMemoryMap, 16); + +struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated { + // type = 18 + // size = 8 + struct KernAux_Multiboot2_ITagBase base; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated, + 8 +); + +struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr { + // type = 19 + // size = 12 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t pointer; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr, + 12 +); + +struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr { + // type = 20 + // size = 16 + struct KernAux_Multiboot2_ITagBase base; + + uint64_t pointer; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr, + 16 +); + +struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr { + // type = 21 + // size = 12 + struct KernAux_Multiboot2_ITagBase base; + + uint32_t load_base_addr; +} +KERNAUX_PACKED; + +KERNAUX_STATIC_TEST_STRUCT_SIZE( + KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr, + 12 +); + +#include + +/********* + * Enums * + *********/ + +#include +#include + +/******************** + * Helper functions * + ********************/ + +#include +#include + +/************************ + * Validation functions * + ************************/ + +#include +#include + +/******************* + * Print functions * + *******************/ + +#include +#include + +/******************* + * Other functions * + *******************/ + +KernAux_Memmap_Builder KernAux_Multiboot2_Info_to_memmap_builder( + const struct KernAux_Multiboot2_Info *multiboot2_info, + KernAux_Malloc malloc +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/header_enums.h b/include/kernaux/multiboot2/header_enums.h new file mode 100644 index 0000000..526926f --- /dev/null +++ b/include/kernaux/multiboot2/header_enums.h @@ -0,0 +1,40 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_HEADER_ENUMS +#define KERNAUX_INCLUDED_MULTIBOOT2_HEADER_ENUMS + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#define KERNAUX_MULTIBOOT2_HEADER_ARCH_I386 0 +#define KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32 4 + +#define KERNAUX_MULTIBOOT2_HTAG_NONE 0 +#define KERNAUX_MULTIBOOT2_HTAG_INFO_REQ 1 +#define KERNAUX_MULTIBOOT2_HTAG_ADDR 2 +#define KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR 3 +#define KERNAUX_MULTIBOOT2_HTAG_FLAGS 4 +#define KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER 5 +#define KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN 6 +#define KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES 7 +#define KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR 8 +#define KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR 9 +#define KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER 10 + +#define KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE 0 +#define KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST 1 +#define KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST 2 + +const char *KernAux_Multiboot2_Header_Arch_to_str(uint32_t arch); +const char *KernAux_Multiboot2_HTag_to_str(uint16_t tag_type); +const char* +KernAux_Multiboot2_HTag_RelocatableHeader_Preference_to_str(uint32_t pref); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/header_helpers.h b/include/kernaux/multiboot2/header_helpers.h new file mode 100644 index 0000000..154a396 --- /dev/null +++ b/include/kernaux/multiboot2/header_helpers.h @@ -0,0 +1,27 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_HEADER_HELPERS +#define KERNAUX_INCLUDED_MULTIBOOT2_HEADER_HELPERS + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +const struct KernAux_Multiboot2_HTagBase +*KernAux_Multiboot2_Header_first_tag_with_type( + const struct KernAux_Multiboot2_Header *multiboot2_header, + uint16_t tag_type +); + +const struct KernAux_Multiboot2_HTagBase +*KernAux_Multiboot2_Header_tag_with_type_after( + const struct KernAux_Multiboot2_Header *multiboot2_header, + uint16_t tag_type, + const struct KernAux_Multiboot2_HTagBase *after_tag +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/header_is_valid.h b/include/kernaux/multiboot2/header_is_valid.h new file mode 100644 index 0000000..cd9b4d7 --- /dev/null +++ b/include/kernaux/multiboot2/header_is_valid.h @@ -0,0 +1,66 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_HEADER_IS_VALID +#define KERNAUX_INCLUDED_MULTIBOOT2_HEADER_IS_VALID + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +bool KernAux_Multiboot2_Header_is_valid( + const struct KernAux_Multiboot2_Header *multiboot2_header +); + +bool KernAux_Multiboot2_HTagBase_is_valid( + const struct KernAux_Multiboot2_HTagBase *tag_base +); + +bool KernAux_Multiboot2_HTag_None_is_valid( + const struct KernAux_Multiboot2_HTag_None *tag +); + +bool KernAux_Multiboot2_HTag_InfoReq_is_valid( + const struct KernAux_Multiboot2_HTag_InfoReq *tag +); + +bool KernAux_Multiboot2_HTag_Addr_is_valid( + const struct KernAux_Multiboot2_HTag_Addr *tag +); + +bool KernAux_Multiboot2_HTag_EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EntryAddr *tag +); + +bool KernAux_Multiboot2_HTag_Flags_is_valid( + const struct KernAux_Multiboot2_HTag_Flags *tag +); + +bool KernAux_Multiboot2_HTag_Framebuffer_is_valid( + const struct KernAux_Multiboot2_HTag_Framebuffer *tag +); + +bool KernAux_Multiboot2_HTag_ModuleAlign_is_valid( + const struct KernAux_Multiboot2_HTag_ModuleAlign *tag +); + +bool KernAux_Multiboot2_HTag_EFIBootServices_is_valid( + const struct KernAux_Multiboot2_HTag_EFIBootServices *tag +); + +bool KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *tag +); + +bool KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *tag +); + +bool KernAux_Multiboot2_HTag_RelocatableHeader_is_valid( + const struct KernAux_Multiboot2_HTag_RelocatableHeader *tag +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/header_macro.h b/include/kernaux/multiboot2/header_macro.h new file mode 100644 index 0000000..dc9ee80 --- /dev/null +++ b/include/kernaux/multiboot2/header_macro.h @@ -0,0 +1,46 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_HEADER_MACRO +#define KERNAUX_INCLUDED_MULTIBOOT2_HEADER_MACRO + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#define KERNAUX_MULTIBOOT2_HEADER_CHECKSUM(arch, total_size) \ + ((uint32_t)(-( \ + ((uint32_t)KERNAUX_MULTIBOOT2_HEADER_MAGIC) + \ + ((uint32_t)(arch)) + \ + ((uint32_t)(total_size)) \ + ))) + +#define KERNAUX_MULTIBOOT2_HFIELDS_COMMON(name, type) \ + struct { \ + struct KernAux_Multiboot2_HTag_##type tag; \ + } KERNAUX_PACKED name; + +#define KERNAUX_MULTIBOOT2_HFIELDS_INFO_REQ_ODD( \ + name, mbi_tag_types_count, align_name \ +) \ + struct { \ + struct KernAux_Multiboot2_HTag_InfoReq tag; \ + uint32_t mbi_tag_types[mbi_tag_types_count]; \ + } KERNAUX_PACKED name; \ + uint8_t align_name[4]; + +#define KERNAUX_MULTIBOOT2_HFIELDS_INFO_REQ_EVEN( \ + name, mbi_tag_types_count \ +) \ + struct { \ + struct KernAux_Multiboot2_HTag_InfoReq tag; \ + uint32_t mbi_tag_types[mbi_tag_types_count]; \ + } KERNAUX_PACKED name; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/header_print.h b/include/kernaux/multiboot2/header_print.h new file mode 100644 index 0000000..a202716 --- /dev/null +++ b/include/kernaux/multiboot2/header_print.h @@ -0,0 +1,79 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_HEADER_PRINT +#define KERNAUX_INCLUDED_MULTIBOOT2_HEADER_PRINT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void KernAux_Multiboot2_Header_print( + const struct KernAux_Multiboot2_Header *multiboot2_header, + KernAux_Display display +); + +void KernAux_Multiboot2_HTagBase_print( + const struct KernAux_Multiboot2_HTagBase *tag_base, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_None_print( + const struct KernAux_Multiboot2_HTag_None *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_InfoReq_print( + const struct KernAux_Multiboot2_HTag_InfoReq *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_Addr_print( + const struct KernAux_Multiboot2_HTag_Addr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EntryAddr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_Flags_print( + const struct KernAux_Multiboot2_HTag_Flags *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_Framebuffer_print( + const struct KernAux_Multiboot2_HTag_Framebuffer *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_ModuleAlign_print( + const struct KernAux_Multiboot2_HTag_ModuleAlign *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_EFIBootServices_print( + const struct KernAux_Multiboot2_HTag_EFIBootServices *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_EFII386EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_HTag_RelocatableHeader_print( + const struct KernAux_Multiboot2_HTag_RelocatableHeader *tag, + KernAux_Display display +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/info_enums.h b/include/kernaux/multiboot2/info_enums.h new file mode 100644 index 0000000..e4ac5b3 --- /dev/null +++ b/include/kernaux/multiboot2/info_enums.h @@ -0,0 +1,49 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_INFO_ENUMS +#define KERNAUX_INCLUDED_MULTIBOOT2_INFO_ENUMS + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#define KERNAUX_MULTIBOOT2_ITAG_NONE 0 +#define KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE 1 +#define KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME 2 +#define KERNAUX_MULTIBOOT2_ITAG_MODULE 3 +#define KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO 4 +#define KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE 5 +#define KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP 6 +#define KERNAUX_MULTIBOOT2_ITAG_VBE_INFO 7 +#define KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO 8 +#define KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS 9 +#define KERNAUX_MULTIBOOT2_ITAG_APM_TABLE 10 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR 11 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR 12 +#define KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES 13 +#define KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP 14 +#define KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP 15 +#define KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO 16 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP 17 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED 18 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR 19 +#define KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR 20 +#define KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR 21 + +#define KERNAUX_MULTIBOOT2_MEMMAP_AVAILABLE 1 +#define KERNAUX_MULTIBOOT2_MEMMAP_RESERVED 2 +#define KERNAUX_MULTIBOOT2_MEMMAP_ACPI_RECLAIMABLE 3 +#define KERNAUX_MULTIBOOT2_MEMMAP_NVS 4 +#define KERNAUX_MULTIBOOT2_MEMMAP_BADRAM 5 + +const char *KernAux_Multiboot2_ITag_to_str(uint32_t tag_type); +const char* +KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str(uint32_t type); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/info_helpers.h b/include/kernaux/multiboot2/info_helpers.h new file mode 100644 index 0000000..3abb505 --- /dev/null +++ b/include/kernaux/multiboot2/info_helpers.h @@ -0,0 +1,31 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_INFO_HELPERS +#define KERNAUX_INCLUDED_MULTIBOOT2_INFO_HELPERS + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +const struct KernAux_Multiboot2_ITagBase +*KernAux_Multiboot2_Info_first_tag_with_type( + const struct KernAux_Multiboot2_Info *multiboot2_info, + uint32_t tag_type +); + +const struct KernAux_Multiboot2_ITagBase +*KernAux_Multiboot2_Info_tag_with_type_after( + const struct KernAux_Multiboot2_Info *multiboot2_info, + uint32_t tag_type, + const struct KernAux_Multiboot2_ITagBase *after_tag +); + +const char *KernAux_Multiboot2_Info_boot_cmd_line( + const struct KernAux_Multiboot2_Info *multiboot2_info +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/info_is_valid.h b/include/kernaux/multiboot2/info_is_valid.h new file mode 100644 index 0000000..d8bf815 --- /dev/null +++ b/include/kernaux/multiboot2/info_is_valid.h @@ -0,0 +1,110 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_INFO_IS_VALID +#define KERNAUX_INCLUDED_MULTIBOOT2_INFO_IS_VALID + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +bool KernAux_Multiboot2_Info_is_valid( + const struct KernAux_Multiboot2_Info *multiboot2_info +); + +bool KernAux_Multiboot2_ITagBase_is_valid( + const struct KernAux_Multiboot2_ITagBase *tag_base +); + +bool KernAux_Multiboot2_ITag_None_is_valid( + const struct KernAux_Multiboot2_ITag_None *tag +); + +bool KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + const struct KernAux_Multiboot2_ITag_BootCmdLine *tag +); + +bool KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + const struct KernAux_Multiboot2_ITag_BootLoaderName *tag +); + +bool KernAux_Multiboot2_ITag_Module_is_valid( + const struct KernAux_Multiboot2_ITag_Module *tag +); + +bool KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + const struct KernAux_Multiboot2_ITag_BasicMemoryInfo *tag +); + +bool KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + const struct KernAux_Multiboot2_ITag_BIOSBootDevice *tag +); + +bool KernAux_Multiboot2_ITag_MemoryMap_is_valid( + const struct KernAux_Multiboot2_ITag_MemoryMap *tag +); + +bool KernAux_Multiboot2_ITag_VBEInfo_is_valid( + const struct KernAux_Multiboot2_ITag_VBEInfo *tag +); + +bool KernAux_Multiboot2_ITag_FramebufferInfo_is_valid( + const struct KernAux_Multiboot2_ITag_FramebufferInfo *tag +); + +bool KernAux_Multiboot2_ITag_ELFSymbols_is_valid( + const struct KernAux_Multiboot2_ITag_ELFSymbols *tag +); + +bool KernAux_Multiboot2_ITag_APMTable_is_valid( + const struct KernAux_Multiboot2_ITag_APMTable *tag +); + +bool KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr *tag +); + +bool KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr *tag +); + +bool KernAux_Multiboot2_ITag_SMBIOSTables_is_valid( + const struct KernAux_Multiboot2_ITag_SMBIOSTables *tag +); + +bool KernAux_Multiboot2_ITag_ACPIOldRSDP_is_valid( + const struct KernAux_Multiboot2_ITag_ACPIOldRSDP *tag +); + +bool KernAux_Multiboot2_ITag_ACPINewRSDP_is_valid( + const struct KernAux_Multiboot2_ITag_ACPINewRSDP *tag +); + +bool KernAux_Multiboot2_ITag_NetworkingInfo_is_valid( + const struct KernAux_Multiboot2_ITag_NetworkingInfo *tag +); + +bool KernAux_Multiboot2_ITag_EFIMemoryMap_is_valid( + const struct KernAux_Multiboot2_ITag_EFIMemoryMap *tag +); + +bool KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_is_valid( + const struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated *tag +); + +bool KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr *tag +); + +bool KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr *tag +); + +bool KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_is_valid( + const struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr *tag +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/multiboot2/info_print.h b/include/kernaux/multiboot2/info_print.h new file mode 100644 index 0000000..c8422c3 --- /dev/null +++ b/include/kernaux/multiboot2/info_print.h @@ -0,0 +1,134 @@ +#ifndef KERNAUX_INCLUDED_MULTIBOOT2_INFO_PRINT +#define KERNAUX_INCLUDED_MULTIBOOT2_INFO_PRINT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +void KernAux_Multiboot2_Info_print( + const struct KernAux_Multiboot2_Info *multiboot2_info, + KernAux_Display display +); + +void KernAux_Multiboot2_ITagBase_print( + const struct KernAux_Multiboot2_ITagBase *tag_base, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_None_print( + const struct KernAux_Multiboot2_ITag_None *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_BootCmdLine_print( + const struct KernAux_Multiboot2_ITag_BootCmdLine *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_BootLoaderName_print( + const struct KernAux_Multiboot2_ITag_BootLoaderName *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_Module_print( + const struct KernAux_Multiboot2_ITag_Module *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_BasicMemoryInfo_print( + const struct KernAux_Multiboot2_ITag_BasicMemoryInfo *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_BIOSBootDevice_print( + const struct KernAux_Multiboot2_ITag_BIOSBootDevice *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_MemoryMap_print( + const struct KernAux_Multiboot2_ITag_MemoryMap *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_VBEInfo_print( + const struct KernAux_Multiboot2_ITag_VBEInfo *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_FramebufferInfo_print( + const struct KernAux_Multiboot2_ITag_FramebufferInfo *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_ELFSymbols_print( + const struct KernAux_Multiboot2_ITag_ELFSymbols *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_APMTable_print( + const struct KernAux_Multiboot2_ITag_APMTable *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_print( + const struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_print( + const struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_SMBIOSTables_print( + const struct KernAux_Multiboot2_ITag_SMBIOSTables *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_ACPIOldRSDP_print( + const struct KernAux_Multiboot2_ITag_ACPIOldRSDP *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_ACPINewRSDP_print( + const struct KernAux_Multiboot2_ITag_ACPINewRSDP *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_NetworkingInfo_print( + const struct KernAux_Multiboot2_ITag_NetworkingInfo *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFIMemoryMap_print( + const struct KernAux_Multiboot2_ITag_EFIMemoryMap *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_print( + const struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_print( + const struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_print( + const struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr *tag, + KernAux_Display display +); + +void KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_print( + const struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr *tag, + KernAux_Display display +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/ntoa.h b/include/kernaux/ntoa.h new file mode 100644 index 0000000..6b3c598 --- /dev/null +++ b/include/kernaux/ntoa.h @@ -0,0 +1,70 @@ +#ifndef KERNAUX_INCLUDED_NTOA +#define KERNAUX_INCLUDED_NTOA + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define KERNAUX_NTOA_MAX_PREFIX_LEN 100 + +#define KERNAUX_NTOA_DEFAULT_PREFIX_2 "0b" +#define KERNAUX_NTOA_DEFAULT_PREFIX_8 "0o" +#define KERNAUX_NTOA_DEFAULT_PREFIX_16 "0x" + +// "1111111111111111111111111111111111111111111111111111111111111111" +#define KERNAUX_UTOA_MIN_BUFFER_SIZE (64 + 1) + +// "111111111111111111111111111111111111111111111111111111111111111" +// "-1000000000000000000000000000000000000000000000000000000000000000" +#define KERNAUX_ITOA_MIN_BUFFER_SIZE (65 + 1) + +// "0b1111111111111111111111111111111111111111111111111111111111111111" +#define KERNAUX_UTOA2_BUFFER_SIZE (64 + 2 + 1) + +// "0b111111111111111111111111111111111111111111111111111111111111111" +// "-0b1000000000000000000000000000000000000000000000000000000000000000" +#define KERNAUX_ITOA2_BUFFER_SIZE (65 + 2 + 1) + +// "0o1777777777777777777777" +#define KERNAUX_UTOA8_BUFFER_SIZE (21 + 2 + 1) + +// "0o777777777777777777777" +// "-0o1000000000000000000000" +#define KERNAUX_ITOA8_BUFFER_SIZE (22 + 2 + 1) + +// "18446744073709551615" +#define KERNAUX_UTOA10_BUFFER_SIZE (20 + 1) + +// "9223372036854775807" +// "-9223372036854775808" +#define KERNAUX_ITOA10_BUFFER_SIZE (20 + 1) + +// "0xffffffffffffffff" +#define KERNAUX_UTOA16_BUFFER_SIZE (16 + 2 + 1) + +// "0x7fffffffffffffff" +// "-0x8000000000000000" +#define KERNAUX_ITOA16_BUFFER_SIZE (17 + 2 + 1) + +char *kernaux_utoa(uint64_t value, char *buffer, int base, const char *prefix); +char *kernaux_itoa(int64_t value, char *buffer, int base, const char *prefix); + +char *kernaux_utoa2(uint64_t value, char *buffer); +char *kernaux_itoa2(int64_t value, char *buffer); + +char *kernaux_utoa8(uint64_t value, char *buffer); +char *kernaux_itoa8(int64_t value, char *buffer); + +char *kernaux_utoa10(uint64_t value, char *buffer); +char *kernaux_itoa10(int64_t value, char *buffer); + +char *kernaux_utoa16(uint64_t value, char *buffer); +char *kernaux_itoa16(int64_t value, char *buffer); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/pfa.h b/include/kernaux/pfa.h new file mode 100644 index 0000000..c6904bb --- /dev/null +++ b/include/kernaux/pfa.h @@ -0,0 +1,36 @@ +#ifndef KERNAUX_INCLUDED_PFA +#define KERNAUX_INCLUDED_PFA + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#define KERNAUX_PFA_PAGE_SIZE (4 * 1024) +#define KERNAUX_PFA_PAGES_COUNT_MAX (1024 * 1024) +#define KERNAUX_PFA_FLAGS_SIZE (KERNAUX_PFA_PAGES_COUNT_MAX / 8) + +typedef struct KernAux_PFA *KernAux_PFA; + +struct KernAux_PFA { + uint8_t flags[KERNAUX_PFA_FLAGS_SIZE]; +}; + +void KernAux_PFA_initialize(KernAux_PFA pfa); + +bool KernAux_PFA_is_available(KernAux_PFA pfa, size_t page_addr); + +void KernAux_PFA_mark_available(KernAux_PFA pfa, size_t start, size_t end); +void KernAux_PFA_mark_unavailable(KernAux_PFA pfa, size_t start, size_t end); + +size_t KernAux_PFA_alloc_pages(KernAux_PFA pfa, size_t mem_size); +void KernAux_PFA_free_pages(KernAux_PFA pfa, size_t page_addr, size_t mem_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/printf.h b/include/kernaux/printf.h new file mode 100644 index 0000000..19caedc --- /dev/null +++ b/include/kernaux/printf.h @@ -0,0 +1,48 @@ +#ifndef KERNAUX_INCLUDED_PRINTF +#define KERNAUX_INCLUDED_PRINTF + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * Tiny [v]fprintf implementation + * \param out An output function + * \param data Additional data for the output function + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that are sent to the output function, not counting the terminating null character + */ +int kernaux_fprintf(void (*out)(char, void*), void *data, const char* format, ...); +int kernaux_vfprintf(void (*out)(char, void*), void *data, const char* format, va_list va); + +/** + * Tiny [v]snprintf implementation + * \param buffer A pointer to the buffer where to store the formatted string + * \param count The maximum number of characters to store in the buffer, including a terminating null character + * \param format A string that specifies the format of the output + * \param va A value identifying a variable arguments list + * \return The number of characters that COULD have been written into the buffer, not counting the terminating + * null character. A value equal or larger than count indicates truncation. Only when the returned value + * is non-negative and less than count, the string has been completely written. + */ +int kernaux_snprintf(char* buffer, size_t count, const char* format, ...); +int kernaux_vsnprintf(char* buffer, size_t count, const char* format, va_list va); + +/** + * Tiny sprintf implementation + * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! + * \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! + * \param format A string that specifies the format of the output + * \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character + */ +int kernaux_sprintf(char* buffer, const char* format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/printf_fmt.h b/include/kernaux/printf_fmt.h new file mode 100644 index 0000000..e566922 --- /dev/null +++ b/include/kernaux/printf_fmt.h @@ -0,0 +1,75 @@ +#ifndef KERNAUX_INCLUDED_PRINTF_FMT +#define KERNAUX_INCLUDED_PRINTF_FMT + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +#define KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD KERNAUX_BITS(0) +#define KERNAUX_PRINTF_FMT_FLAGS_LEFT KERNAUX_BITS(1) +#define KERNAUX_PRINTF_FMT_FLAGS_PLUS KERNAUX_BITS(2) +#define KERNAUX_PRINTF_FMT_FLAGS_SPACE KERNAUX_BITS(3) +#define KERNAUX_PRINTF_FMT_FLAGS_HASH KERNAUX_BITS(4) +#define KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE KERNAUX_BITS(5) +#define KERNAUX_PRINTF_FMT_FLAGS_CHAR KERNAUX_BITS(6) +#define KERNAUX_PRINTF_FMT_FLAGS_SHORT KERNAUX_BITS(7) +#define KERNAUX_PRINTF_FMT_FLAGS_LONG KERNAUX_BITS(8) +#define KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG KERNAUX_BITS(9) +#define KERNAUX_PRINTF_FMT_FLAGS_PRECISION KERNAUX_BITS(10) +#define KERNAUX_PRINTF_FMT_FLAGS_ADAPT_EXP KERNAUX_BITS(11) + +enum KernAux_PrintfFmt_Type { + KERNAUX_PRINTF_FMT_TYPE_NONE, + KERNAUX_PRINTF_FMT_TYPE_INT, + KERNAUX_PRINTF_FMT_TYPE_UINT, + KERNAUX_PRINTF_FMT_TYPE_FLOAT, + KERNAUX_PRINTF_FMT_TYPE_EXP, + KERNAUX_PRINTF_FMT_TYPE_CHAR, + KERNAUX_PRINTF_FMT_TYPE_STR, + KERNAUX_PRINTF_FMT_TYPE_PTR, + KERNAUX_PRINTF_FMT_TYPE_PERCENT, +}; + +struct KernAux_PrintfFmt_Spec { + const char *format_start; + const char *format_limit; + + unsigned int flags; + unsigned int width; + unsigned int precision; + enum KernAux_PrintfFmt_Type type; + unsigned int base; + + bool set_width; + bool set_precision; +}; + +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create( + const char *format +); +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create_out( + const char **format +); +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create_out_new( + const char *format, + const char **new_format +); + +void KernAux_PrintfFmt_Spec_set_width( + struct KernAux_PrintfFmt_Spec *spec, + int width +); +void KernAux_PrintfFmt_Spec_set_precision( + struct KernAux_PrintfFmt_Spec *spec, + int precision +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/runtime.h b/include/kernaux/runtime.h new file mode 100644 index 0000000..20ac4cf --- /dev/null +++ b/include/kernaux/runtime.h @@ -0,0 +1,18 @@ +#ifndef KERNAUX_INCLUDED_RUNTIME +#define KERNAUX_INCLUDED_RUNTIME + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*KernAux_Assert_Cb)(const char *file, int line, const char *msg); + +extern KernAux_Assert_Cb kernaux_assert_cb; + +void kernaux_assert_do(const char *file, int line, const char *msg); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/units.h b/include/kernaux/units.h new file mode 100644 index 0000000..eee9361 --- /dev/null +++ b/include/kernaux/units.h @@ -0,0 +1,42 @@ +#ifndef KERNAUX_INCLUDED_UNITS +#define KERNAUX_INCLUDED_UNITS + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +enum KernAux_Unit { + KERNAUX_UNIT_BIT, + KERNAUX_UNIT_BYTE, +}; + +enum KernAux_UnitPrefixDec { + KERNAUX_UNITPFX_KILO, + KERNAUX_UNITPFX_MEGA, + KERNAUX_UNITPFX_GIGA, +}; + +enum KernAux_UnitPrefixBin { + KERNAUX_UNITPFX_KIBI, + KERNAUX_UNITPFX_MEBI, + KERNAUX_UNITPFX_GIBI, +}; + +bool kernaux_units_human_raw(uint64_t value, enum KernAux_Unit unit, + char *buffer, size_t buffer_size); +bool kernaux_units_human_dec(uint64_t value, enum KernAux_Unit unit, + enum KernAux_UnitPrefixDec prefix, + char *buffer, size_t buffer_size); +bool kernaux_units_human_bin(uint64_t value, enum KernAux_Unit unit, + enum KernAux_UnitPrefixBin prefix, + char *buffer, size_t buffer_size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernaux/version.h.in b/include/kernaux/version.h.in new file mode 100644 index 0000000..24db409 --- /dev/null +++ b/include/kernaux/version.h.in @@ -0,0 +1,16 @@ +#ifndef KERNAUX_INCLUDED_VERSION +#define KERNAUX_INCLUDED_VERSION + +@comment_line_cmdline@#define KERNAUX_VERSION_WITH_CMDLINE +@comment_line_elf@#define KERNAUX_VERSION_WITH_ELF +@comment_line_free_list@#define KERNAUX_VERSION_WITH_FREE_LIST +@comment_line_mbr@#define KERNAUX_VERSION_WITH_MBR +@comment_line_memmap@#define KERNAUX_VERSION_WITH_MEMMAP +@comment_line_multiboot2@#define KERNAUX_VERSION_WITH_MULTIBOOT2 +@comment_line_ntoa@#define KERNAUX_VERSION_WITH_NTOA +@comment_line_pfa@#define KERNAUX_VERSION_WITH_PFA +@comment_line_printf@#define KERNAUX_VERSION_WITH_PRINTF +@comment_line_printf_fmt@#define KERNAUX_VERSION_WITH_PRINTF_FMT +@comment_line_units@#define KERNAUX_VERSION_WITH_UNITS + +#endif diff --git a/libc/Makefile.am b/libc/Makefile.am new file mode 100644 index 0000000..524872f --- /dev/null +++ b/libc/Makefile.am @@ -0,0 +1,30 @@ +include $(top_srcdir)/make/shared.am + +SUBDIRS = include + +if ENABLE_SPLIT_LIBC +lib_LTLIBRARIES = libc.la +else +EXTRA_LTLIBRARIES = libc.la +endif + +libc_la_SOURCES = \ + src/ctype.c \ + src/errno.c \ + src/kernaux.c \ + src/stdlib.c \ + src/string.c + +if ASM_I386 +libc_la_SOURCES += \ + src/asm/i386/longjmp.S \ + src/asm/i386/setjmp.S +endif + +if ASM_X86_64 +libc_la_SOURCES += \ + src/asm/x86_64/longjmp.S \ + src/asm/x86_64/setjmp.S +endif + +# TODO: implement setjmp/longjmp for riscv64 diff --git a/libc/include/Makefile.am b/libc/include/Makefile.am new file mode 100644 index 0000000..4a748cc --- /dev/null +++ b/libc/include/Makefile.am @@ -0,0 +1,9 @@ +nobase_include_HEADERS = \ + kernaux/libc.h \ + ctype.h \ + errno.h \ + inttypes.h \ + setjmp.h \ + stdlib.h \ + string.h \ + sys/types.h diff --git a/libc/include/ctype.h b/libc/include/ctype.h new file mode 100644 index 0000000..f8b895a --- /dev/null +++ b/libc/include/ctype.h @@ -0,0 +1,20 @@ +#ifndef _CTYPE_H +#define _CTYPE_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +int isdigit(int c); +int islower(int c); +int isspace(int c); +int isupper(int c); + +int tolower(int c); +int toupper(int c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/errno.h b/libc/include/errno.h new file mode 100644 index 0000000..755a4c7 --- /dev/null +++ b/libc/include/errno.h @@ -0,0 +1,16 @@ +#ifndef _ERRNO_H +#define _ERRNO_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#define ERANGE 1 + +extern int errno; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/inttypes.h b/libc/include/inttypes.h new file mode 100644 index 0000000..0e78627 --- /dev/null +++ b/libc/include/inttypes.h @@ -0,0 +1,12 @@ +#ifndef _INTTYPES_H +#define _INTTYPES_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/kernaux/libc.h b/libc/include/kernaux/libc.h new file mode 100644 index 0000000..46ceb92 --- /dev/null +++ b/libc/include/kernaux/libc.h @@ -0,0 +1,26 @@ +#ifndef KERNAUX_INCLUDED_LIBC +#define KERNAUX_INCLUDED_LIBC + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +struct KernAux_Libc { + void (*abort)() __attribute__((noreturn)); + void (*exit)(int status) __attribute__((noreturn)); + + void *(*calloc)(size_t nmemb, size_t size); + void (*free)(void *ptr); + void *(*malloc)(size_t size); + void *(*realloc)(void *ptr, size_t size); +}; + +extern struct KernAux_Libc kernaux_libc; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/setjmp.h b/libc/include/setjmp.h new file mode 100644 index 0000000..b46ffac --- /dev/null +++ b/libc/include/setjmp.h @@ -0,0 +1,27 @@ +#ifndef _SETJMP_H +#define _SETJMP_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +// TODO: define in architecture-specific header +typedef unsigned long __jmp_buf[sizeof(long) == 8 ? 8 : 6]; + +typedef struct __jmp_buf_tag { + __jmp_buf __jb; + unsigned long __fl; + unsigned long __ss[128 / sizeof(long)]; +} jmp_buf[1]; + +__attribute__((returns_twice)) +int setjmp(jmp_buf env); + +__attribute__((noreturn)) +void longjmp(jmp_buf env, int val); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h new file mode 100644 index 0000000..18baf84 --- /dev/null +++ b/libc/include/stdlib.h @@ -0,0 +1,29 @@ +#ifndef _STDLIB_H +#define _STDLIB_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + +int atoi(const char *str); + +__attribute__((noreturn)) +void abort(); +__attribute__((noreturn)) +void exit(int status); + +void *calloc(size_t nmemb, size_t size); +void free(void *ptr); +void *malloc(size_t size); +void *realloc(void *ptr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/string.h b/libc/include/string.h new file mode 100644 index 0000000..682a117 --- /dev/null +++ b/libc/include/string.h @@ -0,0 +1,37 @@ +#ifndef _STRING_H +#define _STRING_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +// mem* +int memcmp(const void *s1, const void *s2, size_t n); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memchr(const void *s, int c, size_t n); +void *memset(void *s, int c, size_t n); + +// str* +char *strcat(char *dest, const char *src); +char *strchr(const char *s, int c); +int strcmp(const char *s1, const char *s2); +char *strcpy(char *dest, const char *src); +size_t strlen(const char *s); + +// strn* +char *strncat(char *dest, const char *src, size_t n); +int strncmp(const char *s1, const char *s2, size_t n); +char *strncpy(char *dest, const char *src, size_t n); +size_t strnlen(const char *s, size_t maxlen); + +// str* +char *strstr(const char *haystack, const char *needle); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/include/sys/types.h b/libc/include/sys/types.h new file mode 100644 index 0000000..d1bf329 --- /dev/null +++ b/libc/include/sys/types.h @@ -0,0 +1,12 @@ +#ifndef _SYS_TYPES_H +#define _SYS_TYPES_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libc/src/asm/i386/longjmp.S b/libc/src/asm/i386/longjmp.S new file mode 100644 index 0000000..00fafbd --- /dev/null +++ b/libc/src/asm/i386/longjmp.S @@ -0,0 +1,25 @@ +/** + * The code was taken from musl libc. + * + * Copyright (c) 2011 Rich Felker + * Copyright (c) 2022 Alexander Monakov + * Copyright (c) 2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +.global longjmp +.type longjmp, @function +longjmp: + mov 4(%esp) , %edx + mov 8(%esp) , %eax + cmp $1 , %eax + adc $0 , %al + mov (%edx) , %ebx + mov 4(%edx) , %esi + mov 8(%edx) , %edi + mov 12(%edx) , %ebp + mov 16(%edx) , %esp + jmp *20(%edx) diff --git a/libc/src/asm/i386/setjmp.S b/libc/src/asm/i386/setjmp.S new file mode 100644 index 0000000..841544d --- /dev/null +++ b/libc/src/asm/i386/setjmp.S @@ -0,0 +1,25 @@ +/** + * The code was taken from musl libc. + * + * Copyright (c) 2011-2015 Rich Felker + * Copyright (c) 2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +.global setjmp +.type setjmp, @function +setjmp: + mov 4(%esp) , %eax + mov %ebx , (%eax) + mov %esi , 4(%eax) + mov %edi , 8(%eax) + mov %ebp , 12(%eax) + lea 4(%esp) , %ecx + mov %ecx , 16(%eax) + mov (%esp) , %ecx + mov %ecx , 20(%eax) + xor %eax , %eax + ret diff --git a/libc/src/asm/x86_64/longjmp.S b/libc/src/asm/x86_64/longjmp.S new file mode 100644 index 0000000..7565082 --- /dev/null +++ b/libc/src/asm/x86_64/longjmp.S @@ -0,0 +1,27 @@ +/** + * The code was taken from musl libc. + * + * Copyright (c) 2011 Nicholas J. Kain + * Copyright (c) 2011-2012 Rich Felker + * Copyright (c) 2022 Alexander Monakov + * Copyright (c) 2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +.global longjmp +.type longjmp, @function +longjmp: + xor %eax , %eax + cmp $1 , %esi /* CF = val ? 0 : 1 */ + adc %esi , %eax /* eax = val + !val */ + mov (%rdi) , %rbx /* rdi is the jmp_buf, restore regs from it */ + mov 8(%rdi) , %rbp + mov 16(%rdi) , %r12 + mov 24(%rdi) , %r13 + mov 32(%rdi) , %r14 + mov 40(%rdi) , %r15 + mov 48(%rdi) , %rsp + jmp *56(%rdi) /* goto saved address without altering rsp */ diff --git a/libc/src/asm/x86_64/setjmp.S b/libc/src/asm/x86_64/setjmp.S new file mode 100644 index 0000000..6279a60 --- /dev/null +++ b/libc/src/asm/x86_64/setjmp.S @@ -0,0 +1,28 @@ +/** + * The code was taken from musl libc. + * + * Copyright (c) 2011 Nicholas J. Kain + * Copyright (c) 2011-2012 Rich Felker + * Copyright (c) 2022 Alexander Monakov + * Copyright (c) 2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +.global setjmp +.type setjmp, @function +setjmp: +mov %rbx , (%rdi) /* rdi is jmp_buf, move registers onto it */ +mov %rbp , 8(%rdi) +mov %r12 , 16(%rdi) +mov %r13 , 24(%rdi) +mov %r14 , 32(%rdi) +mov %r15 , 40(%rdi) +lea 8(%rsp) , %rdx /* this is our rsp WITHOUT current ret addr */ +mov %rdx , 48(%rdi) +mov (%rsp) , %rdx /* save return addr ptr for new rip */ +mov %rdx , 56(%rdi) +xor %eax , %eax /* always return 0 */ +ret diff --git a/libc/src/ctype.c b/libc/src/ctype.c new file mode 100644 index 0000000..92ab5f4 --- /dev/null +++ b/libc/src/ctype.c @@ -0,0 +1,35 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +int isdigit(const int c) +{ + return (unsigned)c - '0' < 10; +} + +int islower(const int c) +{ + return (unsigned)c - 'a' < 26; +} + +int isspace(const int c) +{ + return c == ' ' || (unsigned)c - '\t' < 5; +} + +int isupper(const int c) +{ + return (unsigned)c - 'A' < 26; +} + +int tolower(const int c) +{ + return isupper(c) ? (c + ('a' - 'A')) : c; +} + +int toupper(const int c) +{ + return islower(c) ? (c - ('a' - 'A')) : c; +} diff --git a/libc/src/errno.c b/libc/src/errno.c new file mode 100644 index 0000000..ebd7d69 --- /dev/null +++ b/libc/src/errno.c @@ -0,0 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +int errno = 0; diff --git a/libc/src/kernaux.c b/libc/src/kernaux.c new file mode 100644 index 0000000..688c769 --- /dev/null +++ b/libc/src/kernaux.c @@ -0,0 +1,17 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +struct KernAux_Libc kernaux_libc = { + .abort = NULL, + .exit = NULL, + + .calloc = NULL, + .free = NULL, + .malloc = NULL, + .realloc = NULL, +}; diff --git a/libc/src/stdlib.c b/libc/src/stdlib.c new file mode 100644 index 0000000..5d6a9ca --- /dev/null +++ b/libc/src/stdlib.c @@ -0,0 +1,70 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include + +void exit(const int status) +{ + // Custom implementation + kernaux_libc.exit(status); +} + +void abort() +{ + // Custom implementation + if (kernaux_libc.abort) kernaux_libc.abort(); + + // Default implementation + exit(EXIT_FAILURE); +} + +void *calloc(const size_t nmemb, const size_t size) +{ + // Custom implementation + if (kernaux_libc.calloc) return kernaux_libc.calloc(nmemb, size); + + // Default implementation + const size_t total_size = nmemb * size; + if (!total_size) return NULL; + if (total_size / nmemb != size) return NULL; + void *const ptr = malloc(total_size); + if (ptr) memset(ptr, 0, total_size); + return ptr; +} + +void free(void *const ptr) +{ + // Custom implementation + kernaux_libc.free(ptr); +} + +void *malloc(const size_t size) +{ + // Custom implementation + return kernaux_libc.malloc(size); +} + +void *realloc(void *const ptr, const size_t size) +{ + // Custom implementation + return kernaux_libc.realloc(ptr, size); +} + +int atoi(const char *str) +{ + while (isspace(*str)) ++str; + bool is_negative = false; + switch (*str) { + case '-': is_negative = true; // fall through + case '+': ++str; + } + int result = 0; + while (isdigit(*str)) result = 10 * result - (*str++ - '0'); + return is_negative ? result : -result; +} diff --git a/libc/src/string.c b/libc/src/string.c new file mode 100644 index 0000000..45045f8 --- /dev/null +++ b/libc/src/string.c @@ -0,0 +1,135 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +int memcmp(const void *s1, const void *s2, size_t n) +{ + for (const unsigned char *p1 = s1, *p2 = s2; n--; ++p1, ++p2) { + if (*p1 != *p2) return *p1 - *p2; + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t n) +{ + char *dest_cell = dest; + char *src_cell = (char*)src; + while (n--) *dest_cell++ = *src_cell++; + return dest; +} + +void *memmove(void *dest, const void *src, size_t n) +{ + char *dest_cell = dest; + char *src_cell = (char*)src; + if (dest_cell <= src_cell) { + while (n--) *dest_cell++ = *src_cell++; + } else { + dest_cell += n; + src_cell += n; + while (n--) *--dest_cell = *--src_cell; + } + return dest; +} + +void *memchr(const void *s, int c, size_t n) +{ + for (const unsigned char *p = s; n--; ++p) { + if ((unsigned char)c == *p) return (void*)p; + } + return NULL; +} + +void *memset(void *s, int c, size_t n) +{ + char *ss = s; + while (n--) *ss++ = c; + return s; +} + +char *strcat(char *dest, const char *src) +{ + char *const dest_start = dest; + while (*dest) ++dest; + while ((*dest++ = *src++)); + return dest_start; +} + +char *strchr(const char *s, int c) +{ + for (; *s != (char)c; ++s) if (*s == '\0') return NULL; + return (char*)s; +} + +int strcmp(const char *s1, const char *s2) +{ + for (; *s1; ++s1, ++s2) if (*s1 != *s2) return *s1 < *s2 ? -1 : 1; + return 0; +} + +char *strcpy(char *dest, const char *src) +{ + char *tmp = dest; + while ((*dest++ = *src++) != '\0'); + return tmp; +} + +size_t strlen(const char *s) +{ + const char *ss = s; + while (*ss != '\0') ++ss; + return ss - s; +} + +char *strncat(char *dest, const char *src, size_t n) +{ + char *const dest_start = dest; + if (n) { + while (*dest) ++dest; + while ((*dest++ = *src++)) { + if (--n == 0) { + *dest = '\0'; + break; + } + } + } + return dest_start; +} + +int strncmp(const char *s1, const char *s2, size_t n) +{ + for (; *s1 && n; ++s1, ++s2, --n) if (*s1 != *s2) return *s1 < *s2 ? -1 : 1; + return 0; +} + +char *strncpy(char *dest, const char *src, size_t n) +{ + char *tmp = dest; + while (n-- && (*dest++ = *src++) != '\0'); + return tmp; +} + +size_t strnlen(const char *s, size_t maxlen) +{ + const char *ss = s; + while (*ss != '\0' && maxlen--) ++ss; + return ss - s; +} + +char *strstr(const char *haystack, const char *needle) +{ + const size_t needle_slen = strlen(needle); + if (!needle_slen) return (char*)haystack; + + size_t haystack_slen = strlen(haystack); + while (haystack_slen >= needle_slen) { + --haystack_slen; + if (!memcmp(haystack, needle, needle_slen)) return (char*)haystack; + ++haystack; + } + + return NULL; +} diff --git a/libkernaux.pc.in b/libkernaux.pc.in new file mode 100644 index 0000000..6caa194 --- /dev/null +++ b/libkernaux.pc.in @@ -0,0 +1,11 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: @PACKAGE_NAME@ +Description: @PACKAGE_DESCR@ +URL: @PACKAGE_URL@ +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lkernaux +Cflags: -I${includedir} diff --git a/m4/.keep b/m4/.keep new file mode 100644 index 0000000..e69de29 diff --git a/make/checks.am b/make/checks.am new file mode 100644 index 0000000..0424b4a --- /dev/null +++ b/make/checks.am @@ -0,0 +1,40 @@ +# vim: set syntax=automake: + +MY_CHECKS = +check: $(MY_CHECKS) + +if ENABLE_CHECKS_CPPCHECK +MY_CHECKS += check-cppcheck +check-cppcheck: + $(CPPCHECK) $(CPPCHECK_ARGS) $(CPPCHECK_INC) $(CPPCHECK_SUPPRESS) $(CPPCHECK_PATHS) + +CPPCHECK_ARGS = \ + --quiet \ + --error-exitcode=1 \ + --std=c99 \ + --inline-suppr \ + --enable=warning,style,performance,portability + +CPPCHECK_INC = \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include + +CPPCHECK_SUPPRESS = \ + --suppress='constArgument:$(top_srcdir)/examples/macro_cast.c' \ + --suppress='constParameter:$(top_srcdir)/examples/printf_file*.c' \ + --suppress='unusedStructMember:$(top_srcdir)/examples/*.c' \ + --suppress='unusedStructMember:$(top_srcdir)/tests/test_multiboot2_info_*.c' \ + --suppress='unusedVariable' + +CPPCHECK_PATHS = \ + $(top_builddir)/examples \ + $(top_srcdir)/examples \ + $(top_builddir)/include \ + $(top_srcdir)/include \ + $(top_builddir)/libc \ + $(top_srcdir)/libc \ + $(top_builddir)/src \ + $(top_srcdir)/src \ + $(top_builddir)/tests \ + $(top_srcdir)/tests +endif diff --git a/make/shared.am b/make/shared.am new file mode 100644 index 0000000..767a8db --- /dev/null +++ b/make/shared.am @@ -0,0 +1,20 @@ +# vim: set syntax=automake: + +AM_CFLAGS = \ + -std=c99 \ + -pedantic \ + -Wall \ + -Wextra \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -D_POSIX_C_SOURCE=200809L + +if WITH_LIBC +AM_CFLAGS += \ + -I$(top_builddir)/libc/include \ + -I$(top_srcdir)/libc/include +endif + +if ENABLE_WERROR +AM_CFLAGS += -Werror +endif diff --git a/make/test-suite-log b/make/test-suite-log new file mode 100755 index 0000000..de24605 --- /dev/null +++ b/make/test-suite-log @@ -0,0 +1,6 @@ +#!/bin/sh + +set -eux + +test -f examples/test-suite.log && cat examples/test-suite.log || true +test -f tests/test-suite.log && cat tests/test-suite.log || true diff --git a/pkgs/freebsd/.gitignore b/pkgs/freebsd/.gitignore new file mode 100644 index 0000000..caefc69 --- /dev/null +++ b/pkgs/freebsd/.gitignore @@ -0,0 +1 @@ +/devel/libkernaux/work/ diff --git a/pkgs/freebsd/devel/libkernaux/Makefile b/pkgs/freebsd/devel/libkernaux/Makefile new file mode 100644 index 0000000..c59edfb --- /dev/null +++ b/pkgs/freebsd/devel/libkernaux/Makefile @@ -0,0 +1,19 @@ +PORTNAME= libkernaux +DISTVERSION= 0.7.0 +CATEGORIES= devel +MASTER_SITES= https://github.com/tailix/libkernaux/releases/download/v${DISTVERSION}/ + +MAINTAINER= kotovalexarian@gmail.com +COMMENT= Auxiliary library for kernel development +WWW= https://github.com/tailix/libkernaux + +LICENSE= MIT +LICENSE_FILE= ${WRKSRC}/COPYING + +USES= libtool +USE_LDCONFIG= yes +GNU_CONFIGURE= yes + +CONFIGURE_ARGS+= --enable-pkg-config=${PREFIX}/libdata/pkgconfig + +.include diff --git a/pkgs/freebsd/devel/libkernaux/distinfo b/pkgs/freebsd/devel/libkernaux/distinfo new file mode 100644 index 0000000..ab6e09f --- /dev/null +++ b/pkgs/freebsd/devel/libkernaux/distinfo @@ -0,0 +1,3 @@ +TIMESTAMP = 1671787519 +SHA256 (libkernaux-0.7.0.tar.gz) = 19bbfe5d38731175fd762547fc169adffde6350cf30c9238f931909b26decd85 +SIZE (libkernaux-0.7.0.tar.gz) = 500752 diff --git a/pkgs/freebsd/devel/libkernaux/pkg-descr b/pkgs/freebsd/devel/libkernaux/pkg-descr new file mode 100644 index 0000000..01173d6 --- /dev/null +++ b/pkgs/freebsd/devel/libkernaux/pkg-descr @@ -0,0 +1,3 @@ +Auxiliary library for kernel development. + +It's in the early stage of the development so there is no detailed description. diff --git a/pkgs/freebsd/devel/libkernaux/pkg-plist b/pkgs/freebsd/devel/libkernaux/pkg-plist new file mode 100644 index 0000000..83fa1b1 --- /dev/null +++ b/pkgs/freebsd/devel/libkernaux/pkg-plist @@ -0,0 +1,43 @@ +include/kernaux.h +include/kernaux/arch/i386-idt.h +include/kernaux/arch/i386.h +include/kernaux/arch/riscv64.h +include/kernaux/arch/x86.h +include/kernaux/arch/x86_64.h +include/kernaux/asm/i386.h +include/kernaux/asm/riscv64.h +include/kernaux/asm/x86.h +include/kernaux/asm/x86_64.h +include/kernaux/cmdline.h +include/kernaux/elf.h +include/kernaux/free_list.h +include/kernaux/generic/display.h +include/kernaux/generic/malloc.h +include/kernaux/generic/mutex.h +include/kernaux/macro.h +include/kernaux/macro/packing_end.run +include/kernaux/macro/packing_start.run +include/kernaux/mbr.h +include/kernaux/memmap.h +include/kernaux/multiboot2.h +include/kernaux/multiboot2/header_enums.h +include/kernaux/multiboot2/header_helpers.h +include/kernaux/multiboot2/header_is_valid.h +include/kernaux/multiboot2/header_macro.h +include/kernaux/multiboot2/header_print.h +include/kernaux/multiboot2/info_enums.h +include/kernaux/multiboot2/info_helpers.h +include/kernaux/multiboot2/info_is_valid.h +include/kernaux/multiboot2/info_print.h +include/kernaux/ntoa.h +include/kernaux/pfa.h +include/kernaux/printf.h +include/kernaux/printf_fmt.h +include/kernaux/runtime.h +include/kernaux/units.h +include/kernaux/version.h +lib/libkernaux.a +lib/libkernaux.so +lib/libkernaux.so.0 +lib/libkernaux.so.0.0.0 +libdata/pkgconfig/libkernaux.pc diff --git a/sha256sums.txt b/sha256sums.txt new file mode 100644 index 0000000..466d17f --- /dev/null +++ b/sha256sums.txt @@ -0,0 +1,33 @@ +19bbfe5d38731175fd762547fc169adffde6350cf30c9238f931909b26decd85 libkernaux-0.7.0.tar.gz +ab54d7ed92e37ac9ae4982e297c4dcc49f9ae18e1be59631d175a33596d6cfcc kernaux-0.7.0.crate +15d41c87e270fa34a482df5cc243cdc87171ac513eb779f329fd4003d6df693a kernaux-sys-0.7.0.crate +0f3f97ad8244cbe2a0cdb8f8092f4f20d704281d91f0b41955270808f66aead4 kernaux-0.7.0.gem +# +16fc83a36826cad527ec0a232032b7b131b5be0468ccff9163df12e3e3986b9f libkernaux-0.6.1.tar.gz +e330f19d04d392e5c5979f3f4b88029b10ffe23fdf34f6d801947b20c8d0ba88 kernaux-0.6.1.crate +f21520d8d84558408ea444f0029dfca871fd3622513537837f86a97fc3c60648 kernaux-sys-0.6.1.crate +bfa7c9412830917526a563ac6a4402a579e5dba1a51666430069b8c9b5b8b428 kernaux-0.6.1.gem +# +6f32427f7cffdf11c0a11c26605b93e4bc2bdc6b02dd308d75d1e4aed88c6055 libkernaux-0.6.0.tar.gz +2734241f544ae5f5cceb72e9b61ac01b84013e6455c4adb4c0a74c4ac68f477c kernaux-0.6.0.crate +4cd9e098209d1f81cf9efeb334d8b5b9814d875e3ca7ea2e83741acf40d21477 kernaux-sys-0.6.0.crate +56b78efbb4714719c151c6b63dc708db28a5eed8c6ea60e7f96f8c9f355c65ef kernaux-0.6.0.gem +# +e18d88baa68295e57a05348f059bed105b14d6c6b52904db8c04c84cd70629ee libkernaux-0.5.0.tar.gz +c0bc0ed3c396893797912356366068ad04054ade23e293bbf61c72975165a3fc kernaux-0.5.0.crate +986b2f1b0befb8e1e1a6f97e67a64ab52974524eaa3f3948deef2dff28be946a kernaux-sys-0.5.0.crate +da5fbc5ad8d0d1f3c7a9c8627b59bb9c670893eed800f7b2190c81767f8bb70f kernaux-0.5.0.gem +# +893292882e0b658d92e104dc0d2974d61daca60efdf9fdf80996c3db54b0cab2 libkernaux-0.4.0.tar.gz +eb27d72d658f87638998cdb0056018edd49cc8f1144e23a1e00f7921a6bbd5b2 kernaux-0.4.0.crate +ee0d63d1f86113f3f652427b908da27fda7be496b5bdbba9947977891b2e50ef kernaux-sys-0.4.0.crate +2c765894b10830abc5945b003d465515fb28b964f1e2f7cdd5594bbf1cdbce97 kernaux-0.4.0.gem +# +6887939c01b65a4e864d47f103a06a6ad1639f4bffaa5582c95252c4646c8d2a libkernaux-0.3.0.tar.gz +564e5a58a854dd6b2b4e212ebac9c76a099971a2fa95324916de07b67f46934c kernaux-0.3.0.crate +1fddd0383dcce818273f305fffd0c7153d3772fcaf78dae38c30dae4f0960a25 kernaux-sys-0.3.0.crate +986b84827466d4ba7244c2e3ba41502a7984156346613af545946423f913d520 kernaux-0.3.0.gem +# +# Yanked: +00b22e28ecddde5beca8b7d425d91eaf3634ee1d9df0bcf9565938d02353cd49 libkernaux-0.2.0.tar.gz +b80c1d94519a43bd92c2b1a7626bdb5af5aa98dd993b2332d56449b0be7dbc8f libkernaux-0.1.0.tar.gz diff --git a/src/arch/i386/idt.c b/src/arch/i386/idt.c new file mode 100644 index 0000000..d604c15 --- /dev/null +++ b/src/arch/i386/idt.c @@ -0,0 +1,75 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../../assert.h" + +#include + +#include + +#define DPL (0x60u & (dpl << 5)) + +void KernAux_Arch_I386_IDTE_init_intr( + const KernAux_Arch_I386_IDTE idte, + const uint32_t offset, + const uint16_t cs_selector, + const uint8_t dpl +) { + KERNAUX_NOTNULL(idte); + + memset(idte, 0, sizeof(*idte)); + KernAux_Arch_I386_IDTE_set_offset(idte, offset); + idte->selector = cs_selector; + idte->flags |= 0x80u | DPL | 0xeu; // 1-00-01110 +} + +void KernAux_Arch_I386_IDTE_init_task( + const KernAux_Arch_I386_IDTE idte, + const uint16_t tss_selector, + const uint8_t dpl +) { + KERNAUX_NOTNULL(idte); + + memset(idte, 0, sizeof(*idte)); + idte->selector = tss_selector; + idte->flags |= 0x80u | DPL | 0x5u; // 1-00-00101 +} + +void KernAux_Arch_I386_IDTE_init_trap( + const KernAux_Arch_I386_IDTE idte, + const uint32_t offset, + const uint16_t cs_selector, + const uint8_t dpl +) { + KERNAUX_NOTNULL(idte); + + memset(idte, 0, sizeof(*idte)); + KernAux_Arch_I386_IDTE_set_offset(idte, offset); + idte->selector = cs_selector; + idte->flags |= 0x80u | DPL | 0xfu; // 1-00-01111 +} + +uint32_t KernAux_Arch_I386_IDTE_offset(const KernAux_Arch_I386_IDTE idte) +{ + KERNAUX_NOTNULL(idte); + + return (idte->offset_high << 16) | idte->offset_low; +} + +uint8_t KernAux_Arch_I386_IDTE_dpl(const KernAux_Arch_I386_IDTE idte) +{ + KERNAUX_NOTNULL(idte); + + return 3 & (idte->flags >> 5); +} + +void KernAux_Arch_I386_IDTE_set_offset( + const KernAux_Arch_I386_IDTE idte, + const uint32_t offset +) { + KERNAUX_NOTNULL(idte); + + idte->offset_low = 0xffffu & offset; + idte->offset_high = 0xffffu & (offset >> 16); +} diff --git a/src/asm/i386.S b/src/asm/i386.S new file mode 100644 index 0000000..71fba80 --- /dev/null +++ b/src/asm/i386.S @@ -0,0 +1,60 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +.global kernaux_asm_i386_flush_gdt +.global kernaux_asm_i386_flush_idt +.global kernaux_asm_i386_flush_tss +.global kernaux_asm_i386_read_cr0 +.global kernaux_asm_i386_read_cr4 +.global kernaux_asm_i386_write_cr0 +.global kernaux_asm_i386_write_cr3 +.global kernaux_asm_i386_write_cr4 + +kernaux_asm_i386_flush_gdt: + mov 4(%esp), %eax + mov 8(%esp), %edx + lgdt (%eax) + mov %edx, %ds + mov %edx, %es + mov %edx, %fs + mov %edx, %gs + mov %edx, %ss + pushl 12(%esp) + push $.flush + ljmp *(%esp) + .flush: + ret + +kernaux_asm_i386_flush_idt: + mov 4(%esp), %eax + lidt (%eax) + ret + +kernaux_asm_i386_flush_tss: + mov 4(%esp), %ax + ltr %ax + ret + +kernaux_asm_i386_read_cr0: + mov %cr0, %eax + ret + +kernaux_asm_i386_read_cr4: + mov %cr4, %eax + ret + +kernaux_asm_i386_write_cr0: + mov 4(%esp), %eax + mov %eax, %cr0 + ret + +kernaux_asm_i386_write_cr3: + mov 4(%esp), %eax + mov %eax, %cr3 + ret + +kernaux_asm_i386_write_cr4: + mov 4(%esp), %eax + mov %eax, %cr4 + ret diff --git a/src/asm/riscv64.S b/src/asm/riscv64.S new file mode 100644 index 0000000..c7cde56 --- /dev/null +++ b/src/asm/riscv64.S @@ -0,0 +1,3 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif diff --git a/src/asm/x86_64.S b/src/asm/x86_64.S new file mode 100644 index 0000000..c7cde56 --- /dev/null +++ b/src/asm/x86_64.S @@ -0,0 +1,3 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif diff --git a/src/assert.h b/src/assert.h new file mode 100644 index 0000000..b9816b4 --- /dev/null +++ b/src/assert.h @@ -0,0 +1,16 @@ +#ifndef KERNAUX_INCLUDED_ASSERT +#define KERNAUX_INCLUDED_ASSERT + +#include + +#ifdef ENABLE_ASSERT +#define KERNAUX_PANIC(msg) (kernaux_assert_do(__FILE__, __LINE__, msg)) +#define KERNAUX_ASSERT(cond) ((cond) ? (void)0 : KERNAUX_PANIC(#cond)) +#else +#define KERNAUX_PANIC(msg) ((void)0) +#define KERNAUX_ASSERT(cond) ((void)0) +#endif + +#define KERNAUX_NOTNULL(cond) KERNAUX_ASSERT(cond) + +#endif diff --git a/src/cmdline.c b/src/cmdline.c new file mode 100644 index 0000000..1552aa5 --- /dev/null +++ b/src/cmdline.c @@ -0,0 +1,291 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include + +#include +#include + +enum State { + INITIAL, + FINAL, + WHITESPACE, + TOKEN, + BACKSLASH, + QUOTE, + QUOTE_BACKSLASH, +}; + +static bool kernaux_cmdline_common( + const char *cmdline, + char *error_msg, + size_t *argc, + char arg_terminator, + char **argv, + char *buffer, + size_t *arg_idxs, + size_t arg_count_max, + size_t buffer_size +); + +static bool kernaux_cmdline_iter( + char cur, + enum State *state, + size_t *buffer_or_file_pos, + char *error_msg, + size_t *argc, + char arg_terminator, + char **argv, + char *buffer, + size_t *arg_idxs, + size_t arg_count_max, + size_t buffer_size +); + +/******************************* + * Implementations: public API * + *******************************/ + +bool kernaux_cmdline( + const char *const cmdline, + char *const error_msg, + size_t *const argc, + char **const argv, + char *const buffer, + const size_t arg_count_max, + const size_t buffer_size +) { + KERNAUX_NOTNULL(cmdline); + KERNAUX_NOTNULL(error_msg); + KERNAUX_ASSERT(argc); + KERNAUX_NOTNULL(argv); + KERNAUX_ASSERT(arg_count_max > 0); + KERNAUX_ASSERT(buffer_size > 0); + + return kernaux_cmdline_common( + cmdline, + error_msg, + argc, + '\0', // arg_terminator + argv, + buffer, + NULL, + arg_count_max, + buffer_size + ); +} + +/********************************* + * Implementation: main function * + *********************************/ + +#define CLEAR do { \ + *argc = 0; \ + if (argv) memset(argv, 0, sizeof(char*) * arg_count_max); \ + if (buffer) memset(buffer, '\0', buffer_size); \ + if (arg_idxs) memset(arg_idxs, 0, sizeof(size_t) * arg_count_max); \ +} while (0) + +bool kernaux_cmdline_common( + const char *const cmdline, + char *const error_msg, + size_t *const argc, + char arg_terminator, + char **const argv, + char *const buffer, + size_t *const arg_idxs, + const size_t arg_count_max, + const size_t buffer_size +) { + KERNAUX_NOTNULL(cmdline); + KERNAUX_NOTNULL(error_msg); + KERNAUX_ASSERT(argc); + (void)arg_idxs; + + memset(error_msg, '\0', KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX); + CLEAR; + + if (cmdline[0] == '\0') return true; + + enum State state = INITIAL; + size_t buffer_or_file_pos = 0; + + for (size_t index = 0; state != FINAL; ++index) { + const bool result = kernaux_cmdline_iter( + cmdline[index], + &state, + &buffer_or_file_pos, + error_msg, + argc, + arg_terminator, + argv, + buffer, + arg_idxs, + arg_count_max, + buffer_size + ); + if (!result) goto fail; + } + + return true; + +fail: + CLEAR; + return false; +} + +/************************************** + * Implementation: iteration function * + **************************************/ + +#define FAIL(msg) do { \ + strcpy(error_msg, msg); \ + return false; \ +} while (0) + +#define PUT_CHAR(char) do { \ + if (buffer_size && *buffer_or_file_pos >= buffer_size) { \ + FAIL("EOF or buffer overflow"); \ + } \ + if (buffer) { \ + buffer[*buffer_or_file_pos] = char; \ + } \ + ++(*buffer_or_file_pos); \ +} while (0) + +#define PUT_ARG do { \ + if (arg_count_max && *argc >= arg_count_max) { \ + FAIL("too many args"); \ + } \ + if (argv && buffer) { \ + argv[*argc] = &buffer[*buffer_or_file_pos]; \ + } \ + if (arg_idxs) { \ + arg_idxs[*argc] = *buffer_or_file_pos; \ + } \ + ++(*argc); \ +} while (0) + +#define PUT_ARG_AND_CHAR(char) do { \ + if (arg_count_max && *argc >= arg_count_max) { \ + FAIL("too many args"); \ + } \ + if (buffer_size && *buffer_or_file_pos >= buffer_size) { \ + FAIL("EOF or buffer overflow"); \ + } \ + if (argv && buffer) { \ + argv[*argc] = &buffer[*buffer_or_file_pos]; \ + buffer[*buffer_or_file_pos] = char; \ + } \ + if (arg_idxs) { \ + arg_idxs[*argc] = *buffer_or_file_pos; \ + } \ + ++(*argc); \ + ++(*buffer_or_file_pos); \ +} while (0) + +bool kernaux_cmdline_iter( + const char cur, + enum State *const state, + size_t *const buffer_or_file_pos, + char *const error_msg, + size_t *const argc, + char arg_terminator, + char **const argv, + char *const buffer, + size_t *const arg_idxs, + const size_t arg_count_max, + const size_t buffer_size +) { + switch (*state) { + case FINAL: + break; + + case INITIAL: + if (cur == '\0') { + *state = FINAL; + } else if (cur == ' ') { + *state = WHITESPACE; + } else if (cur == '\\') { + *state = BACKSLASH; + PUT_ARG; + } else if (cur == '"') { + *state = QUOTE; + PUT_ARG; + } else { + *state = TOKEN; + PUT_ARG_AND_CHAR(cur); + } + break; + + case WHITESPACE: + if (cur == '\0') { + *state = FINAL; + } else if (cur == ' ') { + // do nothing + } else if (cur == '\\') { + *state = BACKSLASH; + PUT_ARG; + } else if (cur == '"') { + *state = QUOTE; + PUT_ARG; + } else { + *state = TOKEN; + PUT_ARG_AND_CHAR(cur); + } + break; + + case TOKEN: + if (cur == '\0') { + *state = FINAL; + PUT_CHAR(arg_terminator); + } else if (cur == ' ') { + *state = WHITESPACE; + PUT_CHAR(arg_terminator); + } else if (cur == '\\') { + *state = BACKSLASH; + } else if (cur == '"') { + FAIL("unescaped quotation mark"); + } else { + PUT_CHAR(cur); + } + break; + + case BACKSLASH: + if (cur == '\0') { + FAIL("EOL after backslash"); + } else { + *state = TOKEN; + PUT_CHAR(cur); + } + break; + + case QUOTE: + if (cur == '\0') { + FAIL("EOL inside quote"); + } else if (cur == '\\') { + *state = QUOTE_BACKSLASH; + } else if (cur == '"') { + *state = WHITESPACE; + PUT_CHAR(arg_terminator); + } else { + PUT_CHAR(cur); + } + break; + + case QUOTE_BACKSLASH: + if (cur == '\0') { + FAIL("EOL after backslash inside quote"); + } else { + *state = QUOTE; + PUT_CHAR(cur); + } + break; + } + + return true; +} diff --git a/src/elf.c b/src/elf.c new file mode 100644 index 0000000..125cbd1 --- /dev/null +++ b/src/elf.c @@ -0,0 +1,48 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +const char *KernAux_ELF_Section_Type_to_str(const uint32_t type) +{ + switch (type) { + case KERNAUX_ELF_SECT_TYPE_NULL: + return "NULL"; + case KERNAUX_ELF_SECT_TYPE_PROGBITS: + return "PROGBITS"; + case KERNAUX_ELF_SECT_TYPE_SYMTAB: + return "SYMTAB"; + case KERNAUX_ELF_SECT_TYPE_STRTAB: + return "STRTAB"; + case KERNAUX_ELF_SECT_TYPE_RELA: + return "RELA"; + case KERNAUX_ELF_SECT_TYPE_HASH: + return "HASH"; + case KERNAUX_ELF_SECT_TYPE_DYNAMIC: + return "DYNAMIC"; + case KERNAUX_ELF_SECT_TYPE_NOTE: + return "NOTE"; + case KERNAUX_ELF_SECT_TYPE_NOBITS: + return "NOBITS"; + case KERNAUX_ELF_SECT_TYPE_REL: + return "REL"; + case KERNAUX_ELF_SECT_TYPE_SHLIB: + return "SHLIB"; + case KERNAUX_ELF_SECT_TYPE_DYNSYM: + return "DYNSYM"; + case KERNAUX_ELF_SECT_TYPE_LOPROC: + return "LOPROC"; + case KERNAUX_ELF_SECT_TYPE_HIPROC: + return "HIPROC"; + case KERNAUX_ELF_SECT_TYPE_LOUSER: + return "LOUSER"; + case KERNAUX_ELF_SECT_TYPE_HIUSER: + return "HIUSER"; + default: + return NULL; + } +} diff --git a/src/free_list.c b/src/free_list.c new file mode 100644 index 0000000..378b522 --- /dev/null +++ b/src/free_list.c @@ -0,0 +1,297 @@ +/** + * The code was inspired by the Embedded Artistry's libmemory. + * + * Copyright (c) 2017-2022 Embedded Artistry LLC + * Copyright (c) 2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define NODE_HEADER_SIZE (offsetof(struct KernAux_FreeList_Node, block)) +#define MIN_ZONE_SIZE (2 * NODE_HEADER_SIZE) +#define MIN_SPLIT_SIZE (NODE_HEADER_SIZE + 16) + +#define ALIGN_MASK(align) ((align) - 1) // align should be a power of 2 +#define ALIGN_UP(val, align) (((val) + ALIGN_MASK(align)) & ~ALIGN_MASK(align)) + +#define PTR_ALIGNMENT (sizeof(void*)) + +#define LOCK(free_list) \ + do { \ + if ((free_list)->mutex) { \ + KernAux_Mutex_lock((free_list)->mutex); \ + } \ + } while (0) + +#define UNLOCK(free_list) \ + do { \ + if ((free_list)->mutex) { \ + KernAux_Mutex_unlock((free_list)->mutex); \ + } \ + } while (0) + +static void KernAux_FreeList_free (void *malloc, void *ptr); +static void *KernAux_FreeList_malloc (void *malloc, size_t size); +static void *KernAux_FreeList_realloc(void *malloc, void *ptr, size_t size); + +static void KernAux_FreeList_defrag(KernAux_FreeList free_list); +static void KernAux_FreeList_insert( + KernAux_FreeList free_list, + KernAux_FreeList_Node node, + KernAux_FreeList_Node prev, + KernAux_FreeList_Node next +); +static void KernAux_FreeList_remove( + KernAux_FreeList free_list, + KernAux_FreeList_Node node +); + +struct KernAux_FreeList KernAux_FreeList_create(const KernAux_Mutex mutex) +{ + struct KernAux_FreeList free_list; + KernAux_FreeList_init(&free_list, mutex); + return free_list; +} + +void KernAux_FreeList_init( + const KernAux_FreeList free_list, + const KernAux_Mutex mutex +) { + KERNAUX_NOTNULL(free_list); + + free_list->malloc.calloc = NULL; + free_list->malloc.free = KernAux_FreeList_free; + free_list->malloc.malloc = KernAux_FreeList_malloc; + free_list->malloc.realloc = KernAux_FreeList_realloc; + free_list->mutex = mutex; + free_list->head = NULL; +} + +void KernAux_FreeList_add_zone( + const KernAux_FreeList free_list, + void *const ptr, + const size_t size +) { + KERNAUX_NOTNULL(free_list); + KERNAUX_NOTNULL(ptr); + KERNAUX_ASSERT(size >= MIN_ZONE_SIZE); + + LOCK(free_list); + + KernAux_FreeList_Node new_node = + (KernAux_FreeList_Node)ALIGN_UP((uintptr_t)ptr, PTR_ALIGNMENT); + new_node->orig_ptr = ptr; + new_node->size = size; + + KernAux_FreeList_Node prev_node = NULL; + KernAux_FreeList_Node next_node; + KernAux_FreeList_Node last_node = NULL; + + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + last_node = item_node; + + if (item_node > new_node) { + next_node = item_node; + if (item_node->prev) prev_node = item_node->prev; + goto block_found; + } + } + + prev_node = last_node; + next_node = NULL; + +block_found: + KernAux_FreeList_insert(free_list, new_node, prev_node, next_node); + KernAux_FreeList_defrag(free_list); + + UNLOCK(free_list); +} + +void KernAux_FreeList_free(void *const malloc, void *const ptr) +{ + const KernAux_FreeList free_list = malloc; + KERNAUX_NOTNULL(free_list); + KERNAUX_NOTNULL(ptr); + + LOCK(free_list); + + KernAux_FreeList_Node node = + KERNAUX_CONTAINER_OF(ptr, struct KernAux_FreeList_Node, block); + KernAux_FreeList_Node last_node = NULL; + + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + last_node = item_node; + + if (item_node > node) { + KernAux_FreeList_insert( + free_list, + node, + item_node->prev, + item_node + ); + goto block_added; + } + } + + KernAux_FreeList_insert(free_list, node, last_node, NULL); + +block_added: + KernAux_FreeList_defrag(free_list); + + UNLOCK(free_list); +} + +void *KernAux_FreeList_malloc(void *const malloc, size_t size) +{ + const KernAux_FreeList free_list = malloc; + KERNAUX_NOTNULL(free_list); + KERNAUX_ASSERT(size); + + LOCK(free_list); + + size = ALIGN_UP(size, PTR_ALIGNMENT); + + KernAux_FreeList_Node node = NULL; + + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + if (item_node->size - NODE_HEADER_SIZE >= size) { + node = item_node; + break; + } + } + + if (node) { + // Can we split the block? + if (node->size - size >= MIN_SPLIT_SIZE) { + KernAux_FreeList_Node new_node = + (KernAux_FreeList_Node)(((uintptr_t)&node->block) + size); + + KERNAUX_ASSERT( + ((uintptr_t)new_node) + == + ALIGN_UP((uintptr_t)new_node, PTR_ALIGNMENT) + ); + + new_node->orig_ptr = new_node; + new_node->size = node->size - size - NODE_HEADER_SIZE; + node->size = NODE_HEADER_SIZE + size; + KernAux_FreeList_insert(free_list, new_node, node, node->next); + } + + KernAux_FreeList_remove(free_list, node); + } + + UNLOCK(free_list); + + if (node) { + return &node->block; + } else { + return NULL; + } +} + +void *KernAux_FreeList_realloc( + void *const malloc, + void *const old_ptr, + const size_t new_size +) { + const KernAux_FreeList free_list = malloc; + KERNAUX_NOTNULL(free_list); + KERNAUX_NOTNULL(old_ptr); + KERNAUX_ASSERT(new_size); + + LOCK(free_list); + + KernAux_FreeList_Node node = + KERNAUX_CONTAINER_OF(old_ptr, struct KernAux_FreeList_Node, block); + const size_t old_size = node->size - NODE_HEADER_SIZE; + + void *new_ptr = KernAux_FreeList_malloc(free_list, new_size); + + if (new_ptr) { + const size_t min_size = old_size < new_size ? old_size : new_size; + memcpy(new_ptr, old_ptr, min_size); + } + + UNLOCK(free_list); + return new_ptr; +} + +void KernAux_FreeList_defrag(const KernAux_FreeList free_list) +{ + KERNAUX_NOTNULL(free_list); + + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + const KernAux_FreeList_Node node = item_node->prev; + if (!node) continue; + if (((uintptr_t)node) + node->size != (uintptr_t)item_node->orig_ptr) { + continue; + } + + node->size += item_node->size; + KernAux_FreeList_remove(free_list, item_node); + } +} + +void KernAux_FreeList_insert( + const KernAux_FreeList free_list, + const KernAux_FreeList_Node node, + const KernAux_FreeList_Node prev, + const KernAux_FreeList_Node next +) { + KERNAUX_NOTNULL(free_list); + KERNAUX_NOTNULL(node); + KERNAUX_ASSERT(node != prev); + KERNAUX_ASSERT(node != next); + KERNAUX_ASSERT(!prev || prev->next == next); + KERNAUX_ASSERT(!next || next->prev == prev); + + if (!prev) free_list->head = node; + node->next = next; + node->prev = prev; + if (node->next) node->next->prev = node; + if (node->prev) node->prev->next = node; +} + +void KernAux_FreeList_remove( + const KernAux_FreeList free_list, + const KernAux_FreeList_Node node +) { + KERNAUX_NOTNULL(free_list); + KERNAUX_NOTNULL(node); + KERNAUX_ASSERT(!node->next || node->next->prev == node); + KERNAUX_ASSERT(!node->prev || node->prev->next == node); + + if (free_list->head == node) free_list->head = node->next; + if (node->next) node->next->prev = node->prev; + if (node->prev) node->prev->next = node->next; +} diff --git a/src/generic/display.c b/src/generic/display.c new file mode 100644 index 0000000..0fe5d3f --- /dev/null +++ b/src/generic/display.c @@ -0,0 +1,116 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include + +void KernAux_Display_putc(const KernAux_Display display, const char c) +{ + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Inherited implementation + display->putc((void*)display, c); +} + +void KernAux_Display_print(const KernAux_Display display, const char *const s) +{ + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Default implementation + for (const char *c = s; *c; ++c) display->putc((void*)display, *c); +} + +void KernAux_Display_println(const KernAux_Display display, const char *const s) +{ + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Default implementation + KernAux_Display_print(display, s); + display->putc((void*)display, '\n'); +} + +void KernAux_Display_write( + const KernAux_Display display, + const char *const data, + const size_t size +) { + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Default implementation + for (size_t i = 0; i < size; ++i) display->putc((void*)display, data[i]); +} + +void KernAux_Display_writeln( + const KernAux_Display display, + const char *const data, + const size_t size +) { + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Default implementation + KernAux_Display_write(display, data, size); + display->putc((void*)display, '\n'); +} + +void KernAux_Display_printf( + const KernAux_Display display, + const char *const format, + ... +) { + KERNAUX_NOTNULL(display); + + // Default implementation + va_list va; + va_start(va, format); + KernAux_Display_vprintf(display, format, va); + va_end(va); +} + +void KernAux_Display_printlnf( + const KernAux_Display display, + const char *const format, + ... +) { + KERNAUX_NOTNULL(display); + + // Default implementation + va_list va; + va_start(va, format); + KernAux_Display_vprintlnf(display, format, va); + va_end(va); +} + +void KernAux_Display_vprintf( + const KernAux_Display display, + const char *const format, + va_list va +) { + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->vprintf); + + // Inherited implementation + display->vprintf((void*)display, format, va); +} + +void KernAux_Display_vprintlnf( + const KernAux_Display display, + const char *const format, + va_list va +) { + KERNAUX_NOTNULL(display); + KERNAUX_ASSERT(display->putc); + + // Default implementation + KernAux_Display_vprintf(display, format, va); + display->putc((void*)display, '\n'); +} diff --git a/src/generic/malloc.c b/src/generic/malloc.c new file mode 100644 index 0000000..f85b5f4 --- /dev/null +++ b/src/generic/malloc.c @@ -0,0 +1,68 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include + +void *KernAux_Malloc_calloc(KernAux_Malloc malloc, size_t nmemb, size_t size) +{ + KERNAUX_NOTNULL(malloc); + + // Common implementation + const size_t total_size = nmemb * size; + if (!total_size) return NULL; + if (total_size / nmemb != size) return NULL; + + // Inherited implementation + if (malloc->calloc) return malloc->calloc((void*)malloc, nmemb, size); + + // Default implementation + void *const ptr = KernAux_Malloc_malloc(malloc, total_size); + if (ptr) memset(ptr, 0, total_size); + return ptr; +} + +void KernAux_Malloc_free(KernAux_Malloc malloc, void *ptr) +{ + KERNAUX_NOTNULL(malloc); + KERNAUX_ASSERT(malloc->free); + + // Common implementation + if (!ptr) return; + + // Inherited implementation + malloc->free((void*)malloc, ptr); +} + +void *KernAux_Malloc_malloc(KernAux_Malloc malloc, size_t size) +{ + KERNAUX_NOTNULL(malloc); + KERNAUX_ASSERT(malloc->malloc); + + // Common implementation + if (!size) return NULL; + + // Inherited implementation + return malloc->malloc((void*)malloc, size); +} + +void *KernAux_Malloc_realloc(KernAux_Malloc malloc, void *ptr, size_t size) +{ + KERNAUX_NOTNULL(malloc); + KERNAUX_ASSERT(malloc->realloc); + + // Common implementation + if (!ptr) return KernAux_Malloc_malloc(malloc, size); + if (!size) { + KernAux_Malloc_free(malloc, ptr); + return NULL; + } + + // Inherited implementation + return malloc->realloc((void*)malloc, ptr, size); +} diff --git a/src/generic/mutex.c b/src/generic/mutex.c new file mode 100644 index 0000000..3729634 --- /dev/null +++ b/src/generic/mutex.c @@ -0,0 +1,23 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +void KernAux_Mutex_lock(const KernAux_Mutex mutex) +{ + KERNAUX_NOTNULL(mutex); + KERNAUX_ASSERT(mutex->lock); + + mutex->lock((void*)mutex); +} + +void KernAux_Mutex_unlock(const KernAux_Mutex mutex) +{ + KERNAUX_NOTNULL(mutex); + KERNAUX_ASSERT(mutex->unlock); + + mutex->unlock((void*)mutex); +} diff --git a/src/mbr.c b/src/mbr.c new file mode 100644 index 0000000..c505117 --- /dev/null +++ b/src/mbr.c @@ -0,0 +1,41 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include + +#include +#include + +bool KernAux_Mbr_is_valid(const struct KernAux_Mbr *const mbr) +{ + KERNAUX_NOTNULL(mbr); + + return KernAux_Mbr_Info_is_valid(&mbr->info); +} + +bool KernAux_Mbr_Info_is_valid(const struct KernAux_Mbr_Info *const mbr_info) +{ + KERNAUX_NOTNULL(mbr_info); + + if (mbr_info->magic != KERNAUX_MBR_MAGIC) return false; + + for (size_t index = 0; index < KERNAUX_MBR_ENTRIES; ++index) { + if (!KernAux_Mbr_Entry_is_valid(&mbr_info->entries[index])) { + return false; + } + } + + return true; +} + +bool KernAux_Mbr_Entry_is_valid(const struct KernAux_Mbr_Entry *const mbr_entry) +{ + KERNAUX_NOTNULL(mbr_entry); + + // TODO: implement this + (void)mbr_entry; + return true; +} diff --git a/src/memmap.c b/src/memmap.c new file mode 100644 index 0000000..46f9fba --- /dev/null +++ b/src/memmap.c @@ -0,0 +1,258 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void free_node(KernAux_Malloc malloc, struct KernAux_Memmap_Node *node); +static void print_nodes( + KernAux_Memmap_Node node, + KernAux_Display display, + unsigned indentation +); + +KernAux_Memmap_Builder +KernAux_Memmap_Builder_new(const KernAux_Malloc malloc) +{ + KERNAUX_NOTNULL(malloc); + + struct KernAux_Memmap_Builder *const builder = + KernAux_Malloc_malloc(malloc, sizeof(*builder)); + if (!builder) { + return NULL; + } + + struct KernAux_Memmap *const memmap = + KernAux_Malloc_malloc(malloc, sizeof(*memmap)); + if (!memmap) { + KernAux_Malloc_free(malloc, builder); + return NULL; + } + + struct KernAux_Memmap_Node *const root_node = + KernAux_Malloc_malloc(malloc, sizeof(*root_node)); + if (!root_node) { + KernAux_Malloc_free(malloc, memmap); + KernAux_Malloc_free(malloc, builder); + return NULL; + } + + *root_node = (struct KernAux_Memmap_Node){ + .mem_start = 0x0, + .mem_end = 0xffffffffffffffff, // 2**64 - 1 + .mem_size = 0xffffffffffffffff, // 2**64 - 1 + .tag = NULL, + .next = NULL, + .children = NULL, + }; + *memmap = (struct KernAux_Memmap){ + .malloc = malloc, + .root_node = root_node, + }; + *builder = (struct KernAux_Memmap_Builder){ + .memmap = memmap, + }; + + return builder; +} + +KernAux_Memmap_Node KernAux_Memmap_Builder_add( + const KernAux_Memmap_Builder builder, + KernAux_Memmap_Node parent_node, + const uint64_t mem_start, + const uint64_t mem_size, + const char *tag +) { + KERNAUX_NOTNULL(builder); + KERNAUX_ASSERT(builder->memmap); + KERNAUX_ASSERT(builder->memmap->root_node); + KERNAUX_ASSERT(builder->memmap->malloc); + + if (mem_size == 0) goto fail; + + char *tag_copy = NULL; + if (tag) { + tag_copy = + KernAux_Malloc_malloc(builder->memmap->malloc, strlen(tag) + 1); + if (!tag_copy) goto fail; + strcpy(tag_copy, tag); + } + + struct KernAux_Memmap_Node *const new_node = + KernAux_Malloc_malloc(builder->memmap->malloc, sizeof(*new_node)); + if (!new_node) goto fail_after_tag; + + new_node->mem_start = mem_start; + new_node->mem_size = mem_size; + new_node->mem_end = mem_start + mem_size - 1; + new_node->tag = tag_copy; + + if (!parent_node) { + parent_node = (struct KernAux_Memmap_Node*)builder->memmap->root_node; + } + + if (new_node->mem_start < parent_node->mem_start || + new_node->mem_end > parent_node->mem_end) + { + goto fail_after_new_node; + } + + if (parent_node->children) { + for ( + struct KernAux_Memmap_Node *curr_node = + (struct KernAux_Memmap_Node*)parent_node->children; + curr_node; + curr_node = (struct KernAux_Memmap_Node*)curr_node->next + ) { + if (!curr_node->next || + curr_node->next->mem_start > new_node->mem_start) + { + if (new_node->next && + new_node->mem_end >= new_node->next->mem_start) + { + goto fail_after_new_node; + } + new_node->next = curr_node->next; + curr_node->next = new_node; + break; + } + } + } else { + new_node->next = NULL; + ((struct KernAux_Memmap_Node*)parent_node)->children = new_node; + } + + return new_node; + +fail_after_new_node: + KernAux_Malloc_free(builder->memmap->malloc, new_node); +fail_after_tag: + if (tag_copy) KernAux_Malloc_free(builder->memmap->malloc, tag_copy); +fail: + return NULL; +} + +KernAux_Memmap +KernAux_Memmap_Builder_finish_and_free(const KernAux_Memmap_Builder builder) +{ + KERNAUX_NOTNULL(builder); + KERNAUX_ASSERT(builder->memmap); + KERNAUX_ASSERT(builder->memmap->root_node); + KERNAUX_ASSERT(builder->memmap->malloc); + + KernAux_Memmap memmap = builder->memmap; + builder->memmap = NULL; + KernAux_Malloc_free(memmap->malloc, builder); + return memmap; +} + +void KernAux_Memmap_free(const KernAux_Memmap memmap) +{ + KERNAUX_NOTNULL(memmap); + KERNAUX_ASSERT(memmap->root_node); + KERNAUX_ASSERT(memmap->malloc); + KERNAUX_ASSERT(memmap->root_node->next == NULL); + + free_node(memmap->malloc, (struct KernAux_Memmap_Node*)memmap->root_node); + + KernAux_Malloc malloc = memmap->malloc; + + ((struct KernAux_Memmap*)memmap)->root_node = NULL; + ((struct KernAux_Memmap*)memmap)->malloc = NULL; + + KernAux_Malloc_free(malloc, (void*)memmap); +} + +void KernAux_Memmap_print( + const KernAux_Memmap memmap, + const KernAux_Display display +) { + KERNAUX_NOTNULL(memmap); + KERNAUX_ASSERT(memmap->root_node); + KERNAUX_ASSERT(memmap->malloc); + KERNAUX_ASSERT(memmap->root_node->next == NULL); + + print_nodes(memmap->root_node, display, 0); +} + +void free_node( + const KernAux_Malloc malloc, + struct KernAux_Memmap_Node *const node +) { + KERNAUX_NOTNULL(malloc); + KERNAUX_NOTNULL(node); + + for ( + struct KernAux_Memmap_Node *child_node = + (struct KernAux_Memmap_Node*)node->children; + child_node; + child_node = (struct KernAux_Memmap_Node*)child_node->next + ) { + free_node(malloc, child_node); + } + + if (node->tag) KernAux_Malloc_free(malloc, (void*)node->tag); + KernAux_Malloc_free(malloc, node); +} + +#define PRINT(s) do { KernAux_Display_print (display, s); } while (0) +#define PRINTLN(s) do { KernAux_Display_println(display, s); } while (0) + +#define PRINTLNF(format, ...) \ + do { KernAux_Display_printlnf(display, format, __VA_ARGS__); } while (0) + +#define INDENT do { \ + for (unsigned index = 0; index < indentation; ++index) PRINT(" "); \ +} while (0) + +void print_nodes( + KernAux_Memmap_Node node, + const KernAux_Display display, + const unsigned indentation +) { + for (; node; node = node->next) { + INDENT; + PRINTLN("{"); + + KERNAUX_CAST_CONST(unsigned long long, mem_start, node->mem_start); + KERNAUX_CAST_CONST(unsigned long long, mem_size, node->mem_size); + KERNAUX_CAST_CONST(unsigned long long, mem_end, node->mem_end); + + INDENT; + PRINTLNF(" u64 mem_start: 0x%llx", mem_start); + INDENT; + PRINTLNF(" u64 mem_size: %llu", mem_size); + INDENT; + PRINTLNF(" u64 mem_end: 0x%llx", mem_end); + INDENT; + if (node->tag) { + PRINTLNF(" char* tag: \"%s\"", node->tag); + } else { + PRINTLN(" char* tag: NULL"); + } + + if (node->children) { + INDENT; + PRINTLN(" struct* children: ["); + print_nodes(node->children, display, indentation + 2); + INDENT; + PRINTLN(" ]"); + } else { + INDENT; + PRINTLN(" struct* children: []"); + } + + INDENT; + PRINTLN("}"); + } +} diff --git a/src/multiboot2/header_enums.c b/src/multiboot2/header_enums.c new file mode 100644 index 0000000..e0f1326 --- /dev/null +++ b/src/multiboot2/header_enums.c @@ -0,0 +1,65 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +const char *KernAux_Multiboot2_Header_Arch_to_str(const uint32_t arch) +{ + switch (arch) { + case KERNAUX_MULTIBOOT2_HEADER_ARCH_I386: + return "i386"; + case KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32: + return "MIPS32"; + default: + return NULL; + } +} + +const char *KernAux_Multiboot2_HTag_to_str(const uint16_t tag_type) +{ + switch (tag_type) { + case KERNAUX_MULTIBOOT2_HTAG_NONE: + return "none"; + case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ: + return "information request"; + case KERNAUX_MULTIBOOT2_HTAG_ADDR: + return "address"; + case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR: + return "entry address"; + case KERNAUX_MULTIBOOT2_HTAG_FLAGS: + return "flags"; + case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER: + return "framebuffer"; + case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN: + return "module alignment"; + case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES: + return "EFI boot services"; + case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR: + return "EFI i386 entry address"; + case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR: + return "EFI amd64 entry address"; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER: + return "relocatable header"; + default: + return NULL; + } +} + +const char* +KernAux_Multiboot2_HTag_RelocatableHeader_Preference_to_str(uint32_t pref) +{ + switch (pref) { + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE: + return "none"; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST: + return "lowest"; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST: + return "highest"; + default: + return NULL; + } +} diff --git a/src/multiboot2/header_helpers.c b/src/multiboot2/header_helpers.c new file mode 100644 index 0000000..20d3f64 --- /dev/null +++ b/src/multiboot2/header_helpers.c @@ -0,0 +1,62 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include + +const struct KernAux_Multiboot2_HTagBase +*KernAux_Multiboot2_Header_first_tag_with_type( + const struct KernAux_Multiboot2_Header *const multiboot2_header, + const uint16_t tag_type +) { + KERNAUX_NOTNULL(multiboot2_header); + + const struct KernAux_Multiboot2_HTagBase *tag_base = + (struct KernAux_Multiboot2_HTagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_header); + + while (tag_base < + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)multiboot2_header + multiboot2_header->total_size)) + { + if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return NULL; + if (tag_base->type == tag_type) return tag_base; + + tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base); + } + + return NULL; +} + +const struct KernAux_Multiboot2_HTagBase +*KernAux_Multiboot2_Header_tag_with_type_after( + const struct KernAux_Multiboot2_Header *const multiboot2_header, + const uint16_t tag_type, + const struct KernAux_Multiboot2_HTagBase *const after_tag +) { + KERNAUX_NOTNULL(multiboot2_header); + KERNAUX_NOTNULL(after_tag); + + const struct KernAux_Multiboot2_HTagBase *tag_base = + (struct KernAux_Multiboot2_HTagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_header); + + KERNAUX_ASSERT(tag_base <= after_tag); + + while (tag_base < + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)multiboot2_header + multiboot2_header->total_size)) + { + if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return NULL; + if (tag_base->type == tag_type && tag_base > after_tag) return tag_base; + + tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base); + } + + return NULL; +} diff --git a/src/multiboot2/header_is_valid.c b/src/multiboot2/header_is_valid.c new file mode 100644 index 0000000..bb0fff1 --- /dev/null +++ b/src/multiboot2/header_is_valid.c @@ -0,0 +1,265 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include +#include + +bool KernAux_Multiboot2_Header_is_valid( + const struct KernAux_Multiboot2_Header *const multiboot2_header +) { + KERNAUX_NOTNULL(multiboot2_header); + + if (multiboot2_header->magic != KERNAUX_MULTIBOOT2_HEADER_MAGIC) { + return false; + } + + if (multiboot2_header->arch != KERNAUX_MULTIBOOT2_HEADER_ARCH_I386 && + multiboot2_header->arch != KERNAUX_MULTIBOOT2_HEADER_ARCH_MIPS32) + { + return false; + } + + if (multiboot2_header->total_size < + sizeof(struct KernAux_Multiboot2_Header) + + sizeof(struct KernAux_Multiboot2_HTag_None)) + { + return false; + } + + if (multiboot2_header->total_size % KERNAUX_MULTIBOOT2_TAG_ALIGN != 0) { + return false; + } + + if (multiboot2_header->checksum != + KERNAUX_MULTIBOOT2_HEADER_CHECKSUM( + multiboot2_header->arch, + multiboot2_header->total_size + ) + ) { + return false; + } + + const struct KernAux_Multiboot2_HTagBase *tag_base = + (struct KernAux_Multiboot2_HTagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_header); + + const struct KernAux_Multiboot2_HTagBase *none_tag_base = NULL; + + while (tag_base < + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)multiboot2_header + multiboot2_header->total_size)) + { + if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return false; + + if (tag_base->type == KERNAUX_MULTIBOOT2_HTAG_NONE && + none_tag_base == NULL + ) { + none_tag_base = tag_base; + } + + tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base); + } + + if (tag_base != + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)multiboot2_header + multiboot2_header->total_size)) + { + return false; + } + + if (none_tag_base != + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)tag_base - + sizeof(struct KernAux_Multiboot2_HTag_None))) + { + return false; + } + + return true; +} + +bool KernAux_Multiboot2_HTagBase_is_valid( + const struct KernAux_Multiboot2_HTagBase *tag_base +) { + KERNAUX_NOTNULL(tag_base); + + switch (tag_base->type) { + case KERNAUX_MULTIBOOT2_HTAG_NONE: + return KernAux_Multiboot2_HTag_None_is_valid( + (struct KernAux_Multiboot2_HTag_None*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ: + return KernAux_Multiboot2_HTag_InfoReq_is_valid( + (struct KernAux_Multiboot2_HTag_InfoReq*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_ADDR: + return KernAux_Multiboot2_HTag_Addr_is_valid( + (struct KernAux_Multiboot2_HTag_Addr*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR: + return KernAux_Multiboot2_HTag_EntryAddr_is_valid( + (struct KernAux_Multiboot2_HTag_EntryAddr*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_FLAGS: + return KernAux_Multiboot2_HTag_Flags_is_valid( + (struct KernAux_Multiboot2_HTag_Flags*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER: + return KernAux_Multiboot2_HTag_Framebuffer_is_valid( + (struct KernAux_Multiboot2_HTag_Framebuffer*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN: + return KernAux_Multiboot2_HTag_ModuleAlign_is_valid( + (struct KernAux_Multiboot2_HTag_ModuleAlign*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES: + return KernAux_Multiboot2_HTag_EFIBootServices_is_valid( + (struct KernAux_Multiboot2_HTag_EFIBootServices*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR: + return KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid( + (struct KernAux_Multiboot2_HTag_EFII386EntryAddr*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR: + return KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid( + (struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr*)tag_base + ); + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER: + return KernAux_Multiboot2_HTag_RelocatableHeader_is_valid( + (struct KernAux_Multiboot2_HTag_RelocatableHeader*)tag_base + ); + default: + return false; + } +} + +bool KernAux_Multiboot2_HTag_None_is_valid( + const struct KernAux_Multiboot2_HTag_None *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_NONE && + tag->base.size == 8 + ); +} + +bool KernAux_Multiboot2_HTag_InfoReq_is_valid( + const struct KernAux_Multiboot2_HTag_InfoReq *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_INFO_REQ && + tag->base.size > 8 && + tag->base.size % 4 == 0 + ); +} + +bool KernAux_Multiboot2_HTag_Addr_is_valid( + const struct KernAux_Multiboot2_HTag_Addr *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_ADDR && + tag->base.size == 24 + ); +} + +bool KernAux_Multiboot2_HTag_EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EntryAddr *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_HTag_Flags_is_valid( + const struct KernAux_Multiboot2_HTag_Flags *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_FLAGS && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_HTag_Framebuffer_is_valid( + const struct KernAux_Multiboot2_HTag_Framebuffer *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER && + tag->base.size == 20 + ); +} + +bool KernAux_Multiboot2_HTag_ModuleAlign_is_valid( + const struct KernAux_Multiboot2_HTag_ModuleAlign *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN && + tag->base.size == 8 + ); +} + +bool KernAux_Multiboot2_HTag_EFIBootServices_is_valid( + const struct KernAux_Multiboot2_HTag_EFIBootServices *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES && + tag->base.size == 8 + ); +} + +bool KernAux_Multiboot2_HTag_EFII386EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_is_valid( + const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_HTag_RelocatableHeader_is_valid( + const struct KernAux_Multiboot2_HTag_RelocatableHeader *tag +) { + KERNAUX_NOTNULL(tag); + + if (!( + tag->base.type == KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER && + tag->base.size == 24 && + tag->min_addr <= tag->max_addr + )) { + return false; + } + + switch (tag->preference) { + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_NONE: break; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_LOWEST: break; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER_PREFERENCE_HIGHEST: break; + default: + return false; + } + + return true; +} diff --git a/src/multiboot2/header_print.c b/src/multiboot2/header_print.c new file mode 100644 index 0000000..af9b231 --- /dev/null +++ b/src/multiboot2/header_print.c @@ -0,0 +1,473 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include +#include +#include + +#include +#include +#include + +#define PRINT(s) do { KernAux_Display_print (display, s); } while (0) +#define PRINTLN(s) do { KernAux_Display_println(display, s); } while (0) + +#define PRINTF(format, ...) \ + do { KernAux_Display_printf (display, format, __VA_ARGS__); } while (0) +#define PRINTLNF(format, ...) \ + do { KernAux_Display_printlnf(display, format, __VA_ARGS__); } while (0) + +#define HEADER(Type) do { \ + KERNAUX_NOTNULL(tag); \ + KERNAUX_NOTNULL(display); \ + \ + if (!KernAux_Multiboot2_HTag_##Type##_is_valid(tag)) { \ + PRINTLN("Multiboot 2 header tag // invalid!"); \ + } \ + \ + KERNAUX_CAST_CONST(unsigned long, type, tag->base.type); \ + KERNAUX_CAST_CONST(unsigned long, size, tag->base.size); \ + \ + PRINTLN ("Multiboot 2 header tag {"); \ + PRINTLNF(" u16 type: %lu (%s)", \ + type, \ + KernAux_Multiboot2_HTag_to_str(tag->base.type) \ + ); \ + PRINT (" u16 flags: "); \ + KernAux_Multiboot2_HTagBase_Flags_print( \ + tag->base.flags, \ + display, \ + 2, \ + 2, \ + false \ + ); \ + PRINTLNF(" u32 size: %lu", size); \ +} while (0) + +#define FOOTER do { PRINTLN("}"); } while (0) + +#define INDENT do { \ + for (unsigned index = 0; index < basic_indentation; ++index) PRINT(" "); \ +} while (0) + +#define INDENT_MORE do { \ + for (unsigned index = 0; index < indentation_delta; ++index) PRINT(" "); \ +} while (0) + +static const struct { + uint32_t number; + const char *name; +} base_flag_names[] = { + { + .number = KERNAUX_MULTIBOOT2_HTAG_BASE_FLAG_OPTIONAL, + .name = "OPTIONAL", + }, +}; + +static const struct { + uint32_t number; + const char *name; +} console_flag_names[] = { + { + .number = KERNAUX_MULTIBOOT2_HTAG_FLAGS_REQUIRE_CONSOLE, + .name = "REQUIRE_CONSOLE", + }, + { + .number = KERNAUX_MULTIBOOT2_HTAG_FLAGS_EGA_SUPPORT, + .name = "EGA_SUPPORT", + } +}; + +static void KernAux_Multiboot2_HTagBase_Flags_print( + uint16_t flags, + KernAux_Display display, + unsigned basic_indentation, + unsigned indentation_delta, + bool indent_first +); + +static void KernAux_Multiboot2_HTag_Flags_ConsoleFlags_print( + uint32_t console_flags, + KernAux_Display display, + unsigned basic_indentation, + unsigned indentation_delta, + bool indent_first +); + +void KernAux_Multiboot2_Header_print( + const struct KernAux_Multiboot2_Header *const multiboot2_header, + const KernAux_Display display +) { + KERNAUX_NOTNULL(multiboot2_header); + KERNAUX_NOTNULL(display); + + KERNAUX_CAST_CONST(unsigned long, magic, multiboot2_header->magic); + KERNAUX_CAST_CONST(unsigned long, arch, multiboot2_header->arch); + KERNAUX_CAST_CONST(unsigned long, total_size, multiboot2_header->total_size); + KERNAUX_CAST_CONST(unsigned long, checksum, multiboot2_header->checksum); + + PRINTLN("Multiboot 2 header {"); + PRINTLNF(" u32 magic: 0x%lx", magic); + PRINTLNF(" u32 arch: %lu (%s)", + arch, + KernAux_Multiboot2_Header_Arch_to_str(multiboot2_header->arch) + ); + PRINTLNF(" u32 size: %lu", total_size); + PRINTLNF(" u32 checksum: 0x%lx", checksum); + PRINTLN("}"); + + const struct KernAux_Multiboot2_HTagBase *tag_base = + (struct KernAux_Multiboot2_HTagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_header); + + while (tag_base < + (struct KernAux_Multiboot2_HTagBase*) + ((uint8_t*)multiboot2_header + multiboot2_header->total_size)) + { + if (!KernAux_Multiboot2_HTagBase_is_valid(tag_base)) return; + KernAux_Multiboot2_HTagBase_print(tag_base, display); + tag_base = KERNAUX_MULTIBOOT2_HTAG_NEXT(tag_base); + } +} + +void KernAux_Multiboot2_HTagBase_print( + const struct KernAux_Multiboot2_HTagBase *const tag_base, + const KernAux_Display display +) { + KERNAUX_NOTNULL(tag_base); + KERNAUX_NOTNULL(display); + + switch (tag_base->type) { + case KERNAUX_MULTIBOOT2_HTAG_NONE: + KernAux_Multiboot2_HTag_None_print( + (struct KernAux_Multiboot2_HTag_None*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_INFO_REQ: + KernAux_Multiboot2_HTag_InfoReq_print( + (struct KernAux_Multiboot2_HTag_InfoReq*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_ADDR: + KernAux_Multiboot2_HTag_Addr_print( + (struct KernAux_Multiboot2_HTag_Addr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_ENTRY_ADDR: + KernAux_Multiboot2_HTag_EntryAddr_print( + (struct KernAux_Multiboot2_HTag_EntryAddr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_FLAGS: + KernAux_Multiboot2_HTag_Flags_print( + (struct KernAux_Multiboot2_HTag_Flags*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_FRAMEBUFFER: + KernAux_Multiboot2_HTag_Framebuffer_print( + (struct KernAux_Multiboot2_HTag_Framebuffer*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_MODULE_ALIGN: + KernAux_Multiboot2_HTag_ModuleAlign_print( + (struct KernAux_Multiboot2_HTag_ModuleAlign*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_EFI_BOOT_SERVICES: + KernAux_Multiboot2_HTag_EFIBootServices_print( + (struct KernAux_Multiboot2_HTag_EFIBootServices*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_EFI_I386_ENTRY_ADDR: + KernAux_Multiboot2_HTag_EFII386EntryAddr_print( + (struct KernAux_Multiboot2_HTag_EFII386EntryAddr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_EFI_AMD64_ENTRY_ADDR: + KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_print( + (struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_HTAG_RELOCATABLE_HEADER: + KernAux_Multiboot2_HTag_RelocatableHeader_print( + (struct KernAux_Multiboot2_HTag_RelocatableHeader*)tag_base, + display + ); + break; + } +} + +void KernAux_Multiboot2_HTag_None_print( + const struct KernAux_Multiboot2_HTag_None *const tag, + const KernAux_Display display +) { + HEADER(None); + FOOTER; +} + +void KernAux_Multiboot2_HTag_InfoReq_print( + const struct KernAux_Multiboot2_HTag_InfoReq *const tag, + const KernAux_Display display +) { + HEADER(InfoReq); + + // Print data: + + if ((tag->base.size - sizeof(*tag)) / sizeof(uint32_t) == 0) { + PRINTLN(" u32 mbi_tag_types[]: []"); + } else { + PRINTLN(" u32 mbi_tag_types[]: ["); + + const uint32_t *const mbi_tag_types = + (const uint32_t*)KERNAUX_MULTIBOOT2_DATA(tag); + + for ( + size_t index = 0; + index < (tag->base.size - sizeof(*tag)) / sizeof(uint32_t); + ++index + ) { + KERNAUX_CAST_CONST(unsigned long, type, mbi_tag_types[index]); + + PRINTLNF(" %lu (%s)", + type, + KernAux_Multiboot2_ITag_to_str(type) + ); + } + + PRINTLN(" ]"); + } + + FOOTER; +} + +void KernAux_Multiboot2_HTag_Addr_print( + const struct KernAux_Multiboot2_HTag_Addr *const tag, + const KernAux_Display display +) { + HEADER(Addr); + + KERNAUX_CAST_CONST(unsigned long, header_addr, tag->header_addr); + KERNAUX_CAST_CONST(unsigned long, load_addr, tag->load_addr); + KERNAUX_CAST_CONST(unsigned long, load_end_addr, tag->load_end_addr); + KERNAUX_CAST_CONST(unsigned long, bss_end_addr, tag->bss_end_addr); + + PRINTLNF(" u32 header_addr: 0x%lx", header_addr); + PRINTLNF(" u32 load_addr: 0x%lx", load_addr); + PRINTLNF(" u32 load_end_addr: 0x%lx", load_end_addr); + PRINTLNF(" u32 bss_end_addr: 0x%lx", bss_end_addr); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EntryAddr *const tag, + const KernAux_Display display +) { + HEADER(EntryAddr); + + KERNAUX_CAST_CONST(unsigned long, entry_addr, tag->entry_addr); + + PRINTLNF(" u32 entry_addr: 0x%lx", entry_addr); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_Flags_print( + const struct KernAux_Multiboot2_HTag_Flags *const tag, + const KernAux_Display display +) { + HEADER(Flags); + + PRINT(" u32 console_flags: "); + KernAux_Multiboot2_HTag_Flags_ConsoleFlags_print( + tag->console_flags, + display, + 2, + 2, + false + ); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_Framebuffer_print( + const struct KernAux_Multiboot2_HTag_Framebuffer *const tag, + const KernAux_Display display +) { + HEADER(Framebuffer); + + KERNAUX_CAST_CONST(unsigned long, width, tag->width); + KERNAUX_CAST_CONST(unsigned long, height, tag->height); + KERNAUX_CAST_CONST(unsigned long, depth, tag->depth); + + PRINTLNF(" u32 width: %lu", width); + PRINTLNF(" u32 height: %lu", height); + PRINTLNF(" u32 depth: %lu", depth); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_ModuleAlign_print( + const struct KernAux_Multiboot2_HTag_ModuleAlign *const tag, + const KernAux_Display display +) { + HEADER(ModuleAlign); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_EFIBootServices_print( + const struct KernAux_Multiboot2_HTag_EFIBootServices *const tag, + const KernAux_Display display +) { + HEADER(EFIBootServices); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_EFII386EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EFII386EntryAddr *const tag, + const KernAux_Display display +) { + HEADER(EFII386EntryAddr); + + KERNAUX_CAST_CONST(unsigned long, entry_addr, tag->entry_addr); + + PRINTLNF(" u32 entry_addr: 0x%lx", entry_addr); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_EFIAmd64EntryAddr_print( + const struct KernAux_Multiboot2_HTag_EFIAmd64EntryAddr *const tag, + const KernAux_Display display +) { + HEADER(EFIAmd64EntryAddr); + + KERNAUX_CAST_CONST(unsigned long, entry_addr, tag->entry_addr); + + PRINTLNF(" u32 entry_addr: 0x%lx", entry_addr); + + FOOTER; +} + +void KernAux_Multiboot2_HTag_RelocatableHeader_print( + const struct KernAux_Multiboot2_HTag_RelocatableHeader *const tag, + const KernAux_Display display +) { + HEADER(RelocatableHeader); + + KERNAUX_CAST_CONST(unsigned long, min_addr, tag->min_addr); + KERNAUX_CAST_CONST(unsigned long, max_addr, tag->max_addr); + KERNAUX_CAST_CONST(unsigned long, align, tag->align); + KERNAUX_CAST_CONST(unsigned long, preference, tag->preference); + + PRINTLNF(" u32 min_addr: 0x%lx", min_addr); + PRINTLNF(" u32 max_addr: 0x%lx", max_addr); + PRINTLNF(" u32 align: %lu", align); + PRINTLNF(" u32 preference: %lu (%s)", + preference, + KernAux_Multiboot2_HTag_RelocatableHeader_Preference_to_str( + tag->preference + ) + ); + + FOOTER; +} + +void KernAux_Multiboot2_HTagBase_Flags_print( + const uint16_t flags, + const KernAux_Display display, + const unsigned basic_indentation, + const unsigned indentation_delta, + const bool indent_first +) { + KERNAUX_CAST_CONST(unsigned long, flags_ul, flags); + + if (indent_first) INDENT; + PRINTF("0x%lx (", flags_ul); + + bool is_first = true; + + for ( + size_t index = 0; + index < sizeof(base_flag_names) / sizeof(base_flag_names[0]); + ++index + ) { + if (flags & base_flag_names[index].number) { + if (is_first) { + PRINTLN(""); + } else { + PRINTLN(" |"); + } + + INDENT; + INDENT_MORE; + PRINTF("%s", base_flag_names[index].name); + is_first = false; + } + } + + if (is_first) { + PRINTLN(")"); + } else { + PRINTLN(""); + INDENT; + PRINTLN(")"); + } +} + +void KernAux_Multiboot2_HTag_Flags_ConsoleFlags_print( + const uint32_t console_flags, + const KernAux_Display display, + const unsigned basic_indentation, + const unsigned indentation_delta, + const bool indent_first +) { + KERNAUX_CAST_CONST(unsigned long, console_flags_ul, console_flags); + + if (indent_first) INDENT; + PRINTF("0x%lx (", console_flags_ul); + + bool is_first = true; + + for ( + size_t index = 0; + index < sizeof(console_flag_names) / sizeof(console_flag_names[0]); + ++index + ) { + if (console_flags & console_flag_names[index].number) { + if (is_first) { + PRINTLN(""); + } else { + PRINTLN(" |"); + } + + INDENT; + INDENT_MORE; + PRINTF("%s", console_flag_names[index].name); + is_first = false; + } + } + + if (is_first) { + PRINTLN(")"); + } else { + PRINTLN(""); + INDENT; + PRINTLN(")"); + } +} diff --git a/src/multiboot2/info_convert.c b/src/multiboot2/info_convert.c new file mode 100644 index 0000000..3e0b707 --- /dev/null +++ b/src/multiboot2/info_convert.c @@ -0,0 +1,71 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include +#include +#include +#include + +#include + +#ifdef WITH_MEMMAP +KernAux_Memmap_Builder KernAux_Multiboot2_Info_to_memmap_builder( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const KernAux_Malloc malloc +) { + KERNAUX_NOTNULL(multiboot2_info); + KERNAUX_NOTNULL(malloc); + + if (!KernAux_Multiboot2_Info_is_valid(multiboot2_info)) return NULL; + + const struct KernAux_Multiboot2_ITag_MemoryMap *const memory_map_tag = + (const struct KernAux_Multiboot2_ITag_MemoryMap*) + KernAux_Multiboot2_Info_first_tag_with_type( + multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP + ); + if (!memory_map_tag) return NULL; + + const void *const data = KERNAUX_MULTIBOOT2_DATA(memory_map_tag); + size_t data_size = memory_map_tag->base.size - sizeof(*memory_map_tag); + const void *const data_end = ((const char*)data) + data_size; + + if (memory_map_tag->entry_size < + sizeof(struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase)) + { + return NULL; + } + + KernAux_Memmap_Builder builder = + KernAux_Memmap_Builder_new(malloc); + if (!builder) return NULL; + + for ( + const struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase *entry = data; + (const char*)entry < (const char*)data_end; + entry = + (const struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase*) + (((const char*)entry) + memory_map_tag->entry_size) + ) { + const void *const node = KernAux_Memmap_Builder_add( + builder, + NULL, + entry->base_addr, + entry->length, + KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str( + entry->type + ) + ); + + if (!node) { + KernAux_Memmap_Builder_finish_and_free(builder); + return NULL; + } + } + + return builder; +} +#endif diff --git a/src/multiboot2/info_enums.c b/src/multiboot2/info_enums.c new file mode 100644 index 0000000..06c5058 --- /dev/null +++ b/src/multiboot2/info_enums.c @@ -0,0 +1,79 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +const char *KernAux_Multiboot2_ITag_to_str(const uint32_t tag_type) +{ + switch (tag_type) { + case KERNAUX_MULTIBOOT2_ITAG_NONE: + return "none"; + case KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE: + return "boot cmd line"; + case KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME: + return "boot loader name"; + case KERNAUX_MULTIBOOT2_ITAG_MODULE: + return "module"; + case KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO: + return "basic memory info"; + case KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE: + return "BIOS boot device"; + case KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP: + return "memory map"; + case KERNAUX_MULTIBOOT2_ITAG_VBE_INFO: + return "VBE info"; + case KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO: + return "framebuffer info"; + case KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS: + return "ELF symbols"; + case KERNAUX_MULTIBOOT2_ITAG_APM_TABLE: + return "APM table"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR: + return "EFI 32bit system table ptr"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR: + return "EFI 64bit system table ptr"; + case KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES: + return "SMBIOS tables"; + case KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP: + return "ACPI old RSDP"; + case KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP: + return "ACPI new RSDP"; + case KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO: + return "networking info"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP: + return "EFI memory map"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED: + return "EFI boot services not terminated"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR: + return "EFI 32bit image handle ptr"; + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR: + return "EFI 64bit image handle ptr"; + case KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR: + return "image load base phys addr"; + default: + return NULL; + } +} + +const char* +KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str(const uint32_t type) +{ + switch (type) { + case KERNAUX_MULTIBOOT2_MEMMAP_AVAILABLE: + return "available"; + case KERNAUX_MULTIBOOT2_MEMMAP_RESERVED: + return "reserved"; + case KERNAUX_MULTIBOOT2_MEMMAP_ACPI_RECLAIMABLE: + return "ACPI reclaimable"; + case KERNAUX_MULTIBOOT2_MEMMAP_NVS: + return "ACPI Non-Volatile Storage"; + case KERNAUX_MULTIBOOT2_MEMMAP_BADRAM: + return "bad RAM"; + default: + return NULL; + } +} diff --git a/src/multiboot2/info_helpers.c b/src/multiboot2/info_helpers.c new file mode 100644 index 0000000..6e940fa --- /dev/null +++ b/src/multiboot2/info_helpers.c @@ -0,0 +1,79 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include + +const struct KernAux_Multiboot2_ITagBase +*KernAux_Multiboot2_Info_first_tag_with_type( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const uint32_t tag_type +) { + KERNAUX_NOTNULL(multiboot2_info); + + const struct KernAux_Multiboot2_ITagBase *tag_base = + (struct KernAux_Multiboot2_ITagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_info); + + while (tag_base < + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)multiboot2_info + multiboot2_info->total_size)) + { + if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return NULL; + if (tag_base->type == tag_type) return tag_base; + + tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base); + } + + return NULL; +} + +const struct KernAux_Multiboot2_ITagBase +*KernAux_Multiboot2_Info_tag_with_type_after( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const uint32_t tag_type, + const struct KernAux_Multiboot2_ITagBase *const after_tag +) { + KERNAUX_NOTNULL(multiboot2_info); + KERNAUX_NOTNULL(after_tag); + + const struct KernAux_Multiboot2_ITagBase *tag_base = + (struct KernAux_Multiboot2_ITagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_info); + + KERNAUX_ASSERT(tag_base <= after_tag); + + while (tag_base < + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)multiboot2_info + multiboot2_info->total_size)) + { + if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return NULL; + if (tag_base->type == tag_type && tag_base > after_tag) return tag_base; + + tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base); + } + + return NULL; +} + +const char *KernAux_Multiboot2_Info_boot_cmd_line( + const struct KernAux_Multiboot2_Info *const multiboot2_info +) { + KERNAUX_NOTNULL(multiboot2_info); + + const struct KernAux_Multiboot2_ITag_BootCmdLine *const tag = + (struct KernAux_Multiboot2_ITag_BootCmdLine*) + KernAux_Multiboot2_Info_first_tag_with_type( + multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE + ); + + if (!tag) return NULL; + + return (char*)KERNAUX_MULTIBOOT2_DATA(tag); +} diff --git a/src/multiboot2/info_is_valid.c b/src/multiboot2/info_is_valid.c new file mode 100644 index 0000000..75755f9 --- /dev/null +++ b/src/multiboot2/info_is_valid.c @@ -0,0 +1,428 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include + +#include +#include +#include + +bool KernAux_Multiboot2_Info_is_valid( + const struct KernAux_Multiboot2_Info *const multiboot2_info +) { + KERNAUX_NOTNULL(multiboot2_info); + + if (multiboot2_info->total_size < + sizeof(struct KernAux_Multiboot2_Info) + + sizeof(struct KernAux_Multiboot2_ITag_None)) + { + return false; + } + + if (multiboot2_info->total_size % KERNAUX_MULTIBOOT2_TAG_ALIGN != 0) { + return false; + } + + const struct KernAux_Multiboot2_ITagBase *tag_base = + (struct KernAux_Multiboot2_ITagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_info); + + const struct KernAux_Multiboot2_ITagBase *none_tag_base = NULL; + + while (tag_base < + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)multiboot2_info + multiboot2_info->total_size)) + { + if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return false; + + if (tag_base->type == KERNAUX_MULTIBOOT2_ITAG_NONE && + none_tag_base == NULL + ) { + none_tag_base = tag_base; + } + + tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base); + } + + if (tag_base != + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)multiboot2_info + multiboot2_info->total_size)) + { + return false; + } + + if (none_tag_base != + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)tag_base - + sizeof(struct KernAux_Multiboot2_ITag_None))) + { + return false; + } + + return true; +} + +bool KernAux_Multiboot2_ITagBase_is_valid( + const struct KernAux_Multiboot2_ITagBase *const tag_base +) { + KERNAUX_NOTNULL(tag_base); + + switch (tag_base->type) { + case KERNAUX_MULTIBOOT2_ITAG_NONE: + return KernAux_Multiboot2_ITag_None_is_valid( + (struct KernAux_Multiboot2_ITag_None*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE: + return KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + (struct KernAux_Multiboot2_ITag_BootCmdLine*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME: + return KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + (struct KernAux_Multiboot2_ITag_BootLoaderName*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_MODULE: + return KernAux_Multiboot2_ITag_Module_is_valid( + (struct KernAux_Multiboot2_ITag_Module*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO: + return KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + (struct KernAux_Multiboot2_ITag_BasicMemoryInfo*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE: + return KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + (struct KernAux_Multiboot2_ITag_BIOSBootDevice*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP: + return KernAux_Multiboot2_ITag_MemoryMap_is_valid( + (struct KernAux_Multiboot2_ITag_MemoryMap*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_VBE_INFO: + return KernAux_Multiboot2_ITag_VBEInfo_is_valid( + (struct KernAux_Multiboot2_ITag_VBEInfo*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO: + return KernAux_Multiboot2_ITag_FramebufferInfo_is_valid( + (struct KernAux_Multiboot2_ITag_FramebufferInfo*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS: + return KernAux_Multiboot2_ITag_ELFSymbols_is_valid( + (struct KernAux_Multiboot2_ITag_ELFSymbols*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_APM_TABLE: + return KernAux_Multiboot2_ITag_APMTable_is_valid( + (struct KernAux_Multiboot2_ITag_APMTable*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR: + return KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_is_valid( + (struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR: + return KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_is_valid( + (struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES: + return KernAux_Multiboot2_ITag_SMBIOSTables_is_valid( + (struct KernAux_Multiboot2_ITag_SMBIOSTables*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP: + return KernAux_Multiboot2_ITag_ACPIOldRSDP_is_valid( + (struct KernAux_Multiboot2_ITag_ACPIOldRSDP*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP: + return KernAux_Multiboot2_ITag_ACPINewRSDP_is_valid( + (struct KernAux_Multiboot2_ITag_ACPINewRSDP*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO: + return KernAux_Multiboot2_ITag_NetworkingInfo_is_valid( + (struct KernAux_Multiboot2_ITag_NetworkingInfo*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP: + return KernAux_Multiboot2_ITag_EFIMemoryMap_is_valid( + (struct KernAux_Multiboot2_ITag_EFIMemoryMap*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED: + return KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_is_valid( + (struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated*) + tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR: + return KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_is_valid( + (struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR: + return KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_is_valid( + (struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr*)tag_base + ); + case KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR: + return KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_is_valid( + (struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr*)tag_base + ); + default: + return false; + } +} + +bool KernAux_Multiboot2_ITag_None_is_valid( + const struct KernAux_Multiboot2_ITag_None *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_NONE && + tag->base.size == 8 + ); +} + +bool KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + const struct KernAux_Multiboot2_ITag_BootCmdLine *const tag +) { + KERNAUX_NOTNULL(tag); + + size_t index = 1; + + for ( + const char *ptr = (char*)KERNAUX_MULTIBOOT2_DATA(tag); + *ptr && index < tag->base.size; + ++ptr + ) { + ++index; + } + + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE && + tag->base.size == 8 + index + ); +} + +bool KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + const struct KernAux_Multiboot2_ITag_BootLoaderName *const tag +) { + KERNAUX_NOTNULL(tag); + + size_t index = 1; + + for ( + const char *ptr = (char*)KERNAUX_MULTIBOOT2_DATA(tag); + *ptr && index < tag->base.size; + ++ptr + ) { + ++index; + } + + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME && + tag->base.size == 8 + index + ); +} + +bool KernAux_Multiboot2_ITag_Module_is_valid( + const struct KernAux_Multiboot2_ITag_Module *const tag +) { + KERNAUX_NOTNULL(tag); + + size_t index = 1; + + for ( + const char *ptr = (char*)KERNAUX_MULTIBOOT2_DATA(tag); + *ptr && index < tag->base.size; + ++ptr + ) { + ++index; + } + + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_MODULE && + tag->base.size == 16 + index && + tag->mod_start < tag->mod_end + ); +} + +bool KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + const struct KernAux_Multiboot2_ITag_BasicMemoryInfo *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO && + tag->base.size == 16 + ); +} + +bool KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + const struct KernAux_Multiboot2_ITag_BIOSBootDevice *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE && + tag->base.size == 20 + ); +} + +bool KernAux_Multiboot2_ITag_MemoryMap_is_valid( + const struct KernAux_Multiboot2_ITag_MemoryMap *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP && + tag->base.size >= 16 && + tag->entry_size > 0 && + tag->entry_size % 8 == 0 && + (tag->base.size - 16) % tag->entry_size == 0 + ); +} + +bool KernAux_Multiboot2_ITag_VBEInfo_is_valid( + const struct KernAux_Multiboot2_ITag_VBEInfo *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_VBE_INFO && + tag->base.size == 784 + ); +} + +bool KernAux_Multiboot2_ITag_FramebufferInfo_is_valid( + const struct KernAux_Multiboot2_ITag_FramebufferInfo *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO && + tag->base.size >= 32 + ); +} + +bool KernAux_Multiboot2_ITag_ELFSymbols_is_valid( + const struct KernAux_Multiboot2_ITag_ELFSymbols *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS && + tag->base.size >= 20 && + ( + tag->entsize == 0 || + (tag->base.size - 20) % tag->entsize == 0 + ) + ); +} + +bool KernAux_Multiboot2_ITag_APMTable_is_valid( + const struct KernAux_Multiboot2_ITag_APMTable *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_APM_TABLE && + tag->base.size == 28 + ); +} + +bool KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR && + tag->base.size == 16 + ); +} + +bool KernAux_Multiboot2_ITag_SMBIOSTables_is_valid( + const struct KernAux_Multiboot2_ITag_SMBIOSTables *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES && + tag->base.size >= 16 + ); +} + +bool KernAux_Multiboot2_ITag_ACPIOldRSDP_is_valid( + const struct KernAux_Multiboot2_ITag_ACPIOldRSDP *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP && + tag->base.size >= 8 + ); +} + +bool KernAux_Multiboot2_ITag_ACPINewRSDP_is_valid( + const struct KernAux_Multiboot2_ITag_ACPINewRSDP *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP && + tag->base.size >= 8 + ); +} + +bool KernAux_Multiboot2_ITag_NetworkingInfo_is_valid( + const struct KernAux_Multiboot2_ITag_NetworkingInfo *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO && + tag->base.size >= 8 + ); +} + +bool KernAux_Multiboot2_ITag_EFIMemoryMap_is_valid( + const struct KernAux_Multiboot2_ITag_EFIMemoryMap *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP && + tag->base.size >= 16 + ); +} + +bool KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_is_valid( + const struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == + KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED && + tag->base.size == 8 + ); +} + +bool KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR && + tag->base.size == 12 + ); +} + +bool KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_is_valid( + const struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR && + tag->base.size == 16 + ); +} + +bool KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_is_valid( + const struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr *const tag +) { + KERNAUX_NOTNULL(tag); + return ( + tag->base.type == KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR && + tag->base.size == 12 + ); +} diff --git a/src/multiboot2/info_print.c b/src/multiboot2/info_print.c new file mode 100644 index 0000000..d02898b --- /dev/null +++ b/src/multiboot2/info_print.c @@ -0,0 +1,767 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "../assert.h" + +#include +#include +#include +#include + +#include +#include +#include + +#define PRINT(s) do { KernAux_Display_print (display, s); } while (0) +#define PRINTLN(s) do { KernAux_Display_println(display, s); } while (0) + +#define PRINTF(format, ...) \ + do { KernAux_Display_printf (display, format, __VA_ARGS__); } while (0) +#define PRINTLNF(format, ...) \ + do { KernAux_Display_printlnf(display, format, __VA_ARGS__); } while (0) + +#define HEADER(Type) do { \ + KERNAUX_NOTNULL(tag); \ + KERNAUX_NOTNULL(display); \ + \ + if (!KernAux_Multiboot2_ITag_##Type##_is_valid(tag)) { \ + PRINTLN("Multiboot 2 info tag // invalid!"); \ + } \ + \ + KERNAUX_CAST_CONST(unsigned long, type, tag->base.type); \ + KERNAUX_CAST_CONST(unsigned long, size, tag->base.size); \ + \ + PRINTLN("Multiboot 2 info tag {"); \ + PRINTLNF(" u32 type: %lu (%s)", \ + type, \ + KernAux_Multiboot2_ITag_to_str(tag->base.type) \ + ); \ + PRINTLNF(" u32 size: %lu", size); \ +} while (0) + +#define FOOTER do { PRINTLN("}"); } while (0) + +#define INDENT do { \ + for (unsigned index = 0; index < basic_indentation; ++index) PRINT(" "); \ +} while (0) + +#define INDENT_MORE do { \ + for (unsigned index = 0; index < indentation_delta; ++index) PRINT(" "); \ +} while (0) + +static const struct { + uint32_t number; + const char *name; +} section_flag_names[] = { + { + .number = KERNAUX_ELF_SECT_FLAGS_WRITE, + .name = "WRITE", + }, + { + .number = KERNAUX_ELF_SECT_FLAGS_ALLOC, + .name = "ALLOC", + }, + { + .number = KERNAUX_ELF_SECT_FLAGS_EXECINSTR, + .name = "EXECINSTR", + }, + { + .number = KERNAUX_ELF_SECT_FLAGS_MASKPROC, + .name = "MASKPROC", + }, +}; + +static void KernAux_ELF_Section_Flags_print( + uint16_t flags, + KernAux_Display display, + unsigned basic_indentation, + unsigned indentation_delta, + bool indent_first +); + +void KernAux_Multiboot2_Info_print( + const struct KernAux_Multiboot2_Info *const multiboot2_info, + const KernAux_Display display +) { + KERNAUX_NOTNULL(multiboot2_info); + KERNAUX_NOTNULL(display); + + KERNAUX_CAST_CONST(unsigned long, total_size, multiboot2_info->total_size); + KERNAUX_CAST_CONST(unsigned long, reserved, multiboot2_info->reserved); + + PRINTLN("Multiboot 2 info {"); + PRINTLNF(" u32 size: %lu", total_size); + PRINTLNF(" u32 reserved: 0x%lx", reserved); + PRINTLN("}"); + + const struct KernAux_Multiboot2_ITagBase *tag_base = + (struct KernAux_Multiboot2_ITagBase*) + KERNAUX_MULTIBOOT2_DATA(multiboot2_info); + + while (tag_base < + (struct KernAux_Multiboot2_ITagBase*) + ((uint8_t*)multiboot2_info + multiboot2_info->total_size)) + { + if (!KernAux_Multiboot2_ITagBase_is_valid(tag_base)) return; + KernAux_Multiboot2_ITagBase_print(tag_base, display); + tag_base = KERNAUX_MULTIBOOT2_ITAG_NEXT(tag_base); + } +} + +void KernAux_Multiboot2_ITagBase_print( + const struct KernAux_Multiboot2_ITagBase *const tag_base, + const KernAux_Display display +) { + KERNAUX_NOTNULL(tag_base); + KERNAUX_NOTNULL(display); + + switch (tag_base->type) { + case KERNAUX_MULTIBOOT2_ITAG_NONE: + KernAux_Multiboot2_ITag_None_print( + (struct KernAux_Multiboot2_ITag_None*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE: + KernAux_Multiboot2_ITag_BootCmdLine_print( + (struct KernAux_Multiboot2_ITag_BootCmdLine*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME: + KernAux_Multiboot2_ITag_BootLoaderName_print( + (struct KernAux_Multiboot2_ITag_BootLoaderName*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_MODULE: + KernAux_Multiboot2_ITag_Module_print( + (struct KernAux_Multiboot2_ITag_Module*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO: + KernAux_Multiboot2_ITag_BasicMemoryInfo_print( + (struct KernAux_Multiboot2_ITag_BasicMemoryInfo*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE: + KernAux_Multiboot2_ITag_BIOSBootDevice_print( + (struct KernAux_Multiboot2_ITag_BIOSBootDevice*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP: + KernAux_Multiboot2_ITag_MemoryMap_print( + (struct KernAux_Multiboot2_ITag_MemoryMap*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_VBE_INFO: + KernAux_Multiboot2_ITag_VBEInfo_print( + (struct KernAux_Multiboot2_ITag_VBEInfo*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO: + KernAux_Multiboot2_ITag_FramebufferInfo_print( + (struct KernAux_Multiboot2_ITag_FramebufferInfo*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS: + KernAux_Multiboot2_ITag_ELFSymbols_print( + (struct KernAux_Multiboot2_ITag_ELFSymbols*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_APM_TABLE: + KernAux_Multiboot2_ITag_APMTable_print( + (struct KernAux_Multiboot2_ITag_APMTable*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR: + KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_print( + (struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR: + KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_print( + (struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES: + KernAux_Multiboot2_ITag_SMBIOSTables_print( + (struct KernAux_Multiboot2_ITag_SMBIOSTables*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP: + KernAux_Multiboot2_ITag_ACPIOldRSDP_print( + (struct KernAux_Multiboot2_ITag_ACPIOldRSDP*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP: + KernAux_Multiboot2_ITag_ACPINewRSDP_print( + (struct KernAux_Multiboot2_ITag_ACPINewRSDP*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO: + KernAux_Multiboot2_ITag_NetworkingInfo_print( + (struct KernAux_Multiboot2_ITag_NetworkingInfo*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP: + KernAux_Multiboot2_ITag_EFIMemoryMap_print( + (struct KernAux_Multiboot2_ITag_EFIMemoryMap*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED: + KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_print( + (struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated*) + tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR: + KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_print( + (struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR: + KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_print( + (struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr*)tag_base, + display + ); + break; + case KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR: + KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_print( + (struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr*)tag_base, + display + ); + break; + } +} + +void KernAux_Multiboot2_ITag_None_print( + const struct KernAux_Multiboot2_ITag_None *const tag, + const KernAux_Display display +) { + HEADER(None); + FOOTER; +} + +void KernAux_Multiboot2_ITag_BootCmdLine_print( + const struct KernAux_Multiboot2_ITag_BootCmdLine *const tag, + const KernAux_Display display +) { + HEADER(BootCmdLine); + + // Print data: + PRINTLNF(" char cmdline[]: \"%s\"", KERNAUX_MULTIBOOT2_DATA(tag)); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_BootLoaderName_print( + const struct KernAux_Multiboot2_ITag_BootLoaderName *const tag, + const KernAux_Display display +) { + HEADER(BootLoaderName); + + // Print data: + PRINTLNF(" char name[]: \"%s\"", KERNAUX_MULTIBOOT2_DATA(tag)); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_Module_print( + const struct KernAux_Multiboot2_ITag_Module *const tag, + const KernAux_Display display +) { + HEADER(Module); + + KERNAUX_CAST_CONST(unsigned long, mod_start, tag->mod_start); + KERNAUX_CAST_CONST(unsigned long, mod_end, tag->mod_end); + + PRINTLNF(" u32 mod_start: 0x%lx", mod_start); + PRINTLNF(" u32 mod_end: 0x%lx", mod_end); + + // Print data: + PRINTLNF(" char cmdline[]: \"%s\"", KERNAUX_MULTIBOOT2_DATA(tag)); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_BasicMemoryInfo_print( + const struct KernAux_Multiboot2_ITag_BasicMemoryInfo *const tag, + const KernAux_Display display +) { + HEADER(BasicMemoryInfo); + + KERNAUX_CAST_CONST(unsigned long, mem_lower, tag->mem_lower); + KERNAUX_CAST_CONST(unsigned long, mem_upper, tag->mem_upper); + + PRINTLNF(" u32 mem_lower: %lu", mem_lower); + PRINTLNF(" u32 mem_upper: %lu", mem_upper); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_BIOSBootDevice_print( + const struct KernAux_Multiboot2_ITag_BIOSBootDevice *const tag, + const KernAux_Display display +) { + HEADER(BIOSBootDevice); + + KERNAUX_CAST_CONST(unsigned long, biosdev, tag->biosdev); + KERNAUX_CAST_CONST(unsigned long, partition, tag->partition); + KERNAUX_CAST_CONST(unsigned long, sub_partition, tag->sub_partition); + + PRINTLNF(" u32 biosdev: %lu", biosdev); + PRINTLNF(" u32 partition: %lu", partition); + PRINTLNF(" u32 sub_partition: %lu", sub_partition); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_MemoryMap_print( + const struct KernAux_Multiboot2_ITag_MemoryMap *const tag, + const KernAux_Display display +) { + HEADER(MemoryMap); + + KERNAUX_CAST_CONST(unsigned long, entry_size, tag->entry_size); + KERNAUX_CAST_CONST(unsigned long, entry_version, tag->entry_version); + + PRINTLNF(" u32 entry_size: %lu", entry_size); + PRINTLNF(" u32 entry_version: %lu", entry_version); + + // Print data: + + if (tag->entry_size == 0 || + (tag->base.size - sizeof(*tag)) / tag->entry_size == 0) + { + PRINTLN (" varies(entry_size) entries[]: []"); + } else { + PRINTLN (" varies(entry_size) entries[]: ["); + + const struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase* + const entries = + (struct KernAux_Multiboot2_ITag_MemoryMap_EntryBase*) + KERNAUX_MULTIBOOT2_DATA(tag); + + for ( + size_t index = 0; + index < (tag->base.size - sizeof(*tag)) / tag->entry_size; + ++index + ) { + KERNAUX_CAST_CONST(unsigned long long, base_addr, + entries[index].base_addr); + KERNAUX_CAST_CONST(unsigned long long, length, + entries[index].length); + KERNAUX_CAST_CONST(unsigned long, type, + entries[index].type); + KERNAUX_CAST_CONST(unsigned long, reserved, + entries[index].reserved); + + PRINTLNF(" [%zu]: {", index); + PRINTLNF(" u64 base_addr: 0x%llx", base_addr); + PRINTLNF(" u64 length: %llu", length); + PRINTLNF(" u32 type: %lu (%s)", + type, + KernAux_Multiboot2_ITag_MemoryMap_EntryBase_Type_to_str( + entries[index].type + ) + ); + PRINTLNF(" u32 reserved: 0x%lx", reserved); + PRINTLN (" }"); + } + + PRINTLN(" ]"); + } + + FOOTER; +} + +void KernAux_Multiboot2_ITag_VBEInfo_print( + const struct KernAux_Multiboot2_ITag_VBEInfo *const tag, + const KernAux_Display display +) { + HEADER(VBEInfo); + + KERNAUX_CAST_CONST(unsigned long, mode, tag->vbe_mode); + KERNAUX_CAST_CONST(unsigned long, interface_seg, tag->vbe_interface_seg); + KERNAUX_CAST_CONST(unsigned long, interface_off, tag->vbe_interface_off); + KERNAUX_CAST_CONST(unsigned long, interface_len, tag->vbe_interface_len); + + PRINTLNF(" u16 vbe_mode: %lu", mode); + PRINTLNF(" u16 vbe_interface_seg: %lu", interface_seg); + PRINTLNF(" u16 vbe_interface_off: %lu", interface_off); + PRINTLNF(" u16 vbe_interface_len: %lu", interface_len); + + const size_t cols = 16; + + PRINTLN (" u8 vbe_control_info[]: ["); + for ( + size_t index = 0; + index < sizeof(tag->vbe_control_info) / sizeof(tag->vbe_control_info[0]); + index += cols + ) { + PRINTF(" %-3u", tag->vbe_control_info[index]); + for (size_t col = 1; col < cols; ++col) { + PRINTF(" %-3u", tag->vbe_control_info[index + col]); + } + PRINTLN(""); + } + PRINTLN (" ]"); + + PRINTLN (" u8 vbe_mode_info[]: ["); + for ( + size_t index = 0; + index < sizeof(tag->vbe_mode_info) / sizeof(tag->vbe_mode_info[0]); + index += cols + ) { + PRINTF(" %-3u", tag->vbe_mode_info[index]); + for (size_t col = 1; col < cols; ++col) { + PRINTF(" %-3u", tag->vbe_mode_info[index + col]); + } + PRINTLN(""); + } + PRINTLN (" ]"); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_FramebufferInfo_print( + const struct KernAux_Multiboot2_ITag_FramebufferInfo *const tag, + const KernAux_Display display +) { + HEADER(FramebufferInfo); + + KERNAUX_CAST_CONST(unsigned long long, addr, tag->framebuffer_addr); + KERNAUX_CAST_CONST(unsigned long, pitch, tag->framebuffer_pitch); + KERNAUX_CAST_CONST(unsigned long, width, tag->framebuffer_width); + KERNAUX_CAST_CONST(unsigned long, height, tag->framebuffer_height); + KERNAUX_CAST_CONST(unsigned long, bpp, tag->framebuffer_bpp); + KERNAUX_CAST_CONST(unsigned long, type, tag->framebuffer_type); + KERNAUX_CAST_CONST(unsigned long, reserved, tag->reserved); + + PRINTLNF(" u64 framebuffer_addr: 0x%llx", addr); + PRINTLNF(" u32 framebuffer_pitch: %lu", pitch); + PRINTLNF(" u32 framebuffer_width: %lu", width); + PRINTLNF(" u32 framebuffer_height: %lu", height); + PRINTLNF(" u8 framebuffer_bpp: %lu", bpp); + PRINTLNF(" u8 framebuffer_type: %lu", type); + PRINTLNF(" u16 reserved: 0x%lx", reserved); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_ELFSymbols_print( + const struct KernAux_Multiboot2_ITag_ELFSymbols *const tag, + const KernAux_Display display +) { + HEADER(ELFSymbols); + + KERNAUX_CAST_CONST(unsigned long, num, tag->num); + KERNAUX_CAST_CONST(unsigned long, entsize, tag->entsize); + KERNAUX_CAST_CONST(unsigned long, shndx, tag->shndx); + + PRINTLNF(" u32 num: %lu", num); + PRINTLNF(" u32 entsize: %lu", entsize); + PRINTLNF(" u32 shndx: %lu", shndx); + + // Print data: + + if (tag->num == 0) { + PRINTLN(" varies(entsize) section_headers[]: []"); + } else { + PRINTLN(" varies(entsize) section_headers[]: ["); + + const struct KernAux_ELF_Section *section = + (const struct KernAux_ELF_Section*) + KERNAUX_MULTIBOOT2_DATA(tag); + + for (size_t index = 0; index < tag->num; ++index) { + KERNAUX_CAST_CONST(unsigned long, name, section->name); + KERNAUX_CAST_CONST(unsigned long, type, section->type); + KERNAUX_CAST_CONST(unsigned long, addr, section->addr); + KERNAUX_CAST_CONST(unsigned long, offset, section->offset); + KERNAUX_CAST_CONST(unsigned long, size, section->size); + KERNAUX_CAST_CONST(unsigned long, link, section->link); + KERNAUX_CAST_CONST(unsigned long, info, section->info); + KERNAUX_CAST_CONST(unsigned long, addralign, section->addralign); + KERNAUX_CAST_CONST(unsigned long, s_entsize, section->entsize); + + const char *const type_name = +#ifdef WITH_ELF + KernAux_ELF_Section_Type_to_str(section->type); +#else + "?"; +#endif + + PRINTLNF(" [%zu]: {", index); + PRINTLNF(" name: %lu", name); + PRINTLNF(" type: %lu (%s)", type, type_name); + PRINT (" flags: "); + KernAux_ELF_Section_Flags_print( + section->flags, + display, + 6, + 2, + false + ); + PRINTLNF(" addr: 0x%lx", addr); + PRINTLNF(" offset: 0x%lx", offset); + PRINTLNF(" size: %lu", size); + PRINTLNF(" link: %lu", link); + PRINTLNF(" info: %lu", info); + PRINTLNF(" addralign: %lu", addralign); + PRINTLNF(" entsize: %lu", s_entsize); + PRINTLN (" }"); + + section = + (const struct KernAux_ELF_Section*) + (((const uint8_t*)section) + tag->entsize); + } + + PRINTLN(" ]"); + } + + FOOTER; +} + +void KernAux_Multiboot2_ITag_APMTable_print( + const struct KernAux_Multiboot2_ITag_APMTable *const tag, + const KernAux_Display display +) { + HEADER(APMTable); + + KERNAUX_CAST_CONST(unsigned long, version, tag->version); + KERNAUX_CAST_CONST(unsigned long, cseg, tag->cseg); + KERNAUX_CAST_CONST(unsigned long, offset, tag->offset); + KERNAUX_CAST_CONST(unsigned long, cseg_16, tag->cseg_16); + KERNAUX_CAST_CONST(unsigned long, dseg, tag->dseg); + KERNAUX_CAST_CONST(unsigned long, flags, tag->flags); + KERNAUX_CAST_CONST(unsigned long, cseg_len, tag->cseg_len); + KERNAUX_CAST_CONST(unsigned long, cseg_16_len, tag->cseg_16_len); + KERNAUX_CAST_CONST(unsigned long, dseg_len, tag->dseg_len); + + PRINTLNF(" u16 version: %lu", version); + PRINTLNF(" u16 cseg: %lu", cseg); + PRINTLNF(" u32 offset: %lu", offset); + PRINTLNF(" u16 cseg_16: %lu", cseg_16); + PRINTLNF(" u16 dseg: %lu", dseg); + PRINTLNF(" u16 flags: %lu", flags); + PRINTLNF(" u16 cseg_len: %lu", cseg_len); + PRINTLNF(" u16 cseg_16_len: %lu", cseg_16_len); + PRINTLNF(" u16 dseg_len: %lu", dseg_len); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr_print( + const struct KernAux_Multiboot2_ITag_EFI32bitSystemTablePtr *const tag, + const KernAux_Display display +) { + HEADER(EFI32bitSystemTablePtr); + + KERNAUX_CAST_CONST(unsigned long, pointer, tag->pointer); + + PRINTLNF(" u32 pointer: %lu", pointer); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr_print( + const struct KernAux_Multiboot2_ITag_EFI64bitSystemTablePtr *const tag, + const KernAux_Display display +) { + HEADER(EFI64bitSystemTablePtr); + + KERNAUX_CAST_CONST(unsigned long long, pointer, tag->pointer); + + PRINTLNF(" u64 pointer: %llu", pointer); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_SMBIOSTables_print( + const struct KernAux_Multiboot2_ITag_SMBIOSTables *const tag, + const KernAux_Display display +) { + HEADER(SMBIOSTables); + + KERNAUX_CAST_CONST(unsigned long, major, tag->major); + KERNAUX_CAST_CONST(unsigned long, minor, tag->minor); + KERNAUX_CAST_CONST(unsigned long, reserved0, tag->reserved[0]); + KERNAUX_CAST_CONST(unsigned long, reserved1, tag->reserved[1]); + KERNAUX_CAST_CONST(unsigned long, reserved2, tag->reserved[2]); + KERNAUX_CAST_CONST(unsigned long, reserved3, tag->reserved[3]); + KERNAUX_CAST_CONST(unsigned long, reserved4, tag->reserved[4]); + KERNAUX_CAST_CONST(unsigned long, reserved5, tag->reserved[5]); + + PRINTLNF(" u8 major: %lu", major); + PRINTLNF(" u8 minor: %lu", minor); + PRINTLNF(" u8 reserved[6]: [0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx]", + reserved0, reserved1, reserved2, + reserved3, reserved4, reserved5 + ); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_ACPIOldRSDP_print( + const struct KernAux_Multiboot2_ITag_ACPIOldRSDP *const tag, + const KernAux_Display display +) { + HEADER(ACPIOldRSDP); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_ACPINewRSDP_print( + const struct KernAux_Multiboot2_ITag_ACPINewRSDP *const tag, + const KernAux_Display display +) { + HEADER(ACPINewRSDP); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_NetworkingInfo_print( + const struct KernAux_Multiboot2_ITag_NetworkingInfo *const tag, + const KernAux_Display display +) { + HEADER(NetworkingInfo); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFIMemoryMap_print( + const struct KernAux_Multiboot2_ITag_EFIMemoryMap *const tag, + const KernAux_Display display +) { + HEADER(EFIMemoryMap); + + KERNAUX_CAST_CONST(unsigned long, descr_size, tag->descriptor_size); + KERNAUX_CAST_CONST(unsigned long, descr_version, tag->descriptor_version); + + PRINTLNF(" u32 descriptor_size: %lu", descr_size); + PRINTLNF(" u32 descriptor_version: %lu", descr_version); + + // TODO: Print data? + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated_print( + const struct KernAux_Multiboot2_ITag_EFIBootServicesNotTerminated *const tag, + const KernAux_Display display +) { + HEADER(EFIBootServicesNotTerminated); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr_print( + const struct KernAux_Multiboot2_ITag_EFI32bitImageHandlePtr *const tag, + const KernAux_Display display +) { + HEADER(EFI32bitImageHandlePtr); + + KERNAUX_CAST_CONST(unsigned long, pointer, tag->pointer); + + PRINTLNF(" u32 pointer: %lu", pointer); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr_print( + const struct KernAux_Multiboot2_ITag_EFI64bitImageHandlePtr *const tag, + const KernAux_Display display +) { + HEADER(EFI64bitImageHandlePtr); + + KERNAUX_CAST_CONST(unsigned long long, pointer, tag->pointer); + + PRINTLNF(" u64 pointer: %llu", pointer); + + FOOTER; +} + +void KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr_print( + const struct KernAux_Multiboot2_ITag_ImageLoadBasePhysAddr *const tag, + const KernAux_Display display +) { + HEADER(ImageLoadBasePhysAddr); + + KERNAUX_CAST_CONST(unsigned long, load_base_addr, tag->load_base_addr); + + PRINTLNF(" u32 load_base_addr: 0x%lx", load_base_addr); + + FOOTER; +} + +void KernAux_ELF_Section_Flags_print( + const uint16_t flags, + const KernAux_Display display, + const unsigned basic_indentation, + const unsigned indentation_delta, + const bool indent_first +) { + KERNAUX_CAST_CONST(unsigned long, flags_ul, flags); + + if (indent_first) INDENT; + PRINTF("0x%lx (", flags_ul); + + bool is_first = true; + + for ( + size_t index = 0; + index < sizeof(section_flag_names) / sizeof(section_flag_names[0]); + ++index + ) { + if (flags & section_flag_names[index].number) { + if (is_first) { + PRINTLN(""); + } else { + PRINTLN(" |"); + } + + INDENT; + INDENT_MORE; + PRINTF("%s", section_flag_names[index].name); + is_first = false; + } + } + + if (is_first) { + PRINTLN(")"); + } else { + PRINTLN(""); + INDENT; + PRINTLN(")"); + } +} diff --git a/src/ntoa.c b/src/ntoa.c new file mode 100644 index 0000000..70d9611 --- /dev/null +++ b/src/ntoa.c @@ -0,0 +1,119 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include + +#include + +char *kernaux_utoa(uint64_t value, char *buffer, int base, const char *prefix) +{ + KERNAUX_NOTNULL(buffer); + + // Protect caller from invalid state in case of future assertions + // cppcheck-suppress ctunullpointer + *buffer = '\0'; + + switch (base) { + case 'b': case 'B': base = 2; break; + case 'o': case 'O': base = 8; break; + case 'd': case 'D': base = 10; break; + case 'h': case 'x': base = 16; break; + case 'H': case 'X': base = -16; break; + } + + // Uppercase + char alpha = 'a'; + if (base < 0) { + alpha = 'A'; + base = -base; + } + + KERNAUX_ASSERT(base >= 2 && base <= 36); + + // Write prefix + if (prefix) { + for (size_t prefix_len = 1; *prefix; ++prefix_len) { + if (prefix_len > KERNAUX_NTOA_MAX_PREFIX_LEN) { + // Protect caller from invalid state + *buffer = '\0'; + KERNAUX_PANIC("prefix is too long"); + } + *(buffer++) = *(prefix++); + } + } + + // Write number + char *pos = buffer; + if (value == 0) *(pos++) = '0'; + while (value > 0) { + // cppcheck-suppress zerodivcond + const char mod = value % base; + *(pos++) = mod < 10 ? mod + '0' : mod - 10 + alpha; + // cppcheck-suppress zerodivcond + value /= base; + } + char *const result = pos; + *(pos--) = '\0'; + + // Reverse number + while (buffer < pos) { + const char tmp = *buffer; + *(buffer++) = *pos; + *(pos--) = tmp; + } + + return result; +} + +char *kernaux_itoa(int64_t value, char *buffer, int base, const char *const prefix) +{ + if (value >= 0) { + return kernaux_utoa(value, buffer, base, prefix); + } else { + *(buffer++) = '-'; + return kernaux_utoa(-value, buffer, base, prefix); + } +} + +char *kernaux_utoa2(uint64_t value, char *buffer) +{ + return kernaux_utoa(value, buffer, 'b', KERNAUX_NTOA_DEFAULT_PREFIX_2); +} + +char *kernaux_itoa2(int64_t value, char *buffer) +{ + return kernaux_itoa(value, buffer, 'b', KERNAUX_NTOA_DEFAULT_PREFIX_2); +} + +char *kernaux_utoa8(uint64_t value, char *buffer) +{ + return kernaux_utoa(value, buffer, 'o', KERNAUX_NTOA_DEFAULT_PREFIX_8); +} + +char *kernaux_itoa8(int64_t value, char *buffer) +{ + return kernaux_itoa(value, buffer, 'o', KERNAUX_NTOA_DEFAULT_PREFIX_8); +} + +char *kernaux_utoa10(uint64_t value, char *buffer) +{ + return kernaux_utoa(value, buffer, 'd', NULL); +} + +char *kernaux_itoa10(int64_t value, char *buffer) +{ + return kernaux_itoa(value, buffer, 'd', NULL); +} + +char *kernaux_utoa16(uint64_t value, char *buffer) +{ + return kernaux_utoa(value, buffer, 'x', KERNAUX_NTOA_DEFAULT_PREFIX_16); +} + +char *kernaux_itoa16(int64_t value, char *buffer) +{ + return kernaux_itoa(value, buffer, 'x', KERNAUX_NTOA_DEFAULT_PREFIX_16); +} diff --git a/src/pfa.c b/src/pfa.c new file mode 100644 index 0000000..f4535a6 --- /dev/null +++ b/src/pfa.c @@ -0,0 +1,163 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include + +#include + +#define PAGE_INDEX(page_addr) ((page_addr) / KERNAUX_PFA_PAGE_SIZE) + +#define FLAG_INDEX_FROM_INDEX(page_index) ((page_index) / 8) +#define FLAG_MASK_FROM_INDEX(page_index) KERNAUX_BITS((page_index) % 8) + +#define FLAG_INDEX_FROM_ADDR(page_addr) \ + (FLAG_INDEX_FROM_INDEX(PAGE_INDEX(page_addr))) +#define FLAG_MASK_FROM_ADDR(page_addr) \ + (FLAG_MASK_FROM_INDEX(PAGE_INDEX(page_addr))) + +#define GET_FLAG_FROM_INDEX(pfa, page_index) \ + (!!((pfa)->flags[FLAG_INDEX_FROM_INDEX(page_index)] & \ + FLAG_MASK_FROM_INDEX(page_index))) +#define GET_FLAG_FROM_ADDR(pfa, page_addr) \ + (!!((pfa)->flags[FLAG_INDEX_FROM_ADDR(page_addr)] & \ + FLAG_MASK_FROM_ADDR(page_addr))) + +#define FLAG_TRUE_FROM_INDEX(pfa, page_index) \ + ((pfa)->flags[FLAG_INDEX_FROM_INDEX(page_index)] |= \ + FLAG_MASK_FROM_INDEX(page_index)) +#define FLAG_FALSE_FROM_INDEX(pfa, page_index) \ + ((pfa)->flags[FLAG_INDEX_FROM_INDEX(page_index)] &= \ + ~FLAG_MASK_FROM_INDEX(page_index)) + +static void KernAux_PFA_mark( + KernAux_PFA pfa, + bool status, + size_t start, + size_t end +); + +void KernAux_PFA_initialize(const KernAux_PFA pfa) +{ + KERNAUX_NOTNULL(pfa); + // cppcheck-suppress ctunullpointer + memset(pfa->flags, 0, sizeof(pfa->flags)); +} + +bool KernAux_PFA_is_available(const KernAux_PFA pfa, const size_t page_addr) +{ + KERNAUX_NOTNULL(pfa); + KERNAUX_ASSERT(page_addr % KERNAUX_PFA_PAGE_SIZE == 0); + + // cppcheck-suppress ctunullpointer + return GET_FLAG_FROM_ADDR(pfa, page_addr); +} + +void KernAux_PFA_mark_available( + const KernAux_PFA pfa, + const size_t start, + const size_t end +) { + KernAux_PFA_mark(pfa, true, start, end); +} + +void KernAux_PFA_mark_unavailable( + const KernAux_PFA pfa, + const size_t start, + const size_t end +) { + KernAux_PFA_mark(pfa, false, start, end); +} + +void KernAux_PFA_mark( + const KernAux_PFA pfa, + const bool status, + size_t start, + size_t end +) { + KERNAUX_NOTNULL(pfa); + KERNAUX_ASSERT(start < end); + + const size_t start_rem = start % KERNAUX_PFA_PAGE_SIZE; + const size_t end_rem = (end + 1) % KERNAUX_PFA_PAGE_SIZE; + + if (start_rem != 0) { + start = start - start_rem + KERNAUX_PFA_PAGE_SIZE; + } + + if (end_rem != 0) { + end = end - end_rem; + } + + const size_t start_index = start / KERNAUX_PFA_PAGE_SIZE; + const size_t end_index = end / KERNAUX_PFA_PAGE_SIZE; + + for (size_t index = start_index; index <= end_index; ++index) { + if (status) { + FLAG_TRUE_FROM_INDEX(pfa, index); + } else { + FLAG_FALSE_FROM_INDEX(pfa, index); + } + } +} + +size_t KernAux_PFA_alloc_pages(const KernAux_PFA pfa, size_t mem_size) +{ + KERNAUX_NOTNULL(pfa); + + const size_t mem_rem = mem_size % KERNAUX_PFA_PAGE_SIZE; + + if (mem_rem != 0) { + mem_size = mem_size - mem_rem + KERNAUX_PFA_PAGE_SIZE; + } + + const size_t pages_count = mem_size / KERNAUX_PFA_PAGE_SIZE; + + // We start from 1 because 0 indicates failure. + // It is not very useful to alloc page at address 0; + for (size_t index = 1, start = 0; + index < KERNAUX_PFA_PAGES_COUNT_MAX; + ++index) + { + if (!GET_FLAG_FROM_INDEX(pfa, index)) { + start = 0; + continue; + } + + if (start == 0) start = index; + + if (index - start + 1 == pages_count) { + for (; index >= start; --index) { + FLAG_FALSE_FROM_INDEX(pfa, index); + } + return start * KERNAUX_PFA_PAGE_SIZE; + } + } + + return 0; +} + +void KernAux_PFA_free_pages( + const KernAux_PFA pfa, + const size_t page_addr, + size_t mem_size +) { + KERNAUX_NOTNULL(pfa); + KERNAUX_ASSERT(page_addr % KERNAUX_PFA_PAGE_SIZE == 0); + + const size_t mem_rem = mem_size % KERNAUX_PFA_PAGE_SIZE; + + if (mem_rem != 0) { + mem_size = mem_size - mem_rem + KERNAUX_PFA_PAGE_SIZE; + } + + const size_t start_index = page_addr / KERNAUX_PFA_PAGE_SIZE; + const size_t pages_count = mem_size / KERNAUX_PFA_PAGE_SIZE; + + for (size_t index = 0; index < pages_count; ++index) { + FLAG_TRUE_FROM_INDEX(pfa, start_index + index); + } +} diff --git a/src/printf.c b/src/printf.c new file mode 100644 index 0000000..d567c0f --- /dev/null +++ b/src/printf.c @@ -0,0 +1,665 @@ +/** + * The code was taken from Marco Paland's printf. + * + * Copyright (c) 2014-2019 Marco Paland + * Copyright (c) 2021-2022 Alex Kotov + * + * Tiny [v]fprintf, sfprintf and [v]snprintf implementation, optimized for speed + * on embedded systems with a very limited resources. These routines are thread + * safe and reentrant! + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include + +#include +#include +#include + +// import float.h for DBL_MAX +#ifdef ENABLE_FLOAT +#include +#endif + +// 'ntoa' conversion buffer size, this must be big enough to hold one converted +// numeric number including padded zeros (dynamically created on stack) +#define PRINTF_NTOA_BUFFER_SIZE 32u + +// 'ftoa' conversion buffer size, this must be big enough to hold one converted +// float number including padded zeros (dynamically created on stack) +#define PRINTF_FTOA_BUFFER_SIZE 32u + +// define the default floating point precision +#define PRINTF_DEFAULT_FLOAT_PRECISION 6u + +// define the largest float suitable to print with %f +#define PRINTF_MAX_FLOAT 1e9 + +// output function type +typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); + +// wrapper (used as buffer) for output function type +typedef struct { + void (*fct)(char character, void* arg); + void* arg; +} out_fct_wrap_type; + +static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va); + +static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen); +static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen); +static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen); +static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags); +static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags); +static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags); +static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags); + +#ifdef ENABLE_FLOAT +static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); +#endif // ENABLE_FLOAT + +/***************************** + * Implementations: main API * + *****************************/ + +int kernaux_fprintf(void (*out)(char, void*), void *data, const char* format, ...) +{ + KERNAUX_NOTNULL(out); + KERNAUX_NOTNULL(format); + + va_list va; + va_start(va, format); + const out_fct_wrap_type out_fct_wrap = { out, data }; + const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); + va_end(va); + return ret; +} + +int kernaux_vfprintf(void (*out)(char, void*), void *data, const char* format, va_list va) +{ + KERNAUX_NOTNULL(out); + KERNAUX_NOTNULL(format); + + const out_fct_wrap_type out_fct_wrap = { out, data }; + return _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); +} + +int kernaux_snprintf(char* buffer, size_t count, const char* format, ...) +{ + KERNAUX_NOTNULL(buffer); + KERNAUX_NOTNULL(format); + + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); + va_end(va); + return ret; +} + +int kernaux_vsnprintf(char* buffer, size_t count, const char* format, va_list va) +{ + KERNAUX_NOTNULL(buffer); + KERNAUX_NOTNULL(format); + + return _vsnprintf(_out_buffer, buffer, count, format, va); +} + +int kernaux_sprintf(char* buffer, const char* format, ...) +{ + KERNAUX_NOTNULL(buffer); + KERNAUX_NOTNULL(format); + + va_list va; + va_start(va, format); + const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); + va_end(va); + return ret; +} + +/****************************************** + * Implementation: main internal function * + ******************************************/ + +int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) +{ + KERNAUX_NOTNULL(format); + + size_t idx = 0u; + + if (!buffer) { + // use null output function + out = _out_null; + } + + while (*format) + { + // format specifier? %[flags][width][.precision][length] + if (*format != '%') { + // no + out(*format, buffer, idx++, maxlen); + format++; + continue; + } else { + // yes, evaluate it + format++; + } + + struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create_out(&format); + + if (spec.set_width) { + KernAux_PrintfFmt_Spec_set_width(&spec, va_arg(va, int)); + } + if (spec.set_precision) { + KernAux_PrintfFmt_Spec_set_precision(&spec, va_arg(va, int)); + } + + // evaluate specifier + switch (spec.type) { + case KERNAUX_PRINTF_FMT_TYPE_INT: + if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG) { + const long long value = va_arg(va, long long); + idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, spec.base, spec.precision, spec.width, spec.flags); + } else if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LONG) { + const long value = va_arg(va, long); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, spec.base, spec.precision, spec.width, spec.flags); + } else { + const int value = (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_CHAR) ? (char)va_arg(va, int) : (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, spec.base, spec.precision, spec.width, spec.flags); + } + break; + + case KERNAUX_PRINTF_FMT_TYPE_UINT: + if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, spec.base, spec.precision, spec.width, spec.flags); + } else if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LONG) { + idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, spec.base, spec.precision, spec.width, spec.flags); + } else { + const unsigned int value = (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); + idx = _ntoa_long(out, buffer, idx, maxlen, value, false, spec.base, spec.precision, spec.width, spec.flags); + } + break; + +#ifdef ENABLE_FLOAT + case KERNAUX_PRINTF_FMT_TYPE_FLOAT: + idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), spec.precision, spec.width, spec.flags); + break; + + case KERNAUX_PRINTF_FMT_TYPE_EXP: + idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), spec.precision, spec.width, spec.flags); + break; +#endif // ENABLE_FLOAT + + case KERNAUX_PRINTF_FMT_TYPE_CHAR: + { + unsigned int l = 1u; + // pre padding + if (!(spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT)) { + while (l++ < spec.width) { + out(' ', buffer, idx++, maxlen); + } + } + // char output + out((char)va_arg(va, int), buffer, idx++, maxlen); + // post padding + if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) { + while (l++ < spec.width) { + out(' ', buffer, idx++, maxlen); + } + } + break; + } + + case KERNAUX_PRINTF_FMT_TYPE_STR: + { + const char* p = va_arg(va, char*); + unsigned int l = strnlen(p, spec.precision ? spec.precision : (size_t)-1); + // pre padding + if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) { + l = (l < spec.precision ? l : spec.precision); + } + if (!(spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT)) { + while (l++ < spec.width) { + out(' ', buffer, idx++, maxlen); + } + } + // string output + while ((*p != 0) && (!(spec.flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) || spec.precision--)) { + out(*(p++), buffer, idx++, maxlen); + } + // post padding + if (spec.flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) { + while (l++ < spec.width) { + out(' ', buffer, idx++, maxlen); + } + } + break; + } + + case KERNAUX_PRINTF_FMT_TYPE_PTR: + { + const bool is_ll = sizeof(uintptr_t) == sizeof(long long); + // cppcheck-suppress knownConditionTrueFalse + if (is_ll) { + idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16u, spec.precision, spec.width, spec.flags); + } else { + idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16u, spec.precision, spec.width, spec.flags); + } + break; + } + + case KERNAUX_PRINTF_FMT_TYPE_PERCENT: + out('%', buffer, idx++, maxlen); + break; + + default: + out(*format, buffer, idx++, maxlen); + ++format; + break; + } + } + + // termination + out((char)0, buffer, idx < maxlen ? idx : maxlen - 1u, maxlen); + + // return written chars without terminating \0 + return (int)idx; +} + +/************************************* + * Implementations: helper functions * + *************************************/ + +// internal buffer output +void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) +{ + if (idx < maxlen) { + ((char*)buffer)[idx] = character; + } +} + +// internal null output +void _out_null(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)character; (void)buffer; (void)idx; (void)maxlen; +} + +// internal output function wrapper +void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) +{ + (void)idx; (void)maxlen; + if (character) { + // buffer is the output fct pointer + ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); + } +} + +// output the specified string in reverse, taking care of any zero-padding +size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) +{ + const size_t start_idx = idx; + + // pad spaces up to given width + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) && !(flags & KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD)) { + for (size_t i = len; i < width; i++) { + out(' ', buffer, idx++, maxlen); + } + } + + // reverse string + while (len) { + out(buf[--len], buffer, idx++, maxlen); + } + + // append pad spaces up to given width + if (flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) { + while (idx - start_idx < width) { + out(' ', buffer, idx++, maxlen); + } + } + + return idx; +} + +// internal itoa format +size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) +{ + // pad leading zeros + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT)) { + if (width && (flags & KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD) && (negative || (flags & (KERNAUX_PRINTF_FMT_FLAGS_PLUS | KERNAUX_PRINTF_FMT_FLAGS_SPACE)))) { + width--; + } + while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + while ((flags & KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + // handle hash + if (flags & KERNAUX_PRINTF_FMT_FLAGS_HASH) { + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { + len--; + if (len && (base == 16u)) { + len--; + } + } + if ((base == 16u) && !(flags & KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'x'; + } else if ((base == 16u) && (flags & KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'X'; + } else if ((base == 2u) && (len < PRINTF_NTOA_BUFFER_SIZE)) { + buf[len++] = 'b'; + } + if (len < PRINTF_NTOA_BUFFER_SIZE) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_NTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & KERNAUX_PRINTF_FMT_FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & KERNAUX_PRINTF_FMT_FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal itoa for 'long' type +size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0u; + + // no hash for 0 values + if (!value) { + flags &= ~KERNAUX_PRINTF_FMT_FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : ((flags & KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +// internal itoa for 'long long' type +size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) +{ + char buf[PRINTF_NTOA_BUFFER_SIZE]; + size_t len = 0u; + + // no hash for 0 values + if (!value) { + flags &= ~KERNAUX_PRINTF_FMT_FLAGS_HASH; + } + + // write if precision != 0 and value is != 0 + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) || value) { + do { + const char digit = (char)(value % base); + buf[len++] = digit < 10 ? '0' + digit : ((flags & KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE) ? 'A' : 'a') + digit - 10; + value /= base; + } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); + } + + return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); +} + +#ifdef ENABLE_FLOAT +// internal ftoa for fixed decimal floating point +size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // powers of 10 + static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; + + // test for special values + if (value != value) + return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); + if (value < -DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); + if (value > DBL_MAX) + return _out_rev(out, buffer, idx, maxlen, (flags & KERNAUX_PRINTF_FMT_FLAGS_PLUS) ? "fni+" : "fni", (flags & KERNAUX_PRINTF_FMT_FLAGS_PLUS) ? 4u : 3u, width, flags); + + // test for very large values + // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad + if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { + return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // test for negative + bool negative = false; + if (value < 0) { + negative = true; + value = 0 - value; + } + + // set default precision, if not set explicitly + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + char buf[PRINTF_FTOA_BUFFER_SIZE]; + size_t len = 0u; + + const unsigned int orig_prec = prec; + // limit precision to 9, cause a prec >= 10 can lead to overflow errors + if (prec > 9u) prec = 9u; + + int whole = (int)value; + double tmp = (value - whole) * pow10[prec]; + unsigned long frac = (unsigned long)tmp; + double diff = tmp - frac; + + if (diff > 0.5) { + ++frac; + // handle rollover, e.g. case 0.99 with prec 1 is 1.0 + if (frac >= pow10[prec]) { + frac = 0; + ++whole; + } + } else if (diff < 0.5) { + // TODO: do nothing? + } else if ((frac == 0u) || (frac & 1u)) { + // if halfway, round up if odd OR if last digit is 0 + ++frac; + } + + if (prec == 0u) { + diff = value - (double)whole; + // cppcheck-suppress redundantCondition + if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { + // exactly 0.5 and ODD, then round up + // 1.5 -> 2, but 2.5 -> 2 + ++whole; + } + } else { + unsigned int count = prec; + // now do fractional part, as an unsigned number + while (len < PRINTF_FTOA_BUFFER_SIZE) { + --count; + buf[len++] = (char)(48u + (frac % 10u)); + if (!(frac /= 10u)) { + break; + } + } + // add extra 0s + while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0u)) { + buf[len++] = '0'; + } + if (len < PRINTF_FTOA_BUFFER_SIZE) { + // add decimal + buf[len++] = '.'; + } + } + + // do whole part, number is reversed + while (len < PRINTF_FTOA_BUFFER_SIZE) { + buf[len++] = (char)(48 + (whole % 10)); + if (!(whole /= 10)) { + break; + } + } + + // pad leading zeros + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) && (flags & KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD)) { + if (width && (negative || (flags & (KERNAUX_PRINTF_FMT_FLAGS_PLUS | KERNAUX_PRINTF_FMT_FLAGS_SPACE)))) { + width--; + } + while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { + buf[len++] = '0'; + } + } + + if (len < PRINTF_FTOA_BUFFER_SIZE) { + if (negative) { + buf[len++] = '-'; + } else if (flags & KERNAUX_PRINTF_FMT_FLAGS_PLUS) { + buf[len++] = '+'; // ignore the space if the '+' exists + } else if (flags & KERNAUX_PRINTF_FMT_FLAGS_SPACE) { + buf[len++] = ' '; + } + } + + // This slows down the algorighm, but + // only if the precision was more than 9. + if (orig_prec > prec) { + const size_t space_left = PRINTF_FTOA_BUFFER_SIZE - len; + const size_t zeroes_wanted = orig_prec - prec; + const size_t delta = + space_left < zeroes_wanted ? space_left : zeroes_wanted; + + for (size_t rev_index = 0; rev_index < len; ++rev_index) { + const size_t index = len - 1 - rev_index; + buf[index + delta] = buf[index]; + } + + len += delta; + + for (size_t index = 0; index < delta; ++index) buf[index] = '0'; + } + + return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); +} + +// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse +size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) +{ + // check for NaN and special values + if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { + return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); + } + + // determine the sign + const bool negative = value < 0; + if (negative) { + value = -value; + } + + // default precision + if (!(flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION)) { + prec = PRINTF_DEFAULT_FLOAT_PRECISION; + } + + // determine the decimal exponent + // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) + union { + uint64_t U; + double F; + } conv; + + conv.F = value; + int exp2 = (int)((conv.U >> 52u) & 0x07ffu) - 1023; // effectively log2 + conv.U = (conv.U & ((1ull << 52u) - 1u)) | (102ull << 52u); // drop the exponent so conv.F is now in [1,2) + // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 + int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); + // now we want to compute 10^expval but we want to be sure it won't overflow + exp2 = (int)(expval * 3.321928094887362 + 0.5); + const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; + const double z2 = z * z; + conv.U = (uint64_t)(exp2 + 1023) << 52u; + // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex + conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); + // correct for rounding errors + if (value < conv.F) { + expval--; + conv.F /= 10; + } + + // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters + unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4u : 5u; + + // in "%g" mode, "prec" is the number of *significant figures* not decimals + if (flags & KERNAUX_PRINTF_FMT_FLAGS_ADAPT_EXP) { + // do we want to fall-back to "%f" mode? + if ((value >= 1e-4) && (value < 1e6)) { + if ((int)prec > expval) { + prec = (unsigned)((int)prec - expval - 1); + } else { + prec = 0; + } + flags |= KERNAUX_PRINTF_FMT_FLAGS_PRECISION; // make sure _ftoa respects precision + // no characters in exponent + minwidth = 0u; + expval = 0; + } else { + // we use one sigfig for the whole part + if ((prec > 0) && (flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION)) { + --prec; + } + } + } + + // will everything fit? + unsigned int fwidth = width; + if (width > minwidth) { + // we didn't fall-back so subtract the characters required for the exponent + fwidth -= minwidth; + } else { + // not enough characters, so go back to default sizing + fwidth = 0u; + } + if ((flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) && minwidth) { + // if we're padding on the right, DON'T pad the floating part + fwidth = 0u; + } + + // rescale the float value + if (expval) { + value /= conv.F; + } + + // output the floating part + const size_t start_idx = idx; + idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~KERNAUX_PRINTF_FMT_FLAGS_ADAPT_EXP); + + // output the exponent part + if (minwidth) { + // output the exponential symbol + out((flags & KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); + // output the exponent value + idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD | KERNAUX_PRINTF_FMT_FLAGS_PLUS); + // might need to right-pad spaces + if (flags & KERNAUX_PRINTF_FMT_FLAGS_LEFT) { + while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); + } + } + return idx; +} +#endif // ENABLE_FLOAT diff --git a/src/printf_fmt.c b/src/printf_fmt.c new file mode 100644 index 0000000..191ee36 --- /dev/null +++ b/src/printf_fmt.c @@ -0,0 +1,349 @@ +/** + * The code was taken from Marco Paland's printf. + * + * Copyright (c) 2014-2019 Marco Paland + * Copyright (c) 2021-2022 Alex Kotov + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include + +#include +#include +#include +#include +#include + +typedef struct KernAux_PrintfFmt_Spec *Spec; + +static void parse_flags (Spec spec, const char **format); +static void parse_width (Spec spec, const char **format); +static void parse_precision(Spec spec, const char **format); +static void parse_length (Spec spec, const char **format); +static void parse_type (Spec spec, const char **format); + +static unsigned int _atoi(const char** str); + +/*********************************** + * Public function implementations * + ***********************************/ + +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create_out( + const char **const format +) { + KERNAUX_NOTNULL(format); + + const struct KernAux_PrintfFmt_Spec spec = + KernAux_PrintfFmt_Spec_create(*format); + *format = spec.format_limit; + return spec; +} + +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create_out_new( + const char *const format, + const char **const new_format +) { + KERNAUX_NOTNULL(format); + KERNAUX_NOTNULL(new_format); + + *new_format = NULL; + const struct KernAux_PrintfFmt_Spec spec = + KernAux_PrintfFmt_Spec_create(format); + *new_format = spec.format_limit; + return spec; +} + +struct KernAux_PrintfFmt_Spec KernAux_PrintfFmt_Spec_create(const char *format) +{ + KERNAUX_NOTNULL(format); + + struct KernAux_PrintfFmt_Spec spec; + + spec.format_start = format; + + spec.flags = 0u; + spec.width = 0u; + spec.precision = 0u; + spec.type = KERNAUX_PRINTF_FMT_TYPE_NONE; + spec.base = 0; + + spec.set_width = false; + spec.set_precision = false; + + parse_flags(&spec, &format); + parse_width(&spec, &format); + parse_precision(&spec, &format); + parse_length(&spec, &format); + parse_type(&spec, &format); + + spec.format_limit = format; + + return spec; +} + +void KernAux_PrintfFmt_Spec_set_width(const Spec spec, const int width) +{ + KERNAUX_NOTNULL(spec); + + if (width < 0) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LEFT; // reverse padding + spec->width = (unsigned int)-width; + } else { + spec->width = (unsigned int)width; + } +} + +void KernAux_PrintfFmt_Spec_set_precision(const Spec spec, const int precision) +{ + KERNAUX_NOTNULL(spec); + + spec->precision = precision > 0 ? (unsigned int)precision : 0u; +} + +/************************************ + * Private function implementations * + ************************************/ + +void parse_flags(const Spec spec, const char **const format) +{ + KERNAUX_NOTNULL(spec); + KERNAUX_NOTNULL(format); + KERNAUX_ASSERT(*format); + + bool running = true; + do { + switch (**format) { + case '0': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD; + ++(*format); + break; + case '-': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LEFT; + ++(*format); + break; + case '+': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_PLUS; + ++(*format); + break; + case ' ': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_SPACE; + ++(*format); + break; + case '#': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_HASH; + ++(*format); + break; + default: running = false; break; + } + } while (running); +} + +void parse_width(const Spec spec, const char **const format) +{ + KERNAUX_NOTNULL(spec); + KERNAUX_NOTNULL(format); + KERNAUX_ASSERT(*format); + + if (isdigit(**format)) { + spec->width = _atoi(format); + spec->set_width = false; + } else if (**format == '*') { + ++(*format); + spec->set_width = true; + } else { + spec->set_width = false; + } +} + +void parse_precision(const Spec spec, const char **const format) +{ + KERNAUX_NOTNULL(spec); + KERNAUX_NOTNULL(format); + KERNAUX_ASSERT(*format); + + if (**format == '.') { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_PRECISION; + ++(*format); + if (isdigit(**format)) { + spec->precision = _atoi(format); + spec->set_precision = false; + } else if (**format == '*') { + ++(*format); + spec->set_precision = true; + } else { + spec->set_precision = false; + } + } else { + spec->set_precision = false; + } +} + +void parse_length(const Spec spec, const char **const format) +{ + KERNAUX_NOTNULL(spec); + KERNAUX_NOTNULL(format); + KERNAUX_ASSERT(*format); + + switch (**format) { + case 'l': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG; + ++(*format); + if (**format == 'l') { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG; + ++(*format); + } + break; + case 'h': + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_SHORT; + ++(*format); + if (**format == 'h') { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_CHAR; + ++(*format); + } + break; + case 't': + if (sizeof(ptrdiff_t) == sizeof(long)) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG; + } else { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG; + } + ++(*format); + break; + case 'j': + if (sizeof(ptrdiff_t) == sizeof(long)) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG; + } else { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG; + } + ++(*format); + break; + case 'z': + if (sizeof(ptrdiff_t) == sizeof(long)) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG; + } else { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_LONG_LONG; + } + ++(*format); + break; + default: + break; + } +} + +void parse_type(const Spec spec, const char **const format) +{ + KERNAUX_NOTNULL(spec); + KERNAUX_NOTNULL(format); + KERNAUX_ASSERT(*format); + + switch (**format) { + case 'd': + case 'i': + case 'u': + case 'x': + case 'X': + case 'o': + case 'b': + // set the base + if (**format == 'x' || **format == 'X') { + spec->base = 16u; + } else if (**format == 'o') { + spec->base = 8u; + } else if (**format == 'b') { + spec->base = 2u; + } else { + spec->base = 10u; + // no hash for dec format + spec->flags &= ~KERNAUX_PRINTF_FMT_FLAGS_HASH; + } + // uppercase + if (**format == 'X') { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE; + } + + // no plus or space flag for u, x, X, o, b + if ((**format != 'i') && (**format != 'd')) { + spec->flags &= ~(KERNAUX_PRINTF_FMT_FLAGS_PLUS | + KERNAUX_PRINTF_FMT_FLAGS_SPACE); + } + + // ignore '0' flag when precision is given + if (spec->flags & KERNAUX_PRINTF_FMT_FLAGS_PRECISION) { + spec->flags &= ~KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD; + } + + // convert the integer + if ((**format == 'i') || (**format == 'd')) { + spec->type = KERNAUX_PRINTF_FMT_TYPE_INT; + } else { + spec->type = KERNAUX_PRINTF_FMT_TYPE_UINT; + } + ++(*format); + break; + +#ifdef ENABLE_FLOAT + case 'f': + case 'F': + if (**format == 'F') { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE; + } + spec->type = KERNAUX_PRINTF_FMT_TYPE_FLOAT; + ++(*format); + break; + + case 'e': + case 'E': + case 'g': + case 'G': + if ((**format == 'g') || (**format == 'G')) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ADAPT_EXP; + } + if ((**format == 'E') || (**format == 'G')) { + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE; + } + spec->type = KERNAUX_PRINTF_FMT_TYPE_EXP; + ++(*format); + break; +#endif // ENABLE_FLOAT + + case 'c': + spec->type = KERNAUX_PRINTF_FMT_TYPE_CHAR; + ++(*format); + break; + + case 's': + spec->type = KERNAUX_PRINTF_FMT_TYPE_STR; + ++(*format); + break; + + case 'p': + spec->width = sizeof(void*) * 2u; + spec->flags |= KERNAUX_PRINTF_FMT_FLAGS_ZEROPAD | + KERNAUX_PRINTF_FMT_FLAGS_UPPERCASE; + spec->type = KERNAUX_PRINTF_FMT_TYPE_PTR; + ++(*format); + break; + + case '%': + spec->type = KERNAUX_PRINTF_FMT_TYPE_PERCENT; + ++(*format); + break; + + default: + spec->type = KERNAUX_PRINTF_FMT_TYPE_NONE; + break; + } +} + +// internal ASCII string to unsigned int conversion +unsigned int _atoi(const char** str) +{ + const int result = atoi(*str); + while (isdigit(**str)) (*str)++; + return result; +} diff --git a/src/runtime.c b/src/runtime.c new file mode 100644 index 0000000..e7e7888 --- /dev/null +++ b/src/runtime.c @@ -0,0 +1,17 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +KernAux_Assert_Cb kernaux_assert_cb = NULL; + +void kernaux_assert_do( + const char *const file, + const int line, + const char *const msg +) { + if (kernaux_assert_cb) kernaux_assert_cb(file, line, msg); +} diff --git a/src/units.c b/src/units.c new file mode 100644 index 0000000..911f186 --- /dev/null +++ b/src/units.c @@ -0,0 +1,149 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "assert.h" + +#include +#include + +#include + +#define TMP_BUFFER_SIZE (64) + +bool kernaux_units_human_raw( + const uint64_t value, + const enum KernAux_Unit unit, + char *const buffer, + const size_t buffer_size +) { + KERNAUX_NOTNULL(buffer); + KERNAUX_ASSERT(buffer_size > 0); + + char tmp_buffer[TMP_BUFFER_SIZE]; + char *tmp = tmp_buffer; + + kernaux_utoa10(value, tmp); + while (*tmp != '\0') ++tmp; + *(tmp++) = ' '; + + switch (unit) { + case KERNAUX_UNIT_BIT: + *(tmp++) = 'b'; + *(tmp++) = 'i'; + *(tmp++) = 't'; + break; + case KERNAUX_UNIT_BYTE: + *(tmp++) = 'B'; + *(tmp++) = 'y'; + *(tmp++) = 't'; + *(tmp++) = 'e'; + break; + } + + *(tmp++) = '\0'; + + const size_t tmp_size = strlen(tmp_buffer) + 1; + if (tmp_size > buffer_size) return false; + + strcpy(buffer, tmp_buffer); + return true; +} + +bool kernaux_units_human_dec( + const uint64_t value, + const enum KernAux_Unit unit, + const enum KernAux_UnitPrefixDec prefix, + char *const buffer, + const size_t buffer_size +) { + KERNAUX_NOTNULL(buffer); + KERNAUX_ASSERT(buffer_size > 0); + + char tmp_buffer[TMP_BUFFER_SIZE]; + char *tmp = tmp_buffer; + + kernaux_utoa10(value, tmp); + while (*tmp != '\0') ++tmp; + *(tmp++) = ' '; + + switch (prefix) { + case KERNAUX_UNITPFX_KILO: + *(tmp++) = 'k'; + break; + case KERNAUX_UNITPFX_MEGA: + *(tmp++) = 'M'; + break; + case KERNAUX_UNITPFX_GIGA: + *(tmp++) = 'G'; + break; + } + + switch (unit) { + case KERNAUX_UNIT_BIT: + *(tmp++) = 'b'; + *(tmp++) = 'i'; + *(tmp++) = 't'; + break; + case KERNAUX_UNIT_BYTE: + *(tmp++) = 'B'; + break; + } + + *(tmp++) = '\0'; + + const size_t tmp_size = strlen(tmp_buffer) + 1; + if (tmp_size > buffer_size) return false; + + strcpy(buffer, tmp_buffer); + return true; +} + +bool kernaux_units_human_bin( + const uint64_t value, + const enum KernAux_Unit unit, + const enum KernAux_UnitPrefixBin prefix, + char *const buffer, + const size_t buffer_size +) { + char tmp_buffer[TMP_BUFFER_SIZE]; + char *tmp = tmp_buffer; + + kernaux_utoa10(value, tmp); + while (*tmp != '\0') ++tmp; + *(tmp++) = ' '; + + switch (prefix) { + case KERNAUX_UNITPFX_KIBI: + *(tmp++) = 'K'; + *(tmp++) = 'i'; + break; + case KERNAUX_UNITPFX_MEBI: + *(tmp++) = 'M'; + *(tmp++) = 'i'; + break; + case KERNAUX_UNITPFX_GIBI: + *(tmp++) = 'G'; + *(tmp++) = 'i'; + break; + } + + switch (unit) { + case KERNAUX_UNIT_BIT: + *(tmp++) = 'b'; + *(tmp++) = 'i'; + *(tmp++) = 't'; + break; + case KERNAUX_UNIT_BYTE: + *(tmp++) = 'B'; + break; + } + + *(tmp++) = '\0'; + + const size_t tmp_size = strlen(tmp_buffer) + 1; + if (tmp_size > buffer_size) return false; + + strcpy(buffer, tmp_buffer); + return true; +} diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..957f241 --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1,33 @@ +/multiboot2_header_print0 +/multiboot2_header_print1 +/multiboot2_header_print2 +/multiboot2_info_print0 +/multiboot2_info_print1 +/multiboot2_info_print2 +/test_arch_i386 +/test_cmdline +/test_cmdline_gen +/test_cmdline_gen.c +/test_elf +/test_free_list +/test_mbr +/test_multiboot2_common_packing +/test_multiboot2_header_helpers +/test_multiboot2_header_print +/test_multiboot2_header_print.c +/test_multiboot2_header_validation +/test_multiboot2_info_convert_memmap +/test_multiboot2_info_helpers +/test_multiboot2_info_print +/test_multiboot2_info_print.c +/test_multiboot2_info_validation +/test_ntoa +/test_ntoa_assert +/test_pfa +/test_pfa_assert +/test_printf +/test_printf_fmt_gen +/test_printf_fmt_gen.c +/test_printf_gen +/test_printf_gen.c +/test_units_human diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..3fc5cbc --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,414 @@ +include $(top_srcdir)/make/shared.am + +CLEANFILES = +TESTS = +noinst_PROGRAMS = $(TESTS) + +############################ +# multiboot2_header_print0 # +############################ + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_header_print0 +multiboot2_header_print0_LDADD = $(top_builddir)/libkernaux.la +multiboot2_header_print0_SOURCES = \ + main.c \ + multiboot2_header_print0.c \ + ../fixtures/multiboot2_header_example0.h +endif + +############################ +# multiboot2_header_print1 # +############################ + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_header_print1 +multiboot2_header_print1_LDADD = $(top_builddir)/libkernaux.la +multiboot2_header_print1_SOURCES = \ + main.c \ + multiboot2_header_print1.c \ + ../fixtures/multiboot2_header_example1.h +endif + +############################ +# multiboot2_header_print2 # +############################ + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_header_print2 +multiboot2_header_print2_LDADD = $(top_builddir)/libkernaux.la +multiboot2_header_print2_SOURCES = \ + main.c \ + multiboot2_header_print2.c \ + ../fixtures/multiboot2_header_example2.h +endif + +########################## +# multiboot2_info_print0 # +########################## + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_info_print0 +multiboot2_info_print0_LDADD = $(top_builddir)/libkernaux.la +multiboot2_info_print0_SOURCES = \ + main.c \ + multiboot2_info_print0.c \ + ../fixtures/multiboot2_info_example0.h +endif + +########################## +# multiboot2_info_print1 # +########################## + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_info_print1 +multiboot2_info_print1_LDADD = $(top_builddir)/libkernaux.la +multiboot2_info_print1_SOURCES = \ + main.c \ + multiboot2_info_print1.c \ + ../fixtures/multiboot2_info_example1.h +endif + +########################## +# multiboot2_info_print2 # +########################## + +if WITH_MULTIBOOT2 +noinst_PROGRAMS += multiboot2_info_print2 +multiboot2_info_print2_LDADD = $(top_builddir)/libkernaux.la +multiboot2_info_print2_SOURCES = \ + main.c \ + multiboot2_info_print2.c \ + ../fixtures/multiboot2_info_example2.h +endif + +################## +# test_arch_i386 # +################## + +if WITH_ARCH_I386 +TESTS += test_arch_i386 +test_arch_i386_LDADD = $(top_builddir)/libkernaux.la +test_arch_i386_SOURCES = \ + main.c \ + test_arch_i386.c +endif + +################ +# test_cmdline # +################ + +if WITH_CMDLINE +TESTS += test_cmdline +test_cmdline_LDADD = $(top_builddir)/libkernaux.la +test_cmdline_SOURCES = \ + main.c \ + test_cmdline.c \ + cmdline_test.h \ + cmdline_test.c +endif + +#################### +# test_cmdline_gen # +#################### + +if ENABLE_CHECKS_PYTHON +if WITH_CMDLINE +TESTS += test_cmdline_gen +test_cmdline_gen_LDADD = $(top_builddir)/libkernaux.la +test_cmdline_gen_SOURCES = \ + main.c \ + test_cmdline_gen.c \ + cmdline_gen.py \ + cmdline_gen.jinja \ + $(top_srcdir)/fixtures/cmdline.yml \ + cmdline_test.h \ + cmdline_test.c +endif +endif + +CLEANFILES += test_cmdline_gen.c + +test_cmdline_gen.c: $(top_srcdir)/tests/cmdline_gen.py $(top_srcdir)/tests/cmdline_gen.jinja $(top_srcdir)/fixtures/cmdline.yml + $(PYTHON) $(top_srcdir)/tests/cmdline_gen.py $(top_srcdir)/tests/cmdline_gen.jinja $(top_srcdir)/fixtures/cmdline.yml test_cmdline_gen.c + +############ +# test_elf # +############ + +if WITH_ELF +TESTS += test_elf +test_elf_LDADD = $(top_builddir)/libkernaux.la +test_elf_SOURCES = \ + main.c \ + test_elf.c +endif + +################## +# test_free_list # +################## + +if WITH_FREE_LIST +TESTS += test_free_list +test_free_list_LDADD = $(top_builddir)/libkernaux.la +test_free_list_SOURCES = \ + main.c \ + test_free_list.c +endif + +############ +# test_mbr # +############ + +if WITH_MBR +TESTS += test_mbr +test_mbr_LDADD = $(top_builddir)/libkernaux.la +test_mbr_SOURCES = \ + main.c \ + test_mbr.c +endif + +################################## +# test_multiboot2_common_packing # +################################## + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_common_packing +test_multiboot2_common_packing_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_common_packing_SOURCES = \ + main.c \ + test_multiboot2_common_packing.c \ + ../fixtures/multiboot2_header_example2.h \ + ../fixtures/multiboot2_info_example2.h +endif + +################################## +# test_multiboot2_header_helpers # +################################## + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_header_helpers +test_multiboot2_header_helpers_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_header_helpers_SOURCES = \ + main.c \ + test_multiboot2_header_helpers.c \ + ../fixtures/multiboot2_header_example1.h \ + ../fixtures/multiboot2_header_example2.h +endif + +################################ +# test_multiboot2_header_print # +################################ + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_header_print +test_multiboot2_header_print_DEPENDENCIES = \ + multiboot2_header_print0 \ + multiboot2_header_print1 \ + multiboot2_header_print2 \ + ../fixtures/multiboot2_header_example0.txt \ + ../fixtures/multiboot2_header_example1.txt \ + ../fixtures/multiboot2_header_example2.txt +test_multiboot2_header_print_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_header_print_SOURCES = \ + main.c \ + test_multiboot2_header_print.c +endif + +##################################### +# test_multiboot2_header_validation # +##################################### + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_header_validation +test_multiboot2_header_validation_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_header_validation_SOURCES = \ + main.c \ + test_multiboot2_header_validation.c \ + ../fixtures/multiboot2_header_example1.h \ + ../fixtures/multiboot2_header_example2.h +endif + +####################################### +# test_multiboot2_info_convert_memmap # +####################################### + +if WITH_MULTIBOOT2 +if WITH_MEMMAP +TESTS += test_multiboot2_info_convert_memmap +test_multiboot2_info_convert_memmap_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_info_convert_memmap_SOURCES = \ + main.c \ + test_multiboot2_info_convert_memmap.c \ + ../fixtures/multiboot2_header_example2.h \ + ../fixtures/multiboot2_info_example2.h +endif +endif + +################################ +# test_multiboot2_info_helpers # +################################ + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_info_helpers +test_multiboot2_info_helpers_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_info_helpers_SOURCES = \ + main.c \ + test_multiboot2_info_helpers.c \ + ../fixtures/multiboot2_info_example1.h \ + ../fixtures/multiboot2_info_example2.h +endif + +############################## +# test_multiboot2_info_print # +############################## + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_info_print +test_multiboot2_info_print_DEPENDENCIES = \ + multiboot2_info_print0 \ + multiboot2_info_print1 \ + multiboot2_info_print2 \ + ../fixtures/multiboot2_info_example0.txt \ + ../fixtures/multiboot2_info_example1.txt \ + ../fixtures/multiboot2_info_example2.txt +test_multiboot2_info_print_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_info_print_SOURCES = \ + main.c \ + test_multiboot2_info_print.c +endif + +################################### +# test_multiboot2_info_validation # +################################### + +if WITH_MULTIBOOT2 +TESTS += test_multiboot2_info_validation +test_multiboot2_info_validation_LDADD = $(top_builddir)/libkernaux.la +test_multiboot2_info_validation_SOURCES = \ + main.c \ + test_multiboot2_info_validation.c \ + ../fixtures/multiboot2_info_example1.h \ + ../fixtures/multiboot2_info_example2.h +endif + +############# +# test_ntoa # +############# + +if WITH_NTOA +TESTS += test_ntoa +test_ntoa_LDADD = $(top_builddir)/libkernaux.la +test_ntoa_SOURCES = \ + main.c \ + test_ntoa.c +endif + +#################### +# test_ntoa_assert # +#################### + +if ENABLE_ASSERT +if WITH_NTOA +TESTS += test_ntoa_assert +test_ntoa_assert_LDADD = $(top_builddir)/libkernaux.la +test_ntoa_assert_SOURCES = \ + main.c \ + test_ntoa_assert.c +endif +endif + +############ +# test_pfa # +############ + +if WITH_PFA +TESTS += test_pfa +test_pfa_LDADD = $(top_builddir)/libkernaux.la +test_pfa_SOURCES = \ + main.c \ + test_pfa.c +endif + +################### +# test_pfa_assert # +################### + +if ENABLE_ASSERT +if WITH_PFA +TESTS += test_pfa_assert +test_pfa_assert_LDADD = $(top_builddir)/libkernaux.la +test_pfa_assert_SOURCES = \ + main.c \ + test_pfa_assert.c +endif +endif + +############### +# test_printf # +############### + +if WITH_PRINTF +TESTS += test_printf +test_printf_LDADD = $(top_builddir)/libkernaux.la +test_printf_SOURCES = \ + main.c \ + test_printf.c +endif + +####################### +# test_printf_fmt_gen # +####################### + +if ENABLE_CHECKS_PYTHON +if WITH_PRINTF_FMT +TESTS += test_printf_fmt_gen +test_printf_fmt_gen_LDADD = $(top_builddir)/libkernaux.la +test_printf_fmt_gen_SOURCES = \ + main.c \ + test_printf_fmt_gen.c \ + printf_fmt_gen.py \ + printf_fmt_gen.jinja \ + $(top_srcdir)/fixtures/printf_fmt.yml +endif +endif + +CLEANFILES += test_printf_fmt_gen.c + +test_printf_fmt_gen.c: $(top_srcdir)/tests/printf_fmt_gen.py $(top_srcdir)/tests/printf_fmt_gen.jinja $(top_srcdir)/fixtures/printf_fmt.yml + $(PYTHON) $(top_srcdir)/tests/printf_fmt_gen.py $(top_srcdir)/tests/printf_fmt_gen.jinja $(top_srcdir)/fixtures/printf_fmt.yml test_printf_fmt_gen.c + +################### +# test_printf_gen # +################### + +if ENABLE_CHECKS_PYTHON +if WITH_PRINTF +TESTS += test_printf_gen +test_printf_gen_LDADD = $(top_builddir)/libkernaux.la +test_printf_gen_SOURCES = \ + main.c \ + test_printf_gen.c \ + printf_gen.py \ + printf_gen.jinja \ + $(top_srcdir)/fixtures/printf.yml \ + $(top_srcdir)/fixtures/printf_orig.yml +endif +endif + +CLEANFILES += test_printf_gen.c + +test_printf_gen.c: $(top_srcdir)/tests/printf_gen.py $(top_srcdir)/tests/printf_gen.jinja $(top_srcdir)/fixtures/printf.yml $(top_srcdir)/fixtures/printf_orig.yml + $(PYTHON) $(top_srcdir)/tests/printf_gen.py $(top_srcdir)/tests/printf_gen.jinja $(top_srcdir)/fixtures/printf.yml $(top_srcdir)/fixtures/printf_orig.yml test_printf_gen.c + +#################### +# test_units_human # +#################### + +if WITH_UNITS +TESTS += test_units_human +test_units_human_LDADD = $(top_builddir)/libkernaux.la +test_units_human_SOURCES = \ + main.c \ + test_units_human.c +endif diff --git a/tests/cmdline_gen.jinja b/tests/cmdline_gen.jinja new file mode 100644 index 0000000..1148616 --- /dev/null +++ b/tests/cmdline_gen.jinja @@ -0,0 +1,46 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cmdline_test.h" + +#include +#include +#include +#include + +void test_main() +{ + {% for case in cases %} + { + {% if not case.error and len(case.result) %} + const char *const expected_argv[] = { + {% for item in case.result %} + {{ escape_str(item) }}, + {% endfor %} + }; + {% endif %} + + test( + {{ escape_str(case.cmdline) }}, + {{ escape_int(case.arg_count_max or 0) }}, + {{ escape_int(case.buffer_size or 0) }}, + {% if not case.error %} + true, + "", + {{ escape_int(len(case.result)) }}, + {% if len(case.result) %} + expected_argv + {% else %} + NULL + {% endif %} + {% else %} + false, + {{ escape_str(case.error) }}, + 0, + NULL + {% endif %} + ); + } + {% endfor %} +} diff --git a/tests/cmdline_gen.py b/tests/cmdline_gen.py new file mode 100644 index 0000000..32883dc --- /dev/null +++ b/tests/cmdline_gen.py @@ -0,0 +1,49 @@ +from jinja2 import Environment, FileSystemLoader +from os import path +from sys import argv +from yaml import safe_load + +def main(test_filepath, template_filepath, cases_filepath): + cases = safe_load(open(cases_filepath)) + + jinja_env = Environment( + keep_trailing_newline=True, + loader=FileSystemLoader(path.dirname(template_filepath)), + ) + jinja_template = jinja_env.get_template(path.basename(template_filepath)) + + result = jinja_template.render( + cases=cases, + escape_str=escape_str, + escape_int=escape_int, + len=len, + ) + + with open(test_filepath, 'w') as f: + f.write(result) + +def escape_int(n): + return str(n) + +def escape_str(s): + return '"' + s + '"' + +if __name__ == '__main__': + print(argv) + + template_filepath = argv[1] + cases_filepath = argv[2] + test_filepath = argv[3] + + print('test_filepath: %s' % test_filepath) + print('template_filepath: %s' % template_filepath) + print('cases_filepath: %s' % cases_filepath) + + if path.exists(test_filepath) and not path.isfile(test_filepath): + raise RuntimeError('invalid test file path') + if not path.isfile(template_filepath): + raise RuntimeError('invalid template file path') + if not path.isfile(cases_filepath): + raise RuntimeError('invalid cases file path') + + main(test_filepath, template_filepath, cases_filepath) diff --git a/tests/cmdline_test.c b/tests/cmdline_test.c new file mode 100644 index 0000000..050b068 --- /dev/null +++ b/tests/cmdline_test.c @@ -0,0 +1,71 @@ +#include "cmdline_test.h" + +#include + +#include +#include +#include + +#define ARG_COUNT_MAX 100 +#define BUFFER_SIZE 4096 + +void test( + const char *const cmdline, + size_t arg_count_max, + size_t buffer_size, + + const bool expected_result, + const char *const expected_error_msg, + size_t expected_argc, + const char *const *const expected_argv +) { + if (arg_count_max == 0) arg_count_max = ARG_COUNT_MAX; + if (buffer_size == 0) buffer_size = BUFFER_SIZE; + + char *error_msg = malloc(KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX); + size_t argc = 1234; + char **const argv = malloc(sizeof(char*) * arg_count_max); + size_t *arg_idxs = malloc(sizeof(size_t) * arg_count_max); + char *const buffer = malloc(buffer_size); + + assert(error_msg); + assert(argv); + assert(buffer); + + { + memset(error_msg, 'x', KERNAUX_CMDLINE_ERROR_MSG_SIZE_MAX); + memset(argv, 0, sizeof(char*) * arg_count_max); + memset(arg_idxs, 0, sizeof(size_t) * arg_count_max); + memset(buffer, 'x', buffer_size); + + assert( + kernaux_cmdline( + cmdline, + error_msg, + &argc, + argv, + buffer, + arg_count_max, + buffer_size + ) == !!expected_result + ); + + assert(strcmp(error_msg, expected_error_msg) == 0); + assert(argc == expected_argc); + + if (expected_argv) { + for (size_t index = 0; index < argc; ++index) { + assert(strcmp(argv[index], expected_argv[index]) == 0); + } + } + + for (size_t index = argc; index < arg_count_max; ++index) { + assert(argv[index] == NULL); + } + } + + free(error_msg); + free(argv); + free(arg_idxs); + free(buffer); +} diff --git a/tests/cmdline_test.h b/tests/cmdline_test.h new file mode 100644 index 0000000..645e24f --- /dev/null +++ b/tests/cmdline_test.h @@ -0,0 +1,26 @@ +#ifndef KERNAUX_INCLUDED_TESTS_CMDLINE_TEST +#define KERNAUX_INCLUDED_TESTS_CMDLINE_TEST + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void test( + const char *cmdline, + size_t arg_count_max, + size_t buffer_size, + + bool expected_result, + const char *expected_error_msg, + size_t expected_argc, + const char *const *const expected_argv +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..f2a2c77 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,26 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +void test_main(int argc, char **argv); + +static void assert_cb( + const char *const file, + const int line, + const char *const msg +) { + fprintf(stderr, "%s:%d:%s\n", file, line, msg); + abort(); +} + +int main(int argc, char **argv) +{ + kernaux_assert_cb = assert_cb; + test_main(argc, argv); + exit(EXIT_SUCCESS); +} diff --git a/tests/multiboot2_header_print0.c b/tests/multiboot2_header_print0.c new file mode 100644 index 0000000..407e221 --- /dev/null +++ b/tests/multiboot2_header_print0.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_header_example0.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Header_is_valid( + (struct KernAux_Multiboot2_Header*)&multiboot2_header_example0 + )); + + KernAux_Multiboot2_Header_print( + (struct KernAux_Multiboot2_Header*)&multiboot2_header_example0, + &display + ); +} diff --git a/tests/multiboot2_header_print1.c b/tests/multiboot2_header_print1.c new file mode 100644 index 0000000..fe16b40 --- /dev/null +++ b/tests/multiboot2_header_print1.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_header_example1.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Header_is_valid( + (struct KernAux_Multiboot2_Header*)&multiboot2_header_example1 + )); + + KernAux_Multiboot2_Header_print( + (struct KernAux_Multiboot2_Header*)&multiboot2_header_example1, + &display + ); +} diff --git a/tests/multiboot2_header_print2.c b/tests/multiboot2_header_print2.c new file mode 100644 index 0000000..1d00386 --- /dev/null +++ b/tests/multiboot2_header_print2.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_header_example2.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Header_is_valid( + &multiboot2_header_example2.multiboot2_header + )); + + KernAux_Multiboot2_Header_print( + &multiboot2_header_example2.multiboot2_header, + &display + ); +} diff --git a/tests/multiboot2_info_print0.c b/tests/multiboot2_info_print0.c new file mode 100644 index 0000000..ddcf46d --- /dev/null +++ b/tests/multiboot2_info_print0.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_info_example0.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_info_example0.multiboot2_info + )); + + KernAux_Multiboot2_Info_print( + &multiboot2_info_example0.multiboot2_info, + &display + ); +} diff --git a/tests/multiboot2_info_print1.c b/tests/multiboot2_info_print1.c new file mode 100644 index 0000000..8770af9 --- /dev/null +++ b/tests/multiboot2_info_print1.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_info_example1.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Info_is_valid( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1 + )); + + KernAux_Multiboot2_Info_print( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + &display + ); +} diff --git a/tests/multiboot2_info_print2.c b/tests/multiboot2_info_print2.c new file mode 100644 index 0000000..8e0f1bf --- /dev/null +++ b/tests/multiboot2_info_print2.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_info_example2.h" + +static void my_putc(void *display KERNAUX_UNUSED, char c) +{ + putchar(c); +} + +static +void my_vprintf(void *display KERNAUX_UNUSED, const char *format, va_list va) +{ + vprintf(format, va); +} + +static const struct KernAux_Display display = { + .putc = my_putc, + .vprintf = my_vprintf, +}; + +void test_main() +{ + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_info_example2.multiboot2_info + )); + + KernAux_Multiboot2_Info_print( + &multiboot2_info_example2.multiboot2_info, + &display + ); +} diff --git a/tests/printf_fmt_gen.jinja b/tests/printf_fmt_gen.jinja new file mode 100644 index 0000000..adc774a --- /dev/null +++ b/tests/printf_fmt_gen.jinja @@ -0,0 +1,36 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#include + +void test_main() +{ + {% for case in cases %} + { + const char *const format = {{ escape_str(case.in.format) }}; + + struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create(format); + + if (spec.set_width) { + KernAux_PrintfFmt_Spec_set_width(&spec, {{ none_to_zero(case.in.width) }}); + } + if (spec.set_precision) { + KernAux_PrintfFmt_Spec_set_precision(&spec, {{ none_to_zero(case.in.precision) }}); + } + + assert(spec.format_start == format); + + assert(spec.flags == {{ escape_flags(case.out.flags) }}); + assert(spec.width == {{ case.out.width }}); + assert(spec.precision == {{ case.out.precision }}); + assert(spec.type == {{ escape_type(case.out.type) }}); + assert(spec.base == {{ case.out.base }}); + } + {% endfor %} +} diff --git a/tests/printf_fmt_gen.py b/tests/printf_fmt_gen.py new file mode 100644 index 0000000..96a0925 --- /dev/null +++ b/tests/printf_fmt_gen.py @@ -0,0 +1,65 @@ +from jinja2 import Environment, FileSystemLoader +from os import path +from sys import argv +from yaml import safe_load + +def main(test_filepath, template_filepath, cases_filepath): + cases = safe_load(open(cases_filepath)) + + jinja_env = Environment( + keep_trailing_newline=True, + loader=FileSystemLoader(path.dirname(template_filepath)), + ) + jinja_template = jinja_env.get_template(path.basename(template_filepath)) + + result = jinja_template.render( + cases=cases, + escape_flags=escape_flags, + escape_str=escape_str, + escape_type=escape_type, + none_to_zero=none_to_zero, + ) + + with open(test_filepath, 'w') as f: + f.write(result) + +def escape_flag(flag): + return 'KERNAUX_PRINTF_FMT_FLAGS_' + flag.upper() + +def escape_flags(flags): + if len(flags) == 0: + return '0' + + return '(' + ' | '.join(map(escape_flag, flags)) + ')' + +def escape_str(s): + return '"' + s + '"' + +def escape_type(type_): + return 'KERNAUX_PRINTF_FMT_TYPE_' + type_.upper() + +def none_to_zero(num): + if num is None: + return '0' + else: + return str(num) + +if __name__ == '__main__': + print(argv) + + template_filepath = argv[1] + cases_filepath = argv[2] + test_filepath = argv[3] + + print('test_filepath: %s' % test_filepath) + print('template_filepath: %s' % template_filepath) + print('cases_filepath: %s' % cases_filepath) + + if path.exists(test_filepath) and not path.isfile(test_filepath): + raise RuntimeError('invalid test file path') + if not path.isfile(template_filepath): + raise RuntimeError('invalid template file path') + if not path.isfile(cases_filepath): + raise RuntimeError('invalid cases file path') + + main(test_filepath, template_filepath, cases_filepath) diff --git a/tests/printf_gen.jinja b/tests/printf_gen.jinja new file mode 100644 index 0000000..3d1cf4a --- /dev/null +++ b/tests/printf_gen.jinja @@ -0,0 +1,76 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PROTECTED + +#include +#include + +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 1024 + +static const char *const data = "foobar"; + +static char buffer[BUFFER_SIZE]; +static size_t buffer_index; + +static void test_putc(char c, void *arg KERNAUX_UNUSED) +{ + if (buffer_index >= BUFFER_SIZE) { + printf("Buffer overflow!\n"); + abort(); + } + + buffer[buffer_index++] = c; +} + +static void test(const char *const expected, const char *const format, ...) +{ + va_list va; + int result; + + memset(buffer, '\0', sizeof(buffer)); + buffer_index = 0; + va_start(va, format); + result = kernaux_vfprintf(test_putc, (void*)data, format, va); + va_end(va); + + printf("Exp: %s\n", expected); + printf("Got: %s\n\n", buffer); + + assert((size_t)result == strlen(expected)); + assert(strcmp(expected, buffer) == 0); + + memset(buffer, '\0', sizeof(buffer)); + buffer_index = 0; + va_start(va, format); + result = kernaux_vsnprintf(buffer, sizeof(buffer), format, va); + va_end(va); + + assert((size_t)result == strlen(expected)); + assert(strcmp(expected, buffer) == 0); +} + +void test_main() +{ + memset(buffer, '\0', sizeof(buffer)); + buffer_index = 0; + kernaux_fprintf(test_putc, (void*)data, "Hello, World!"); + assert(strcmp("Hello, World!", buffer) == 0); + + {% for case in cases %} + {% if case.float %} +#ifdef ENABLE_FLOAT + {% endif %} + test({{ escape_str(case.result) }}, {{ escape_str(fmt(case.args)) }}{{ values(case.args) }}); + {% if case.float %} +#endif + {% endif %} + {% endfor %} +} diff --git a/tests/printf_gen.py b/tests/printf_gen.py new file mode 100644 index 0000000..2841a14 --- /dev/null +++ b/tests/printf_gen.py @@ -0,0 +1,88 @@ +from jinja2 import Environment, FileSystemLoader +from os import path +from sys import argv +from yaml import safe_load + +def main(test_filepath, template_filepath, cases_reg_filepath, cases_orig_filepath): + cases_reg = safe_load(open(cases_reg_filepath)) + cases_orig = safe_load(open(cases_orig_filepath)) + + cases = cases_reg + cases_orig + + jinja_env = Environment( + keep_trailing_newline=True, + loader=FileSystemLoader(path.dirname(template_filepath)), + ) + jinja_template = jinja_env.get_template(path.basename(template_filepath)) + + result = jinja_template.render( + cases=cases, + escape_str=escape_str, + fmt=fmt, + values=values, + ) + + with open(test_filepath, 'w') as f: + f.write(result) + +def escape_char(c): + return "'" + c + "'" + +def escape_str(s): + return '"' + s + '"' + +def fmt(args): + fmt = '' + for arg in args: + if type(arg) is list: + fmt += arg[0] + else: + fmt += arg + return fmt + +def values(args): + values = '' + + for arg in args: + if type(arg) is list and len(arg) >= 2: + if len(arg) >= 3: + values += ', ' + str(arg[1]) + arg[1] = arg[2] + + if type(arg[1]) is str: + values += ', ' + escape_str(arg[1]) + elif type(arg[1]) is list: + if len(arg[1]) == 1: + values += ', ' + escape_char(arg[1][0]) + elif arg[1][0] == 'long long': + values += ', (long long)' + str(arg[1][1]) + else: + raise RuntimeError('unknown format: ' + str(args)) + else: + values += ', ' + str(arg[1]) + + return values + +if __name__ == '__main__': + print(argv) + + template_filepath = argv[1] + cases_reg_filepath = argv[2] + cases_orig_filepath = argv[3] + test_filepath = argv[4] + + print('test_filepath: %s' % test_filepath) + print('template_filepath: %s' % template_filepath) + print('cases_reg_filepath: %s' % cases_reg_filepath) + print('cases_orig_filepath: %s' % cases_orig_filepath) + + if path.exists(test_filepath) and not path.isfile(test_filepath): + raise RuntimeError('invalid test file path') + if not path.isfile(template_filepath): + raise RuntimeError('invalid template file path') + if not path.isfile(cases_reg_filepath): + raise RuntimeError('invalid regular cases file path') + if not path.isfile(cases_orig_filepath): + raise RuntimeError('invalid original cases file path') + + main(test_filepath, template_filepath, cases_reg_filepath, cases_orig_filepath) diff --git a/tests/test_arch_i386.c b/tests/test_arch_i386.c new file mode 100644 index 0000000..b600c4d --- /dev/null +++ b/tests/test_arch_i386.c @@ -0,0 +1,132 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +static void test_idte_init_intr(); +static void test_idte_init_task(); +static void test_idte_init_trap(); +static void test_idte_get_and_set_offset(); +static void test_idte_get_dpl(); + +void test_main() +{ + test_idte_init_intr(); + test_idte_init_task(); + test_idte_init_trap(); + test_idte_get_and_set_offset(); + test_idte_get_dpl(); +} + +void test_idte_init_intr() +{ + struct KernAux_Arch_I386_IDTE idte; + memset(&idte, 0xff, sizeof(idte)); + + KernAux_Arch_I386_IDTE_init_intr(&idte, 0x12345678, 0xcafe, 0); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0x12345678); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 0); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0x8e); // 1-00-01110 + + KernAux_Arch_I386_IDTE_init_intr(&idte, 0x12345678, 0xcafe, 3); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0x12345678); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 3); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0xee); // 1-11-01110 +} + +void test_idte_init_task() +{ + struct KernAux_Arch_I386_IDTE idte; + memset(&idte, 0xff, sizeof(idte)); + + KernAux_Arch_I386_IDTE_init_task(&idte, 0xcafe, 0); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 0); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0x85); // 1-00-00101 + + KernAux_Arch_I386_IDTE_init_task(&idte, 0xcafe, 3); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 3); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0xe5); // 1-11-00101 +} + +void test_idte_init_trap() +{ + struct KernAux_Arch_I386_IDTE idte; + memset(&idte, 0xff, sizeof(idte)); + + KernAux_Arch_I386_IDTE_init_trap(&idte, 0x12345678, 0xcafe, 0); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0x12345678); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 0); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0x8f); // 1-00-01111 + + KernAux_Arch_I386_IDTE_init_trap(&idte, 0x12345678, 0xcafe, 3); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0x12345678); + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 3); + assert(idte.selector == 0xcafe); + assert(idte._ == 0); + assert(idte.flags == 0xef); // 1-11-01111 +} + +void test_idte_get_and_set_offset() +{ + struct KernAux_Arch_I386_IDTE idte; + + memset(&idte, 0xff, sizeof(idte)); + KernAux_Arch_I386_IDTE_set_offset(&idte, 0); + assert(idte.offset_high == 0); + assert(idte.offset_low == 0); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0); + + memset(&idte, 0, sizeof(idte)); + KernAux_Arch_I386_IDTE_set_offset(&idte, 0xffffffff); + assert(idte.offset_high == 0xffff); + assert(idte.offset_low == 0xffff); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0xffffffff); + + memset(&idte, 0, sizeof(idte)); + KernAux_Arch_I386_IDTE_set_offset(&idte, 0x12345678); + assert(idte.offset_high == 0x1234); + assert(idte.offset_low == 0x5678); + assert(KernAux_Arch_I386_IDTE_offset(&idte) == 0x12345678); +} + +void test_idte_get_dpl() +{ + struct KernAux_Arch_I386_IDTE idte; + memset(&idte, 0, sizeof(idte)); + + idte.flags = 0; // 0-00-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 0); + idte.flags = 0x80; // 1-00-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 0); + + idte.flags = 0x20; // 0-01-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 1); + idte.flags = 0xa0; // 1-01-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 1); + + idte.flags = 0x40; // 0-10-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 2); + idte.flags = 0xc0; // 1-10-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 2); + + idte.flags = 0x60; // 0-11-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 3); + idte.flags = 0xe0; // 1-11-00000 + assert(KernAux_Arch_I386_IDTE_dpl(&idte) == 3); +} diff --git a/tests/test_cmdline.c b/tests/test_cmdline.c new file mode 100644 index 0000000..3aba50a --- /dev/null +++ b/tests/test_cmdline.c @@ -0,0 +1,119 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "cmdline_test.h" + +#include +#include +#include +#include + +static const char *const argv_spaceX3_X3[] = {" ", " ", " "}; +static const char *const argv_backslashX3_X3[] = {"\\\\\\", "\\\\\\", "\\\\\\"}; +static const char *const argv_quotmarkX3_X3[] = {"\"\"\"", "\"\"\"", "\"\"\""}; + +static const char *const argv_aX50[] = { + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", +}; + +static const char *const argv_a_X1[] = { "a" }; +static const char *const argv_a_X2[] = { "a", "a" }; +static const char *const argv_a_X3[] = { "a", "a", "a" }; +static const char *const argv_a_X4[] = { "a", "a", "a", "a" }; +static const char *const argv_a_X5[] = { "a", "a", "a", "a", "a" }; +static const char *const argv_a_X6[] = { "a", "a", "a", "a", "a", "a" }; + +void test_main() +{ + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 3, 0, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 3, 0, true, "", 3, argv_backslashX3_X3); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 3, 0, true, "", 3, argv_quotmarkX3_X3); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 0, 12, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 0, 12, true, "", 3, argv_backslashX3_X3); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 0, 12, true, "", 3, argv_quotmarkX3_X3); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 3, 12, true, "", 3, argv_spaceX3_X3); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 3, 12, true, "", 3, argv_backslashX3_X3); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 3, 12, true, "", 3, argv_quotmarkX3_X3); + + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 2, 0, false, "too many args", 0, NULL); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 2, 0, false, "too many args", 0, NULL); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 2, 0, false, "too many args", 0, NULL); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 0, 11, false, "EOF or buffer overflow", 0, NULL); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 0, 11, false, "EOF or buffer overflow", 0, NULL); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 0, 11, false, "EOF or buffer overflow", 0, NULL); + test("\\ \\ \\ \\ \\ \\ \\ \\ \\ ", 2, 11, false, "too many args", 0, NULL); + test("\\\\\\\\\\\\ \\\\\\\\\\\\ \\\\\\\\\\\\", 2, 11, false, "too many args", 0, NULL); + test("\\\"\\\"\\\" \\\"\\\"\\\" \\\"\\\"\\\"", 2, 11, false, "too many args", 0, NULL); + + test("\\", 0, 0, false, "EOL after backslash", 0, NULL); + test(" \\", 0, 0, false, "EOL after backslash", 0, NULL); + test("\\ \\", 0, 0, false, "EOL after backslash", 0, NULL); + test("\\\\\\", 0, 0, false, "EOL after backslash", 0, NULL); + test("\\\"\\", 0, 0, false, "EOL after backslash", 0, NULL); + test("foo\\", 0, 0, false, "EOL after backslash", 0, NULL); + + test("\"\\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + test("\" \\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + test("\"\\ \\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + test("\"\\\\\\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + test("\"\\\"\\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + test("\"foo\\", 0, 0, false, "EOL after backslash inside quote", 0, NULL); + + test("foo\"", 0, 0, false, "unescaped quotation mark", 0, NULL); + test("foo\"bar", 0, 0, false, "unescaped quotation mark", 0, NULL); + + test("\"", 0, 0, false, "EOL inside quote", 0, NULL); + test("\"foo", 0, 0, false, "EOL inside quote", 0, NULL); + + test( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + 0, + 0, + true, + "", + 1, + argv_aX50 + ); + + test("a", 1, 0, true, "", 1, argv_a_X1); + test("a ", 1, 0, true, "", 1, argv_a_X1); + test("a a", 1, 0, false, "too many args", 0, NULL); + test("a a ", 1, 0, false, "too many args", 0, NULL); + test("a a", 2, 0, true, "", 2, argv_a_X2); + test("a a ", 2, 0, true, "", 2, argv_a_X2); + test("a a a", 2, 0, false, "too many args", 0, NULL); + test("a a a ", 2, 0, false, "too many args", 0, NULL); + test("a a a", 3, 0, true, "", 3, argv_a_X3); + test("a a a ", 3, 0, true, "", 3, argv_a_X3); + test("a a a a", 3, 0, false, "too many args", 0, NULL); + test("a a a a ", 3, 0, false, "too many args", 0, NULL); + test("a a a a", 4, 0, true, "", 4, argv_a_X4); + test("a a a a ", 4, 0, true, "", 4, argv_a_X4); + test("a a a a a", 4, 0, false, "too many args", 0, NULL); + test("a a a a a ", 4, 0, false, "too many args", 0, NULL); + test("a a a a a", 5, 0, true, "", 5, argv_a_X5); + test("a a a a a ", 5, 0, true, "", 5, argv_a_X5); + test("a a a a a a", 5, 0, false, "too many args", 0, NULL); + test("a a a a a a ", 5, 0, false, "too many args", 0, NULL); + test("a a a a a a", 6, 0, true, "", 6, argv_a_X6); + test("a a a a a a ", 6, 0, true, "", 6, argv_a_X6); + + { + char *const buffer = malloc(4096); + memset(buffer, 'a', 4096 - 1); + buffer[4096 - 1] = '\0'; + // 4095 of "a" + test(buffer, 256, 4096, true, "", 1, NULL); + free(buffer); + } + + { + char *const buffer = malloc(4096 + 1); + memset(buffer, 'a', 4096); + buffer[4096] = '\0'; + // 4096 of "a" + test(buffer, 256, 4096, false, "EOF or buffer overflow", 0, NULL); + free(buffer); + } +} diff --git a/tests/test_elf.c b/tests/test_elf.c new file mode 100644 index 0000000..d71c24a --- /dev/null +++ b/tests/test_elf.c @@ -0,0 +1,7 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +void test_main() {} diff --git a/tests/test_free_list.c b/tests/test_free_list.c new file mode 100644 index 0000000..4fe60df --- /dev/null +++ b/tests/test_free_list.c @@ -0,0 +1,365 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define KERNAUX_ACCESS_PRIVATE + +#include + +#include +#include +#include +#include +#include +#include + +static void test_default(); +static void test_cross_zone_defrag(); +static void test_hello_and_mrb_state(); + +static void test_calloc(); +static void test_calloc_nomem(); +static void test_calloc_overflow(); +static void test_calloc_zero(); + +static void test_realloc_alloc(); +static void test_realloc_free(); +static void test_realloc_memcpy(); +static void test_realloc_increase(); +static void test_realloc_decrease(); + +static size_t nodes_count(KernAux_FreeList free_list); +static void print_nodes(KernAux_FreeList free_list); + +static const char *const hello = "Hello, World!"; + +void test_main() +{ + test_default(); + test_cross_zone_defrag(); + test_hello_and_mrb_state(); + + test_calloc(); + test_calloc_nomem(); + test_calloc_overflow(); + test_calloc_zero(); + + test_realloc_alloc(); + test_realloc_free(); + test_realloc_memcpy(); + test_realloc_increase(); + test_realloc_decrease(); +} + +size_t nodes_count(const KernAux_FreeList free_list) +{ + size_t nodes_count = 0; + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + ++nodes_count; + } + return nodes_count; +} + +void print_nodes(const KernAux_FreeList free_list) +{ + printf("\n\n\n"); + printf("========================================\n"); + printf("free_list: %p\n", (void*)free_list); + printf("free_list->head: %p\n", (void*)free_list->head); + for ( + KernAux_FreeList_Node item_node = free_list->head; + item_node; + item_node = item_node->next + ) { + printf("----------------------------------------\n"); + printf("item_node: %p\n", (void*)item_node); + printf("item_node->orig_ptr: %p\n", (void*)item_node->orig_ptr); + printf("item_node->next: %p\n", (void*)item_node->next); + printf("item_node->prev: %p\n", (void*)item_node->prev); + printf("&item_node->block: %p\n", (void*)&item_node->block); + printf("item_node->size: %lu\n", (unsigned long)item_node->size); + } + printf("========================================\n"); + printf("\n\n\n"); +} + +void test_default() +{ + char memory_block[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, memory_block, sizeof(memory_block)); + + char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 100); + assert(ptr1); + assert(ptr1 > memory_block); + assert(ptr1 < &memory_block[1000]); + + char *const ptr2 = KernAux_Malloc_calloc(&free_list.malloc, 100, 1); + assert(ptr2); + assert(ptr2 > ptr1); + assert(ptr2 < &memory_block[1000]); + + char *const ptr3 = KernAux_Malloc_calloc(&free_list.malloc, 1, 100); + assert(ptr3); + assert(ptr3 > ptr2); + assert(ptr3 < &memory_block[1000]); + + char *const ptr4 = KernAux_Malloc_malloc(&free_list.malloc, 100); + assert(ptr4); + assert(ptr4 > ptr3); + assert(ptr4 < &memory_block[1000]); + + KernAux_Malloc_free(&free_list.malloc, ptr2); + KernAux_Malloc_free(&free_list.malloc, ptr3); + + char *const ptr5 = KernAux_Malloc_malloc(&free_list.malloc, 100); + assert(ptr5 == ptr2); + + char *const ptr6 = KernAux_Malloc_calloc(&free_list.malloc, 10, 10); + assert(ptr6 == ptr3); + + KernAux_Malloc_free(&free_list.malloc, ptr2); + KernAux_Malloc_free(&free_list.malloc, ptr3); + + char *const ptr7 = KernAux_Malloc_malloc(&free_list.malloc, 200); + assert(ptr7 == ptr2); +} + +void test_cross_zone_defrag() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, &zone[0], 500); + KernAux_FreeList_add_zone(&free_list, &zone[500], 500); + + assert(nodes_count(&free_list) == 1); +} + +void test_hello_and_mrb_state() +{ + char *const zone = malloc(1024 * 128); // 128 KiB + assert(zone); + + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + printf("KernAux_FreeList_create(NULL) = %p\n", (void*)&free_list); + print_nodes(&free_list); + + KernAux_FreeList_add_zone(&free_list, zone, 1024 * 128); + printf( + "KernAux_FreeList_add_zone(&free_list, zone = %p, 1024 * 128 = %i)\n", + (void*)zone, + 1024 * 128 + ); + print_nodes(&free_list); + + { + char *const str = KernAux_Malloc_malloc(&free_list.malloc, 100); + printf( + "KernAux_Malloc_malloc(&free_list.malloc, 100) = %p\n", + (void*)str + ); + print_nodes(&free_list); + + assert(str); + + KernAux_Malloc_free(&free_list.malloc, str); + printf( + "KernAux_Malloc_free(&free_list.malloc, str = %p)\n", + (void*)str + ); + print_nodes(&free_list); + } + + { + char *const str = + KernAux_Malloc_realloc(&free_list.malloc, NULL, 100); + printf( + "KernAux_Malloc_realloc(&free_list.malloc, NULL, 100) = %p\n", + (void*)str + ); + print_nodes(&free_list); + + assert(str); + + KernAux_Malloc_free(&free_list.malloc, str); + printf( + "KernAux_Malloc_free(&free_list.malloc, str = %p)\n", + (void*)str + ); + print_nodes(&free_list); + } + + { + char *const mrb = KernAux_Malloc_malloc(&free_list.malloc, 6356); + printf( + "KernAux_Malloc_malloc(&free_list.malloc, 6356) = %p\n", + (void*)mrb + ); + print_nodes(&free_list); + + assert(mrb); + + KernAux_Malloc_free(&free_list.malloc, mrb); + printf( + "KernAux_Malloc_free(&free_list.malloc, mrb = %p)\n", + (void*)mrb + ); + print_nodes(&free_list); + } + + { + char *const mrb = KernAux_Malloc_realloc(&free_list.malloc, NULL, 6356); + printf( + "KernAux_Malloc_realloc(&free_list.malloc, NULL, 6356) = %p\n", + (void*)mrb + ); + print_nodes(&free_list); + + assert(mrb); + + KernAux_Malloc_free(&free_list.malloc, mrb); + printf( + "KernAux_Malloc_free(&free_list.malloc, mrb = %p)\n", + (void*)mrb + ); + print_nodes(&free_list); + } + + free(zone); +} + +void test_calloc() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, 1000); + char *const ptr = KernAux_Malloc_calloc(&free_list.malloc, 1, 900); + for (size_t index = 0; index < 900; ++index) { + assert(ptr[index] == 0); + } + KernAux_Malloc_free(&free_list.malloc, ptr); +} + +void test_calloc_nomem() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + void *const ptr = KernAux_Malloc_calloc(&free_list.malloc, 1, sizeof(zone)); + assert(ptr == NULL); +} + +void test_calloc_overflow() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, SIZE_MAX); + { + void *const ptr = KernAux_Malloc_calloc(&free_list.malloc, 2, SIZE_MAX); + assert(ptr == NULL); + } + { + void *const ptr = KernAux_Malloc_calloc(&free_list.malloc, SIZE_MAX, 2); + assert(ptr == NULL); + } +} + +void test_calloc_zero() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + void *const ptr1 = KernAux_Malloc_calloc(&free_list.malloc, 0, 900); + assert(ptr1 == NULL); + void *const ptr2 = KernAux_Malloc_calloc(&free_list.malloc, 900, 0); + assert(ptr2 == NULL); +} + +void test_realloc_alloc() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + void *const ptr = KernAux_Malloc_realloc(&free_list.malloc, NULL, 900); + assert(ptr != NULL); +} + +void test_realloc_free() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + + void *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 900); + assert(ptr1 != NULL); + + void *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 0); + assert(ptr2 == NULL); + + void *const ptr3 = KernAux_Malloc_malloc(&free_list.malloc, 900); + assert(ptr3 != NULL); +} + +void test_realloc_memcpy() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + + char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 400); + assert(ptr1 != NULL); + + memset(ptr1, 0, 400); + strcpy(ptr1, hello); + strcpy(&ptr1[400 - strlen(hello) - 1], hello); + + char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 400); + assert(ptr2 != NULL); + + assert(strcmp(ptr2, hello) == 0); + assert(strcmp(&ptr2[400 - strlen(hello) - 1], hello) == 0); +} + +void test_realloc_increase() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + + char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 300); + assert(ptr1 != NULL); + + memset(ptr1, 0, 300); + strcpy(ptr1, hello); + strcpy(&ptr1[300 - strlen(hello) - 1], hello); + + char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 500); + assert(ptr2 != NULL); + + assert(strcmp(ptr2, hello) == 0); + assert(strcmp(&ptr2[300 - strlen(hello) - 1], hello) == 0); +} + +void test_realloc_decrease() +{ + char zone[1000]; + struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone)); + + char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 500); + assert(ptr1 != NULL); + + memset(ptr1, 0, 300); + strcpy(ptr1, hello); + strcpy(&ptr1[300 - strlen(hello) - 1], hello); + + char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 300); + assert(ptr2 != NULL); + + assert(strcmp(ptr2, hello) == 0); + assert(strcmp(&ptr2[300 - strlen(hello) - 1], hello) == 0); +} diff --git a/tests/test_mbr.c b/tests/test_mbr.c new file mode 100644 index 0000000..5ff45b3 --- /dev/null +++ b/tests/test_mbr.c @@ -0,0 +1,43 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +static void create_valid_mbr(struct KernAux_Mbr *mbr); + +void test_main() +{ + struct KernAux_Mbr mbr; + + create_valid_mbr(&mbr); + + assert(KernAux_Mbr_is_valid(&mbr)); + assert(KernAux_Mbr_Info_is_valid(&mbr.info)); + for (size_t index = 0; index < KERNAUX_MBR_ENTRIES; ++index) { + assert(KernAux_Mbr_Entry_is_valid(&mbr.info.entries[index])); + } + + create_valid_mbr(&mbr); + mbr.info.magic = 123; + + assert(!KernAux_Mbr_is_valid(&mbr)); + assert(!KernAux_Mbr_Info_is_valid(&mbr.info)); + for (size_t index = 0; index < KERNAUX_MBR_ENTRIES; ++index) { + assert(KernAux_Mbr_Entry_is_valid(&mbr.info.entries[index])); + } + + // TODO: test partition table +} + +void create_valid_mbr(struct KernAux_Mbr *const mbr) +{ + memset(mbr, 0, sizeof(*mbr)); + + mbr->info.magic = KERNAUX_MBR_MAGIC; + mbr->info.disk_id = 0xCAFEBABE; + // TODO: fill partition table +} diff --git a/tests/test_multiboot2_common_packing.c b/tests/test_multiboot2_common_packing.c new file mode 100644 index 0000000..f9215fb --- /dev/null +++ b/tests/test_multiboot2_common_packing.c @@ -0,0 +1,164 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include + +#include "../fixtures/multiboot2_header_example2.h" +#include "../fixtures/multiboot2_info_example2.h" + +#define HEAD_SIZEOF1(type, inst, size) \ + do { \ + assert(sizeof(struct KernAux_Multiboot2_HTag_##type) == (size)); \ + assert(sizeof(multiboot2_header_example2.tag_##inst) == (size)); \ + } while (0) + +#define HEAD_SIZEOF2(type, inst, size1, size2) \ + do { \ + assert(sizeof(struct KernAux_Multiboot2_HTag_##type) == (size1)); \ + assert(sizeof(multiboot2_header_example2.tag_##inst.tag) == (size1)); \ + assert(sizeof(multiboot2_header_example2.tag_##inst) == (size1) + (size2)); \ + } while (0) + +#define INFO_SIZEOF1(type, inst, size) \ + do { \ + assert(sizeof(struct KernAux_Multiboot2_ITag_##type) == (size)); \ + assert(sizeof(multiboot2_info_example2.tag_##inst) == (size)); \ + } while (0) + +#define INFO_SIZEOF2(type, inst, size1, size2) \ + do { \ + assert(sizeof(struct KernAux_Multiboot2_ITag_##type) == (size1)); \ + assert(sizeof(multiboot2_info_example2.tag_##inst.tag) == (size1)); \ + assert(sizeof(multiboot2_info_example2.tag_##inst) == (size1) + (size2)); \ + } while (0) + +// For flag tags that don't include any data +#define HEAD0(name) \ + do { \ + assert(((uint8_t*)&multiboot2_header_example2.tag_##name) == head); \ + head += 8; \ + } while (0) + +#define HEAD2(name, tag_or_data, align) \ + do { \ + assert(((uint8_t*)&multiboot2_header_example2.tag_##name) == head); \ + head += 8 + (tag_or_data) + (align); \ + } while (0) + +#define HEAD3(name, tag, data, align) \ + do { \ + assert(((uint8_t*)&multiboot2_header_example2.tag_##name) == head); \ + head += 8 + (tag) + (data) + (align); \ + } while (0) + +// For flag tags that don't include any data +#define INFO0(name) \ + do { \ + assert(((uint8_t*)&multiboot2_info_example2.tag_##name) == info); \ + info += 8; \ + } while (0) + +#define INFO2(name, tag_or_data, align) \ + do { \ + assert(((uint8_t*)&multiboot2_info_example2.tag_##name) == info); \ + info += 8 + (tag_or_data) + (align); \ + } while (0) + +#define INFO3(name, tag, data, align) \ + do { \ + assert(((uint8_t*)&multiboot2_info_example2.tag_##name) == info); \ + info += 8 + (tag) + (data) + (align); \ + } while (0) + +void test_main() +{ + assert(sizeof(struct KernAux_Multiboot2_Header) == 16); + assert(sizeof(struct KernAux_Multiboot2_Info) == 8); + + assert(sizeof(multiboot2_header_example2.multiboot2_header) == 16); + assert(sizeof(multiboot2_info_example2.multiboot2_info) == 8); + + assert(sizeof(struct KernAux_Multiboot2_HTagBase) == 8); + assert(sizeof(struct KernAux_Multiboot2_ITagBase) == 8); + + HEAD_SIZEOF1(None, none, 8 ); + HEAD_SIZEOF2(InfoReq, info_req, 8, 4 * 22 ); + HEAD_SIZEOF1(Addr, addr, 24 ); + HEAD_SIZEOF1(EntryAddr, entry_addr, 12 ); + HEAD_SIZEOF1(Flags, flags, 12 ); + HEAD_SIZEOF1(Framebuffer, framebuffer, 20 ); + HEAD_SIZEOF1(ModuleAlign, module_align, 8 ); + HEAD_SIZEOF1(EFIBootServices, efi_boot_services, 8 ); + HEAD_SIZEOF1(EFII386EntryAddr, efi_i386_entry_addr, 12 ); + HEAD_SIZEOF1(EFIAmd64EntryAddr, efi_amd64_entry_addr, 12 ); + HEAD_SIZEOF1(RelocatableHeader, relocatable_header, 24 ); + + INFO_SIZEOF1(None, none, 8 ); + INFO_SIZEOF2(BootCmdLine, boot_cmd_line, 8, 15 ); + INFO_SIZEOF2(BootLoaderName, boot_loader_name, 8, 22 ); + INFO_SIZEOF2(Module, module1, 16, 17 ); + INFO_SIZEOF2(Module, module2, 16, 17 ); + INFO_SIZEOF1(BasicMemoryInfo, basic_memory_info, 16 ); + INFO_SIZEOF1(BIOSBootDevice, bios_boot_device, 20 ); + INFO_SIZEOF2(MemoryMap, memory_map, 16, 160 - 16); + INFO_SIZEOF1(VBEInfo, vbe_info, 784 ); + INFO_SIZEOF2(FramebufferInfo, framebuffer_info, 32, 8 ); + INFO_SIZEOF2(ELFSymbols, elf_symbols, 20, 420 - 20); + INFO_SIZEOF1(APMTable, apm_table, 28 ); + INFO_SIZEOF1(EFI32bitSystemTablePtr, efi_32bit_system_table_ptr, 12 ); + INFO_SIZEOF1(EFI64bitSystemTablePtr, efi_64bit_system_table_ptr, 16 ); + INFO_SIZEOF2(SMBIOSTables, smbios_tables, 16, 8 ); + INFO_SIZEOF2(ACPIOldRSDP, acpi_old_rsdp, 8, 8 ); + INFO_SIZEOF2(ACPINewRSDP, acpi_new_rsdp, 8, 8 ); + INFO_SIZEOF2(NetworkingInfo, networking_info, 8, 8 ); + INFO_SIZEOF2(EFIMemoryMap, efi_memory_map, 16, 8 ); + INFO_SIZEOF1(EFIBootServicesNotTerminated, efi_boot_services_not_terminated, 8 ); + INFO_SIZEOF1(EFI32bitImageHandlePtr, efi_32bit_image_handle_ptr, 12 ); + INFO_SIZEOF1(EFI64bitImageHandlePtr, efi_64bit_image_handle_ptr, 16 ); + INFO_SIZEOF1(ImageLoadBasePhysAddr, image_load_base_phys_addr, 12 ); + + const uint8_t *head = ((const uint8_t*)&multiboot2_header_example2) + 16; + const uint8_t *info = ((const uint8_t*)&multiboot2_info_example2) + 8; + + // (name, tag, data, align); + HEAD2(info_req, /**/ 4 * 22, 0 ); + HEAD2(addr, 16, /**/ 0 ); + HEAD2(entry_addr, 4, /**/ 4 ); + HEAD2(flags, 4, /**/ 4 ); + HEAD2(framebuffer, 12, /**/ 4 ); + HEAD0(module_align /**/ /**/ /**/ ); + HEAD0(efi_boot_services /**/ /**/ /**/ ); + HEAD2(efi_i386_entry_addr, 4, /**/ 4 ); + HEAD2(efi_amd64_entry_addr, 4, /**/ 4 ); + HEAD2(relocatable_header, 16, /**/ 0 ); + HEAD0(none /**/ /**/ /**/ ); + + // (name, tag, data, align); + INFO2(boot_cmd_line, /**/ 15, 1 ); + INFO2(boot_loader_name, /**/ 22, 2 ); + INFO3(module1, 8, 17, 7 ); + INFO3(module2, 8, 17, 7 ); + INFO2(basic_memory_info, 8, /**/ 0 ); + INFO2(bios_boot_device, 12, /**/ 4 ); + INFO3(memory_map, 8, 160 - 16, 0 ); + INFO2(vbe_info, 776, /**/ 0 ); + INFO3(framebuffer_info, 23, 8, 1 ); + INFO3(elf_symbols, 8, 420 - 16, 4 ); + INFO2(apm_table, 20, /**/ 4 ); + INFO2(efi_32bit_system_table_ptr, 4, /**/ 4 ); + INFO2(efi_64bit_system_table_ptr, 8, /**/ 0 ); + INFO3(smbios_tables, 8, 8, 0 ); + INFO2(acpi_old_rsdp, /**/ 8, 0 ); + INFO2(acpi_new_rsdp, /**/ 8, 0 ); + INFO2(networking_info, /**/ 8, 0 ); + INFO3(efi_memory_map, 8, 8, 0 ); + INFO0(efi_boot_services_not_terminated /**/ /**/ /**/ ); + INFO2(efi_32bit_image_handle_ptr, 4, /**/ 4 ); + INFO2(efi_64bit_image_handle_ptr, 8, /**/ 0 ); + INFO2(image_load_base_phys_addr, 4, /**/ 4 ); + INFO0(none /**/ /**/ /**/ ); +} diff --git a/tests/test_multiboot2_header_helpers.c b/tests/test_multiboot2_header_helpers.c new file mode 100644 index 0000000..10d2aa1 --- /dev/null +++ b/tests/test_multiboot2_header_helpers.c @@ -0,0 +1,13 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "../fixtures/multiboot2_header_example1.h" +#include "../fixtures/multiboot2_header_example2.h" + +void test_main() +{ + // TODO: write this +} diff --git a/tests/test_multiboot2_header_print.c.in b/tests/test_multiboot2_header_print.c.in new file mode 100644 index 0000000..00cc39e --- /dev/null +++ b/tests/test_multiboot2_header_print.c.in @@ -0,0 +1,61 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifndef __USE_POSIX2 +#define __USE_POSIX2 +#endif +#include + +void test_main() +{ + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_header_print0", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_header_example0.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } + + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_header_print1", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_header_example1.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } + + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_header_print2", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_header_example2.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } +} diff --git a/tests/test_multiboot2_header_validation.c b/tests/test_multiboot2_header_validation.c new file mode 100644 index 0000000..b4a6507 --- /dev/null +++ b/tests/test_multiboot2_header_validation.c @@ -0,0 +1,8 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +void test_main() +{ + // TODO: write this +} diff --git a/tests/test_multiboot2_info_convert_memmap.c b/tests/test_multiboot2_info_convert_memmap.c new file mode 100644 index 0000000..083f828 --- /dev/null +++ b/tests/test_multiboot2_info_convert_memmap.c @@ -0,0 +1,128 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include + +#include "../fixtures/multiboot2_info_example0.h" +#include "../fixtures/multiboot2_info_example1.h" +#include "../fixtures/multiboot2_info_example2.h" + +static char buffer[4096]; + +static void test_examples_1_and_2(KernAux_Memmap_Node node); + +void test_main() +{ + struct KernAux_FreeList malloc = KernAux_FreeList_create(NULL); + KernAux_FreeList_add_zone(&malloc, buffer, sizeof(buffer)); + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + &multiboot2_info_example0.multiboot2_info, + &malloc.malloc + ); + assert(builder == NULL); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + (const struct KernAux_Multiboot2_Info*) + &multiboot2_info_example1, + &malloc.malloc + ); + assert(builder); + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap->root_node); + + KernAux_Memmap_free(memmap); + } + + { + const KernAux_Memmap_Builder builder = + KernAux_Multiboot2_Info_to_memmap_builder( + &multiboot2_info_example2.multiboot2_info, + &malloc.malloc + ); + assert(builder); + const KernAux_Memmap memmap = + KernAux_Memmap_Builder_finish_and_free(builder); + assert(memmap); + + test_examples_1_and_2(memmap->root_node); + + KernAux_Memmap_free(memmap); + } +} + +void test_examples_1_and_2(KernAux_Memmap_Node node) +{ + assert(node); + assert(node->mem_start == 0x0); + assert(node->mem_size == 0xffffffffffffffff); + assert(node->mem_end == 0xffffffffffffffff); + assert(node->tag == NULL); + assert(node->next == NULL); + + node = node->children; + assert(node); + assert(node->mem_start == 0x0); + assert(node->mem_size == 654336); + assert(node->mem_end == 0x9fbff); + assert(strcmp(node->tag, "available") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x9fc00); + assert(node->mem_size == 1024); + assert(node->mem_end == 0x9ffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0xf0000); + assert(node->mem_size == 65536); + assert(node->mem_end == 0xfffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x100000); + assert(node->mem_size == 133038080); + assert(node->mem_end == 0x7fdffff); + assert(strcmp(node->tag, "available") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0x7fe0000); + assert(node->mem_size == 131072); + assert(node->mem_end == 0x7ffffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node); + assert(node->mem_start == 0xfffc0000); + assert(node->mem_size == 262144); + assert(node->mem_end == 0xffffffff); + assert(strcmp(node->tag, "reserved") == 0); + assert(node->children == NULL); + + node = node->next; + assert(node == NULL); +} diff --git a/tests/test_multiboot2_info_helpers.c b/tests/test_multiboot2_info_helpers.c new file mode 100644 index 0000000..bd9592e --- /dev/null +++ b/tests/test_multiboot2_info_helpers.c @@ -0,0 +1,401 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "../fixtures/multiboot2_info_example1.h" +#include "../fixtures/multiboot2_info_example2.h" + +#include + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_without_boot_cmd_line = { + .multiboot2_info = { + .total_size = sizeof(multiboot2_without_boot_cmd_line), + .reserved = 0, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = sizeof(multiboot2_without_boot_cmd_line.tag_none), + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + + struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[14]; + } tag_boot_cmd_line; + + unsigned char _align1[2]; + + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_some_boot_cmd_line = { + .multiboot2_info = { + .total_size = sizeof(multiboot2_with_some_boot_cmd_line), + .reserved = 0, + }, + .tag_boot_cmd_line = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = sizeof( + multiboot2_with_some_boot_cmd_line.tag_boot_cmd_line + ), + }, + }, + .cmdline = "Hello, World!\0", + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = sizeof(multiboot2_with_some_boot_cmd_line.tag_none), + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + + struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[14]; + } tag_boot_cmd_line1; + + unsigned char _align1[2]; + + struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[13]; + } tag_boot_cmd_line2; + + unsigned char _align2[3]; + + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_two_boot_cmd_lines = { + .multiboot2_info = { + .total_size = sizeof(multiboot2_with_two_boot_cmd_lines), + .reserved = 0, + }, + .tag_boot_cmd_line1 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = sizeof( + multiboot2_with_two_boot_cmd_lines.tag_boot_cmd_line1 + ), + }, + }, + .cmdline = "Hello, World!\0", + }, + .tag_boot_cmd_line2 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = sizeof( + multiboot2_with_two_boot_cmd_lines.tag_boot_cmd_line2 + ), + }, + }, + .cmdline = "Hello again!\0", + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = sizeof(multiboot2_with_two_boot_cmd_lines.tag_none), + }, + }, +}; + +#include + +void test_main() +{ + assert(KernAux_Multiboot2_Info_is_valid( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1 + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_info_example2.multiboot2_info + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_without_boot_cmd_line.multiboot2_info + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_some_boot_cmd_line.multiboot2_info + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_two_boot_cmd_lines.multiboot2_info + )); + + assert( + KernAux_Multiboot2_Info_boot_cmd_line( + &multiboot2_without_boot_cmd_line.multiboot2_info + ) == 0 + ); + + // KernAux_Multiboot2_Info_first_tag_with_type + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + (struct KernAux_Multiboot2_Info*)multiboot2_info_example1, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR + ) == 0 + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_NONE + ) == &multiboot2_info_example2.tag_none.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE + ) == &multiboot2_info_example2.tag_boot_cmd_line.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME + ) == &multiboot2_info_example2.tag_boot_loader_name.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE + ) == &multiboot2_info_example2.tag_module1.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO + ) == &multiboot2_info_example2.tag_basic_memory_info.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_FRAMEBUFFER_INFO + ) == &multiboot2_info_example2.tag_framebuffer_info.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS + ) == &multiboot2_info_example2.tag_elf_symbols.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_APM_TABLE + ) == &multiboot2_info_example2.tag_apm_table.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_SYSTEM_TABLE_PTR + ) == &multiboot2_info_example2.tag_efi_32bit_system_table_ptr.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_SYSTEM_TABLE_PTR + ) == &multiboot2_info_example2.tag_efi_64bit_system_table_ptr.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_SMBIOS_TABLES + ) == &multiboot2_info_example2.tag_smbios_tables.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_ACPI_OLD_RSDP + ) == &multiboot2_info_example2.tag_acpi_old_rsdp.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_ACPI_NEW_RSDP + ) == &multiboot2_info_example2.tag_acpi_new_rsdp.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_NETWORKING_INFO + ) == &multiboot2_info_example2.tag_networking_info.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_MEMORY_MAP + ) == &multiboot2_info_example2.tag_efi_memory_map.tag.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_BOOT_SERVICES_NOT_TERMINATED + ) == &multiboot2_info_example2.tag_efi_boot_services_not_terminated.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_32BIT_IMAGE_HANDLE_PTR + ) == &multiboot2_info_example2.tag_efi_32bit_image_handle_ptr.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_EFI_64BIT_IMAGE_HANDLE_PTR + ) == &multiboot2_info_example2.tag_efi_64bit_image_handle_ptr.base + ); + + assert( + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_IMAGE_LOAD_BASE_PHYS_ADDR + ) == &multiboot2_info_example2.tag_image_load_base_phys_addr.base + ); + + // KernAux_Multiboot2_Info_tag_with_type_after + + assert( + KernAux_Multiboot2_Info_tag_with_type_after( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE, + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE + ) - 1 + ) == (struct KernAux_Multiboot2_ITagBase*) + &multiboot2_info_example2.tag_module1 + ); + + assert( + KernAux_Multiboot2_Info_tag_with_type_after( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE, + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE + ) + ) == (struct KernAux_Multiboot2_ITagBase*) + &multiboot2_info_example2.tag_module2 + ); + + assert( + KernAux_Multiboot2_Info_tag_with_type_after( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE, + KernAux_Multiboot2_Info_tag_with_type_after( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE, + KernAux_Multiboot2_Info_first_tag_with_type( + &multiboot2_info_example2.multiboot2_info, + KERNAUX_MULTIBOOT2_ITAG_MODULE + ) + ) + ) == 0 + ); + + // KernAux_Multiboot2_Info_boot_cmd_line + + assert( + KernAux_Multiboot2_Info_boot_cmd_line( + &multiboot2_with_some_boot_cmd_line.multiboot2_info + ) == multiboot2_with_some_boot_cmd_line.tag_boot_cmd_line.cmdline + ); + + assert( + KernAux_Multiboot2_Info_boot_cmd_line( + &multiboot2_with_two_boot_cmd_lines.multiboot2_info + ) == multiboot2_with_two_boot_cmd_lines.tag_boot_cmd_line1.cmdline + ); +} diff --git a/tests/test_multiboot2_info_print.c.in b/tests/test_multiboot2_info_print.c.in new file mode 100644 index 0000000..d4155a9 --- /dev/null +++ b/tests/test_multiboot2_info_print.c.in @@ -0,0 +1,61 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#ifndef __USE_POSIX2 +#define __USE_POSIX2 +#endif +#include + +void test_main() +{ + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_info_print0", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_info_example0.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } + + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_info_print1", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_info_example1.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } + + { + FILE *const print_file = + popen("@abs_top_builddir@/tests/multiboot2_info_print2", "r"); + FILE *const text_file = + fopen("@abs_top_srcdir@/fixtures/multiboot2_info_example2.txt", "r"); + assert(print_file != NULL); + assert(text_file != NULL); + + while (!feof(text_file)) { + assert(fgetc(print_file) == fgetc(text_file)); + } + + assert(pclose(print_file) == 0); + assert(fclose(text_file) == 0); + } +} diff --git a/tests/test_multiboot2_info_validation.c b/tests/test_multiboot2_info_validation.c new file mode 100644 index 0000000..0aebdf4 --- /dev/null +++ b/tests/test_multiboot2_info_validation.c @@ -0,0 +1,1128 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include + +#include "../fixtures/multiboot2_info_example1.h" +#include "../fixtures/multiboot2_info_example2.h" + +#include + +/************ + * Tag_None * + ************/ + +static const struct KernAux_Multiboot2_ITag_None tag_none_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, +}; + +static const struct KernAux_Multiboot2_ITag_None tag_none_invalid_type = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = 8, + }, +}; + +static const struct KernAux_Multiboot2_ITag_None tag_none_invalid_size = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 9, + }, +}; + +/******************* + * Tag_BootCmdLine * + *******************/ + +static const struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[1]; +} KERNAUX_PACKED tag_boot_cmd_line_with_empty_cmdline_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = 9, + }, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[14]; +} KERNAUX_PACKED tag_boot_cmd_line_with_some_cmdline_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = 22, + }, + }, + .cmdline = "Hello, World!\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[1]; +} KERNAUX_PACKED tag_boot_cmd_line_invalid_type = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 9, + }, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[1]; +} KERNAUX_PACKED tag_boot_cmd_line_with_empty_cmdline_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = 10, + }, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootCmdLine tag; + char cmdline[14]; +} KERNAUX_PACKED tag_boot_cmd_line_with_some_cmdline_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_CMD_LINE, + .size = 23, + }, + }, + .cmdline = "Hello, World!\0", +}; + +/********************** + * Tag_BootLoaderName * + **********************/ + +static const struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[1]; +} KERNAUX_PACKED tag_boot_loader_name_with_empty_name_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + .size = 9, + }, + }, + .name = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[14]; +} KERNAUX_PACKED tag_boot_loader_name_with_some_name_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + .size = 22, + }, + }, + .name = "Hello, World!\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[1]; +} KERNAUX_PACKED tag_boot_loader_name_invalid_type = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 9, + }, + }, + .name = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[1]; +} KERNAUX_PACKED tag_boot_loader_name_with_empty_name_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + .size = 10, + }, + }, + .name = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_BootLoaderName tag; + char name[14]; +} KERNAUX_PACKED tag_boot_loader_name_with_some_name_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BOOT_LOADER_NAME, + .size = 23, + }, + }, + .name = "Hello, World!\0", +}; + +/************** + * Tag_Module * + **************/ + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[1]; +} KERNAUX_PACKED tag_module_with_empty_name_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 17, + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[14]; +} KERNAUX_PACKED tag_module_with_some_name_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 30, + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "Hello, World!\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[1]; +} KERNAUX_PACKED tag_module_invalid_type = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 17, + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[1]; +} KERNAUX_PACKED tag_module_with_empty_name_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 18, + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[14]; +} KERNAUX_PACKED tag_module_with_some_name_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 31, + }, + .mod_start = 123, + .mod_end = 456, + }, + .cmdline = "Hello, World!\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[14]; +} KERNAUX_PACKED tag_module_with_equal_start_end_invalid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 31, + }, + .mod_start = 123, + .mod_end = 123, + }, + .cmdline = "Hello, World!\0", +}; + +static const struct { + struct KernAux_Multiboot2_ITag_Module tag; + char cmdline[14]; +} KERNAUX_PACKED tag_module_with_reversed_start_end_invalid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MODULE, + .size = 31, + }, + .mod_start = 456, + .mod_end = 123, + }, + .cmdline = "Hello, World!\0", +}; + +/*********************** + * Tag_BasicMemoryInfo * + ***********************/ + +static const struct KernAux_Multiboot2_ITag_BasicMemoryInfo +tag_basic_memory_info_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, +}; + +static const struct KernAux_Multiboot2_ITag_BasicMemoryInfo +tag_basic_memory_info_invalid_type = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, +}; + +static const struct KernAux_Multiboot2_ITag_BasicMemoryInfo +tag_basic_memory_info_invalid_size = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 17, + }, + .mem_lower = 123, + .mem_upper = 123, +}; + +/********************** + * Tag_BIOSBootDevice * + **********************/ + +static const struct KernAux_Multiboot2_ITag_BIOSBootDevice +tag_bios_boot_device_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, +}; + +static const struct KernAux_Multiboot2_ITag_BIOSBootDevice +tag_bios_boot_device_invalid_type = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, +}; + +static const struct KernAux_Multiboot2_ITag_BIOSBootDevice +tag_bios_boot_device_invalid_size = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 21, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, +}; + +/***************** + * Tag_MemoryMap * + *****************/ + +static const struct KernAux_Multiboot2_ITag_MemoryMap +tag_memory_map_with_empty_data_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16, + }, + .entry_size = 8, + .entry_version = 0, +}; + +static const struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + unsigned char data[8 * 2]; +} KERNAUX_PACKED tag_memory_map_with_some_small_data_items_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 8 * 2, + }, + .entry_size = 8, + .entry_version = 123, + }, +}; + +static const struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + unsigned char data[64 * 2]; +} KERNAUX_PACKED tag_memory_map_with_some_large_data_items_valid = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 64 * 2, + }, + .entry_size = 64, + .entry_version = 456, + }, +}; + +static const struct KernAux_Multiboot2_ITag_MemoryMap +tag_memory_map_invalid_type = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 16, + }, + .entry_size = 8, + .entry_version = 0, +}; + +static const struct KernAux_Multiboot2_ITag_MemoryMap +tag_memory_map_with_empty_data_invalid_size = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 1, + }, + .entry_size = 8, + .entry_version = 0, +}; + +static const struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + unsigned char data[64 * 2 + 1]; +} KERNAUX_PACKED tag_memory_map_with_some_large_data_items_invalid_size = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 64 * 2 + 1, + }, + .entry_size = 64, + .entry_version = 456, + }, +}; + +static const struct KernAux_Multiboot2_ITag_MemoryMap +tag_memory_map_with_empty_data_invalid_entry_size_zero = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16, + }, + .entry_size = 0, + .entry_version = 0, +}; + +static const struct KernAux_Multiboot2_ITag_MemoryMap +tag_memory_map_with_empty_data_invalid_entry_size_not_mul8 = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16, + }, + .entry_size = 9, + .entry_version = 0, +}; + +static const struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + unsigned char data[9 * 2]; +} KERNAUX_PACKED tag_memory_map_with_some_small_data_items_invalid_entry_size_not_mul8 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 9 * 2, + }, + .entry_size = 9, + .entry_version = 123, + }, +}; + +static const struct { + struct KernAux_Multiboot2_ITag_MemoryMap tag; + unsigned char data[63 * 2]; +} KERNAUX_PACKED tag_memory_map_with_some_large_data_items_invalid_entry_size_not_mul8 = { + .tag = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_MEMORY_MAP, + .size = 16 + 63 * 2, + }, + .entry_size = 63, + .entry_version = 123, + }, +}; + +/*************** + * Tag_VBEInfo * + ***************/ + +static const struct KernAux_Multiboot2_ITag_VBEInfo +tag_vbe_info_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_VBE_INFO, + .size = 784, + }, + .vbe_mode = 123, + .vbe_interface_seg = 456, + .vbe_interface_off = 789, + .vbe_interface_len = 123, + .vbe_control_info = {0, 0, 0}, + .vbe_mode_info = {0, 0, 0}, +}; + +static const struct KernAux_Multiboot2_ITag_VBEInfo +tag_vbe_info_invalid_type = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 784, + }, + .vbe_mode = 123, + .vbe_interface_seg = 456, + .vbe_interface_off = 789, + .vbe_interface_len = 123, + .vbe_control_info = {0, 0, 0}, + .vbe_mode_info = {0, 0, 0}, +}; + +static const struct KernAux_Multiboot2_ITag_VBEInfo +tag_vbe_info_invalid_size = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_VBE_INFO, + .size = 784 + 1, + }, + .vbe_mode = 123, + .vbe_interface_seg = 456, + .vbe_interface_off = 789, + .vbe_interface_len = 123, + .vbe_control_info = {0, 0, 0}, + .vbe_mode_info = {0, 0, 0}, +}; + +/****************** + * Tag_ELFSymbols * + ******************/ + +static const struct KernAux_Multiboot2_ITag_ELFSymbols +tag_elf_symbols_with_zero_entsize_valid = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_ELF_SYMBOLS, + .size = 20, + }, + .num = 0, + .entsize = 0, + .shndx = 0, +}; + +/************** + * Multiboot2 * + **************/ + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_empty_valid = { + .multiboot2_info = { + .total_size = 8 + 8, + .reserved = 0, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_some_additional_tag_valid = { + .multiboot2_info = { + .total_size = 8 + 16 + 8, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device; + unsigned char _align1[4]; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_more_additional_tags_valid = { + .multiboot2_info = { + .total_size = 8 + 16 + (20 + 4) + 8, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, + .tag_bios_boot_device = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_empty_invalid_size = { + .multiboot2_info = { + .total_size = 8, + .reserved = 0, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct KernAux_Multiboot2_Info +multiboot2_without_none_tag_invalid = { + .total_size = 8, + .reserved = 0, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; +} KERNAUX_PACKED multiboot2_with_invalid_last_tag_invalid = { + .multiboot2_info = { + .total_size = 8 + 16, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + struct KernAux_Multiboot2_ITag_None tag_none1; + struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device; + unsigned char _align1[4]; + struct KernAux_Multiboot2_ITag_None tag_none2; +} KERNAUX_PACKED multiboot2_with_early_none_tag_invalid = { + .multiboot2_info = { + .total_size = 8 + 16 + 8 + (20 + 4) + 8, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, + .tag_none1 = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, + .tag_bios_boot_device = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, + }, + .tag_none2 = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device; + unsigned char _align1[4]; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_more_additional_tags_invalid_size_too_big = { + .multiboot2_info = { + .total_size = 8 + 16 + (20 + 4) + 8 + 1, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, + .tag_bios_boot_device = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +static const struct { + struct KernAux_Multiboot2_Info multiboot2_info; + struct KernAux_Multiboot2_ITag_BasicMemoryInfo tag_basic_memory_info; + struct KernAux_Multiboot2_ITag_BIOSBootDevice tag_bios_boot_device; + unsigned char _align1[4]; + struct KernAux_Multiboot2_ITag_None tag_none; +} KERNAUX_PACKED multiboot2_with_more_additional_tags_invalid_size_too_small = { + .multiboot2_info = { + .total_size = 8 + 16 + (20 + 4) + 8 - 1, + .reserved = 0, + }, + .tag_basic_memory_info = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BASIC_MEMORY_INFO, + .size = 16, + }, + .mem_lower = 123, + .mem_upper = 123, + }, + .tag_bios_boot_device = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_BIOS_BOOT_DEVICE, + .size = 20, + }, + .biosdev = 123, + .partition = 456, + .sub_partition = 789, + }, + .tag_none = { + .base = { + .type = KERNAUX_MULTIBOOT2_ITAG_NONE, + .size = 8, + }, + }, +}; + +#include + +/******** + * main * + ********/ + +void test_main() +{ + // Multiboot2 + + assert(KernAux_Multiboot2_Info_is_valid( + (struct KernAux_Multiboot2_Info*)&multiboot2_info_example1 + )); + + assert(KernAux_Multiboot2_Info_is_valid( + (struct KernAux_Multiboot2_Info*)&multiboot2_info_example2 + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_empty_valid.multiboot2_info + )); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_some_additional_tag_valid.multiboot2_info) + ); + + assert(KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_more_additional_tags_valid.multiboot2_info) + ); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_empty_invalid_size.multiboot2_info + )); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_without_none_tag_invalid + )); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_invalid_last_tag_invalid.multiboot2_info + )); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_early_none_tag_invalid.multiboot2_info + )); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_more_additional_tags_invalid_size_too_big.multiboot2_info + )); + + assert(!KernAux_Multiboot2_Info_is_valid( + &multiboot2_with_more_additional_tags_invalid_size_too_small.multiboot2_info + )); + + // ITagBase + + assert(KernAux_Multiboot2_ITagBase_is_valid(&tag_none_valid.base)); + assert(!KernAux_Multiboot2_ITagBase_is_valid(&tag_none_invalid_size.base)); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_cmd_line_with_empty_cmdline_valid.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_cmd_line_with_some_cmdline_valid.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_cmd_line_with_empty_cmdline_invalid_size.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_cmd_line_with_some_cmdline_invalid_size.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_loader_name_with_empty_name_valid.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_loader_name_with_some_name_valid.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_loader_name_with_empty_name_invalid_size.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_boot_loader_name_with_some_name_invalid_size.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_empty_name_valid.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_some_name_valid.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_empty_name_invalid_size.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_some_name_invalid_size.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_equal_start_end_invalid.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_module_with_reversed_start_end_invalid.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_basic_memory_info_valid.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_basic_memory_info_invalid_size.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_bios_boot_device_valid.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_bios_boot_device_invalid_size.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_empty_data_valid.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_some_small_data_items_valid.tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_some_large_data_items_valid.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_empty_data_invalid_size.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_some_large_data_items_invalid_size.tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_empty_data_invalid_entry_size_zero.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_empty_data_invalid_entry_size_not_mul8.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_some_small_data_items_invalid_entry_size_not_mul8 + .tag.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_memory_map_with_some_large_data_items_invalid_entry_size_not_mul8 + .tag.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_vbe_info_valid.base + )); + + assert(!KernAux_Multiboot2_ITagBase_is_valid( + &tag_vbe_info_invalid_size.base + )); + + assert(KernAux_Multiboot2_ITagBase_is_valid( + &tag_elf_symbols_with_zero_entsize_valid.base + )); + + // Tag_None + + assert(KernAux_Multiboot2_ITag_None_is_valid(&tag_none_valid)); + assert(!KernAux_Multiboot2_ITag_None_is_valid(&tag_none_invalid_type)); + assert(!KernAux_Multiboot2_ITag_None_is_valid(&tag_none_invalid_size)); + + // Tag_BootCmdLine + + assert(KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + &tag_boot_cmd_line_with_empty_cmdline_valid.tag + )); + + assert(KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + &tag_boot_cmd_line_with_some_cmdline_valid.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + &tag_boot_cmd_line_invalid_type.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + &tag_boot_cmd_line_with_empty_cmdline_invalid_size.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootCmdLine_is_valid( + &tag_boot_cmd_line_with_some_cmdline_invalid_size.tag + )); + + // Tag_BootLoaderName + + assert(KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + &tag_boot_loader_name_with_empty_name_valid.tag + )); + + assert(KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + &tag_boot_loader_name_with_some_name_valid.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + &tag_boot_loader_name_invalid_type.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + &tag_boot_loader_name_with_empty_name_invalid_size.tag + )); + + assert(!KernAux_Multiboot2_ITag_BootLoaderName_is_valid( + &tag_boot_loader_name_with_some_name_invalid_size.tag + )); + + // Tag_Module + + assert(KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_empty_name_valid.tag + )); + + assert(KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_some_name_valid.tag + )); + + assert(!KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_invalid_type.tag + )); + + assert(!KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_empty_name_invalid_size.tag + )); + + assert(!KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_some_name_invalid_size.tag + )); + + assert(!KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_equal_start_end_invalid.tag + )); + + assert(!KernAux_Multiboot2_ITag_Module_is_valid( + &tag_module_with_reversed_start_end_invalid.tag + )); + + // Tag_BasicMemoryInfo + + assert(KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + &tag_basic_memory_info_valid + )); + + assert(!KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + &tag_basic_memory_info_invalid_type + )); + + assert(!KernAux_Multiboot2_ITag_BasicMemoryInfo_is_valid( + &tag_basic_memory_info_invalid_size + )); + + // Tag_BIOSBootDevice + + assert(KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + &tag_bios_boot_device_valid + )); + + assert(!KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + &tag_bios_boot_device_invalid_type + )); + + assert(!KernAux_Multiboot2_ITag_BIOSBootDevice_is_valid( + &tag_bios_boot_device_invalid_size + )); + + // Tag_MemoryMap + + assert(KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_empty_data_valid + )); + + assert(KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_some_small_data_items_valid.tag + )); + + assert(KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_some_large_data_items_valid.tag + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_invalid_type + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_some_large_data_items_invalid_size.tag + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_empty_data_invalid_entry_size_zero + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_empty_data_invalid_entry_size_not_mul8 + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_some_small_data_items_invalid_entry_size_not_mul8 + .tag + )); + + assert(!KernAux_Multiboot2_ITag_MemoryMap_is_valid( + &tag_memory_map_with_some_large_data_items_invalid_entry_size_not_mul8 + .tag + )); + + // Tag_VBEInfo + + assert(KernAux_Multiboot2_ITag_VBEInfo_is_valid( + &tag_vbe_info_valid + )); + + assert(!KernAux_Multiboot2_ITag_VBEInfo_is_valid( + &tag_vbe_info_invalid_type + )); + + assert(!KernAux_Multiboot2_ITag_VBEInfo_is_valid( + &tag_vbe_info_invalid_size + )); + + // Tag_ELFSymbols + + + assert(KernAux_Multiboot2_ITag_ELFSymbols_is_valid( + &tag_elf_symbols_with_zero_entsize_valid + )); +} diff --git a/tests/test_ntoa.c b/tests/test_ntoa.c new file mode 100644 index 0000000..05a8e62 --- /dev/null +++ b/tests/test_ntoa.c @@ -0,0 +1,777 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +#define VALID_LONG_PREFIX "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define TOO_LONG_PREFIX "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +static const struct { + const char *result; + int base; + uint64_t value; +} utoa_cases[] = { + { "0", 2, 0 }, { "0", -2, 0 }, { "0", 'b', 0 }, { "0", 'B', 0 }, + { "0", 3, 0 }, { "0", -3, 0 }, + { "0", 4, 0 }, { "0", -4, 0 }, + { "0", 5, 0 }, { "0", -5, 0 }, + { "0", 6, 0 }, { "0", -6, 0 }, + { "0", 7, 0 }, { "0", -7, 0 }, + { "0", 8, 0 }, { "0", -8, 0 }, { "0", 'o', 0 }, { "0", 'O', 0 }, + { "0", 9, 0 }, { "0", -9, 0 }, + { "0", 10, 0 }, { "0", -10, 0 }, { "0", 'd', 0 }, { "0", 'D', 0 }, + { "0", 11, 0 }, { "0", -11, 0 }, + { "0", 12, 0 }, { "0", -12, 0 }, + { "0", 13, 0 }, { "0", -13, 0 }, + { "0", 14, 0 }, { "0", -14, 0 }, + { "0", 15, 0 }, { "0", -15, 0 }, + { "0", 16, 0 }, { "0", -16, 0 }, + { "0", 'h', 0 }, { "0", 'H', 0 }, { "0", 'x', 0 }, { "0", 'X', 0 }, + { "0", 17, 0 }, { "0", -17, 0 }, + { "0", 18, 0 }, { "0", -18, 0 }, + { "0", 19, 0 }, { "0", -19, 0 }, + { "0", 20, 0 }, { "0", -20, 0 }, + { "0", 21, 0 }, { "0", -21, 0 }, + { "0", 22, 0 }, { "0", -22, 0 }, + { "0", 23, 0 }, { "0", -23, 0 }, + { "0", 24, 0 }, { "0", -24, 0 }, + { "0", 25, 0 }, { "0", -25, 0 }, + { "0", 26, 0 }, { "0", -26, 0 }, + { "0", 27, 0 }, { "0", -27, 0 }, + { "0", 28, 0 }, { "0", -28, 0 }, + { "0", 29, 0 }, { "0", -29, 0 }, + { "0", 30, 0 }, { "0", -30, 0 }, + { "0", 31, 0 }, { "0", -31, 0 }, + { "0", 32, 0 }, { "0", -32, 0 }, + { "0", 33, 0 }, { "0", -33, 0 }, + { "0", 34, 0 }, { "0", -34, 0 }, + { "0", 35, 0 }, { "0", -35, 0 }, + { "0", 36, 0 }, { "0", -36, 0 }, + { "1111011", 2, 123 }, { "1111011", -2, 123 }, + { "1111011", 'b', 123 }, { "1111011", 'B', 123 }, + { "11120", 3, 123 }, { "11120", -3, 123 }, + { "1323", 4, 123 }, { "1323", -4, 123 }, + { "443", 5, 123 }, { "443", -5, 123 }, + { "323", 6, 123 }, { "323", -6, 123 }, + { "234", 7, 123 }, { "234", -7, 123 }, + { "173", 8, 123 }, { "173", -8, 123 }, + { "173", 'o', 123 }, { "173", 'O', 123 }, + { "146", 9, 123 }, { "146", -9, 123 }, + { "123", 10, 123 }, { "123", -10, 123 }, + { "123", 'd', 123 }, { "123", 'D', 123 }, + { "102", 11, 123 }, { "102", -11, 123 }, + { "a3", 12, 123 }, { "A3", -12, 123 }, + { "96", 13, 123 }, { "96", -13, 123 }, + { "8b", 14, 123 }, { "8B", -14, 123 }, + { "83", 15, 123 }, { "83", -15, 123 }, + { "7b", 16, 123 }, { "7B", -16, 123 }, + { "7b", 'h', 123 }, { "7B", 'H', 123 }, + { "7b", 'x', 123 }, { "7B", 'X', 123 }, + { "74", 17, 123 }, { "74", -17, 123 }, + { "6f", 18, 123 }, { "6F", -18, 123 }, + { "69", 19, 123 }, { "69", -19, 123 }, + { "63", 20, 123 }, { "63", -20, 123 }, + { "5i", 21, 123 }, { "5I", -21, 123 }, + { "5d", 22, 123 }, { "5D", -22, 123 }, + { "58", 23, 123 }, { "58", -23, 123 }, + { "53", 24, 123 }, { "53", -24, 123 }, + { "4n", 25, 123 }, { "4N", -25, 123 }, + { "4j", 26, 123 }, { "4J", -26, 123 }, + { "4f", 27, 123 }, { "4F", -27, 123 }, + { "4b", 28, 123 }, { "4B", -28, 123 }, + { "47", 29, 123 }, { "47", -29, 123 }, + { "43", 30, 123 }, { "43", -30, 123 }, + { "3u", 31, 123 }, { "3U", -31, 123 }, + { "3r", 32, 123 }, { "3R", -32, 123 }, + { "3o", 33, 123 }, { "3O", -33, 123 }, + { "3l", 34, 123 }, { "3L", -34, 123 }, + { "3i", 35, 123 }, { "3I", -35, 123 }, + { "3f", 36, 123 }, { "3F", -36, 123 }, + { "11000000111001", 2, 12345 }, { "11000000111001", -2, 12345 }, + { "121221020", 3, 12345 }, { "121221020", -3, 12345 }, + { "3000321", 4, 12345 }, { "3000321", -4, 12345 }, + { "343340", 5, 12345 }, { "343340", -5, 12345 }, + { "133053", 6, 12345 }, { "133053", -6, 12345 }, + { "50664", 7, 12345 }, { "50664", -7, 12345 }, + { "30071", 8, 12345 }, { "30071", -8, 12345 }, + { "17836", 9, 12345 }, { "17836", -9, 12345 }, + { "12345", 10, 12345 }, { "12345", -10, 12345 }, + { "9303", 11, 12345 }, { "9303", -11, 12345 }, + { "7189", 12, 12345 }, { "7189", -12, 12345 }, + { "5808", 13, 12345 }, { "5808", -13, 12345 }, + { "46db", 14, 12345 }, { "46DB", -14, 12345 }, + { "39d0", 15, 12345 }, { "39D0", -15, 12345 }, + { "3039", 16, 12345 }, { "3039", -16, 12345 }, + { "28c3", 17, 12345 }, { "28C3", -17, 12345 }, + { "221f", 18, 12345 }, { "221F", -18, 12345 }, + { "1f3e", 19, 12345 }, { "1F3E", -19, 12345 }, + { "1ah5", 20, 12345 }, { "1AH5", -20, 12345 }, + { "16ki", 21, 12345 }, { "16KI", -21, 12345 }, + { "13b3", 22, 12345 }, { "13B3", -22, 12345 }, + { "107h", 23, 12345 }, { "107H", -23, 12345 }, + { "la9", 24, 12345 }, { "LA9", -24, 12345 }, + { "jik", 25, 12345 }, { "JIK", -25, 12345 }, + { "i6l", 26, 12345 }, { "I6L", -26, 12345 }, + { "gp6", 27, 12345 }, { "GP6", -27, 12345 }, + { "fkp", 28, 12345 }, { "FKP", -28, 12345 }, + { "ejk", 29, 12345 }, { "EJK", -29, 12345 }, + { "dlf", 30, 12345 }, { "DLF", -30, 12345 }, + { "cq7", 31, 12345 }, { "CQ7", -31, 12345 }, + { "c1p", 32, 12345 }, { "C1P", -32, 12345 }, + { "bb3", 33, 12345 }, { "BB3", -33, 12345 }, + { "an3", 34, 12345 }, { "AN3", -34, 12345 }, + { "a2p", 35, 12345 }, { "A2P", -35, 12345 }, + { "9ix", 36, 12345 }, { "9IX", -36, 12345 }, +}; + +static const struct { + uint64_t value; + const char *result; +} utoa2_cases[] = { + { 0, "0" }, + { 1, "1" }, + { 2, "10" }, + { 3, "11" }, + { 4, "100" }, + { 5, "101" }, + { 6, "110" }, + { 7, "111" }, + { 8, "1000" }, + { UINT16_MAX, "1111111111111111" }, + { UINT16_MAX + 1, "10000000000000000" }, + { UINT32_MAX, "11111111111111111111111111111111" }, + { (uint64_t)UINT32_MAX + 1, "100000000000000000000000000000000" }, + { UINT64_MAX - 6, "1111111111111111111111111111111111111111111111111111111111111001" }, + { UINT64_MAX - 5, "1111111111111111111111111111111111111111111111111111111111111010" }, + { UINT64_MAX - 1, "1111111111111111111111111111111111111111111111111111111111111110" }, + { UINT64_MAX, "1111111111111111111111111111111111111111111111111111111111111111" }, +}; + +static const struct { + int64_t value; + const char *result; +} itoa2_cases[] = { + { 0, "0" }, + { 1, "1" }, + { -1, "-1" }, + { 2, "10" }, + { -2, "-10" }, + { 3, "11" }, + { -3, "-11" }, + { 4, "100" }, + { -4, "-100" }, + { 5, "101" }, + { -5, "-101" }, + { 6, "110" }, + { -6, "-110" }, + { 7, "111" }, + { -7, "-111" }, + { 8, "1000" }, + { -8, "-1000" }, + { UINT16_MAX, "1111111111111111" }, + { UINT16_MAX + 1, "10000000000000000" }, + { UINT32_MAX, "11111111111111111111111111111111" }, + { (int64_t)UINT32_MAX + 1, "100000000000000000000000000000000" }, + { INT64_MAX - 6, "111111111111111111111111111111111111111111111111111111111111001" }, + { INT64_MIN + 7, "-111111111111111111111111111111111111111111111111111111111111001" }, + { INT64_MAX - 5, "111111111111111111111111111111111111111111111111111111111111010" }, + { INT64_MIN + 6, "-111111111111111111111111111111111111111111111111111111111111010" }, + { INT64_MAX - 1, "111111111111111111111111111111111111111111111111111111111111110" }, + { INT64_MIN + 2, "-111111111111111111111111111111111111111111111111111111111111110" }, + { INT64_MAX, "111111111111111111111111111111111111111111111111111111111111111" }, + { INT64_MIN + 1, "-111111111111111111111111111111111111111111111111111111111111111" }, + { INT64_MIN, "-1000000000000000000000000000000000000000000000000000000000000000" }, +}; + +static const struct { + uint64_t value; + const char *result; +} utoa8_cases[] = { + { 00, "0" }, + { 01, "1" }, + { 02, "2" }, + { 07, "7" }, + { 010, "10" }, + { 011, "11" }, + { 012, "12" }, + { 017, "17" }, + { 020, "20" }, + { 021, "21" }, + { 077, "77" }, + { 0100, "100" }, + { 0101, "101" }, + { 0177, "177" }, + { 0200, "200" }, + { 0201, "201" }, + { 0777, "777" }, + { 01000, "1000" }, + { 01001, "1001" }, + { 01777, "1777" }, + { 02000, "2000" }, + { 02001, "2001" }, + { 07777, "7777" }, + { 010000, "10000" }, + { 010001, "10001" }, + { UINT16_MAX, "177777" }, + { UINT16_MAX + 1, "200000" }, + { UINT32_MAX, "37777777777" }, + { (uint64_t)UINT32_MAX + 1, "40000000000" }, + { UINT64_MAX - 6, "1777777777777777777771" }, + { UINT64_MAX - 5, "1777777777777777777772" }, + { UINT64_MAX - 1, "1777777777777777777776" }, + { UINT64_MAX, "1777777777777777777777" }, +}; + +static const struct { + int64_t value; + const char *result; +} itoa8_cases[] = { + { 00, "0" }, + { 01, "1" }, + { -01, "-1" }, + { 02, "2" }, + { -02, "-2" }, + { 07, "7" }, + { -07, "-7" }, + { 010, "10" }, + { -010, "-10" }, + { 011, "11" }, + { -011, "-11" }, + { 012, "12" }, + { 017, "17" }, + { 020, "20" }, + { 021, "21" }, + { 077, "77" }, + { 0100, "100" }, + { 0101, "101" }, + { 0177, "177" }, + { 0200, "200" }, + { 0201, "201" }, + { 0777, "777" }, + { 01000, "1000" }, + { 01001, "1001" }, + { 01777, "1777" }, + { 02000, "2000" }, + { 02001, "2001" }, + { 07777, "7777" }, + { 010000, "10000" }, + { 010001, "10001" }, + { UINT16_MAX, "177777" }, + { UINT16_MAX + 1, "200000" }, + { UINT32_MAX, "37777777777" }, + { (uint64_t)UINT32_MAX + 1, "40000000000" }, + { INT64_MAX - 6, "777777777777777777771" }, + { INT64_MIN + 7, "-777777777777777777771" }, + { INT64_MAX - 5, "777777777777777777772" }, + { INT64_MIN + 6, "-777777777777777777772" }, + { INT64_MAX - 1, "777777777777777777776" }, + { INT64_MIN + 2, "-777777777777777777776" }, + { INT64_MAX, "777777777777777777777" }, + { INT64_MIN + 1, "-777777777777777777777" }, + { INT64_MIN, "-1000000000000000000000" }, +}; + +static const struct { + uint64_t value; + const char *result; +} utoa10_cases[] = { + { 0, "0" }, + { 1, "1" }, + { 2, "2" }, + { 9, "9" }, + { 10, "10" }, + { 11, "11" }, + { 12, "12" }, + { 19, "19" }, + { 20, "20" }, + { 21, "21" }, + { 99, "99" }, + { 100, "100" }, + { 101, "101" }, + { 199, "199" }, + { 200, "200" }, + { 201, "201" }, + { 999, "999" }, + { 1000, "1000" }, + { 1001, "1001" }, + { 1999, "1999" }, + { 2000, "2000" }, + { 2001, "2001" }, + { 9999, "9999" }, + { 10000, "10000" }, + { 10001, "10001" }, + { UINT16_MAX, "65535" }, + { UINT16_MAX + 1, "65536" }, + { UINT32_MAX, "4294967295" }, + { (uint64_t)UINT32_MAX + 1, "4294967296" }, + { UINT64_MAX - 6, "18446744073709551609" }, + { UINT64_MAX - 5, "18446744073709551610" }, + { UINT64_MAX - 1, "18446744073709551614" }, + { UINT64_MAX, "18446744073709551615" }, +}; + +static const struct { + int64_t value; + const char *result; +} itoa10_cases[] = { + { 0, "0" }, + { 1, "1" }, + { -1, "-1" }, + { 2, "2" }, + { -2, "-2" }, + { 9, "9" }, + { -9, "-9" }, + { 10, "10" }, + { -10, "-10" }, + { 11, "11" }, + { -11, "-11" }, + { 12, "12" }, + { 19, "19" }, + { 20, "20" }, + { 21, "21" }, + { 99, "99" }, + { 100, "100" }, + { 101, "101" }, + { 199, "199" }, + { 200, "200" }, + { 201, "201" }, + { 999, "999" }, + { 1000, "1000" }, + { 1001, "1001" }, + { 1999, "1999" }, + { 2000, "2000" }, + { 2001, "2001" }, + { 9999, "9999" }, + { 10000, "10000" }, + { 10001, "10001" }, + { UINT16_MAX, "65535" }, + { UINT16_MAX + 1, "65536" }, + { UINT32_MAX, "4294967295" }, + { (int64_t)UINT32_MAX + 1, "4294967296" }, + { INT64_MAX - 8, "9223372036854775799" }, + { INT64_MIN + 9, "-9223372036854775799" }, + { INT64_MAX - 7, "9223372036854775800" }, + { INT64_MIN + 8, "-9223372036854775800" }, + { INT64_MAX - 1, "9223372036854775806" }, + { INT64_MIN + 1, "-9223372036854775807" }, + { INT64_MAX, "9223372036854775807" }, + { INT64_MIN, "-9223372036854775808" }, +}; + +static const struct { + uint64_t value; + const char *result; +} utoa16_cases[] = { + { 0x0, "0" }, { 0x1, "1" }, + { 0x2, "2" }, { 0x9, "9" }, + { 0xa, "a" }, { 0xb, "b" }, + { 0xc, "c" }, { 0xd, "d" }, + { 0xe, "e" }, { 0xf, "f" }, + { 0x10, "10" }, { 0x11, "11" }, + { 0x12, "12" }, { 0x19, "19" }, + { 0x1a, "1a" }, { 0x1b, "1b" }, + { 0x1c, "1c" }, { 0x1d, "1d" }, + { 0x1e, "1e" }, { 0x1f, "1f" }, + { 0x20, "20" }, { 0x21, "21" }, + { 0x22, "22" }, { 0x29, "29" }, + { 0x2a, "2a" }, { 0x2b, "2b" }, + { 0x2c, "2c" }, { 0x2d, "2d" }, + { 0x2e, "2e" }, { 0x2f, "2f" }, + { 0x90, "90" }, { 0x91, "91" }, + { 0x92, "92" }, { 0x99, "99" }, + { 0x9a, "9a" }, { 0x9b, "9b" }, + { 0x9c, "9c" }, { 0x9d, "9d" }, + { 0x9e, "9e" }, { 0x9f, "9f" }, + { 0xa0, "a0" }, { 0xa1, "a1" }, + { 0xa2, "a2" }, { 0xa9, "a9" }, + { 0xaa, "aa" }, { 0xab, "ab" }, + { 0xac, "ac" }, { 0xad, "ad" }, + { 0xae, "ae" }, { 0xaf, "af" }, + { 0xb0, "b0" }, { 0xb1, "b1" }, + { 0xb2, "b2" }, { 0xb9, "b9" }, + { 0xba, "ba" }, { 0xbb, "bb" }, + { 0xbc, "bc" }, { 0xbd, "bd" }, + { 0xbe, "be" }, { 0xbf, "bf" }, + { 0xc0, "c0" }, { 0xc1, "c1" }, + { 0xc2, "c2" }, { 0xc9, "c9" }, + { 0xca, "ca" }, { 0xcb, "cb" }, + { 0xcc, "cc" }, { 0xcd, "cd" }, + { 0xce, "ce" }, { 0xcf, "cf" }, + { 0xd0, "d0" }, { 0xd1, "d1" }, + { 0xd2, "d2" }, { 0xd9, "d9" }, + { 0xda, "da" }, { 0xdb, "db" }, + { 0xdc, "dc" }, { 0xdd, "dd" }, + { 0xde, "de" }, { 0xdf, "df" }, + { 0xe0, "e0" }, { 0xe1, "e1" }, + { 0xe2, "e2" }, { 0xe9, "e9" }, + { 0xea, "ea" }, { 0xeb, "eb" }, + { 0xec, "ec" }, { 0xed, "ed" }, + { 0xee, "ee" }, { 0xef, "ef" }, + { 0xf0, "f0" }, { 0xf1, "f1" }, + { 0xf2, "f2" }, { 0xf9, "f9" }, + { 0xfa, "fa" }, { 0xfb, "fb" }, + { 0xfc, "fc" }, { 0xfd, "fd" }, + { 0xfe, "fe" }, { 0xff, "ff" }, + { 0x100, "100" }, { 0x101, "101" }, + { 0x109, "109" }, { 0x10a, "10a" }, + { 0x10f, "10f" }, + { 0x110, "110" }, { 0x111, "111" }, + { 0x119, "119" }, { 0x11a, "11a" }, + { 0x11f, "11f" }, { 0x120, "120" }, + { 0x1ff, "1ff" }, { 0x200, "200" }, + { 0xfff, "fff" }, { 0x1000, "1000" }, + { UINT16_MAX, "ffff" }, + { UINT16_MAX + 1, "10000" }, + { UINT32_MAX, "ffffffff" }, + { (uint64_t)UINT32_MAX + 1, "100000000" }, + { UINT64_MAX - 6, "fffffffffffffff9" }, + { UINT64_MAX - 5, "fffffffffffffffa" }, + { UINT64_MAX - 1, "fffffffffffffffe" }, + { UINT64_MAX, "ffffffffffffffff" }, +}; + +static const struct { + int64_t value; + const char *result; +} itoa16_cases[] = { + { 0x0, "0" }, { 0x1, "1" }, + { 0x2, "2" }, { 0x9, "9" }, + { 0xa, "a" }, { 0xb, "b" }, + { 0xc, "c" }, { 0xd, "d" }, + { 0xe, "e" }, { 0xf, "f" }, + { 0x10, "10" }, { 0x11, "11" }, + { 0x12, "12" }, { 0x19, "19" }, + { 0x1a, "1a" }, { 0x1b, "1b" }, + { 0x1c, "1c" }, { 0x1d, "1d" }, + { 0x1e, "1e" }, { 0x1f, "1f" }, + { 0x20, "20" }, { 0x21, "21" }, + { 0x22, "22" }, { 0x29, "29" }, + { 0x2a, "2a" }, { 0x2b, "2b" }, + { 0x2c, "2c" }, { 0x2d, "2d" }, + { 0x2e, "2e" }, { 0x2f, "2f" }, + { 0x90, "90" }, { 0x91, "91" }, + { 0x92, "92" }, { 0x99, "99" }, + { 0x9a, "9a" }, { 0x9b, "9b" }, + { 0x9c, "9c" }, { 0x9d, "9d" }, + { 0x9e, "9e" }, { 0x9f, "9f" }, + { 0xa0, "a0" }, { 0xa1, "a1" }, + { 0xa2, "a2" }, { 0xa9, "a9" }, + { 0xaa, "aa" }, { 0xab, "ab" }, + { 0xac, "ac" }, { 0xad, "ad" }, + { 0xae, "ae" }, { 0xaf, "af" }, + { 0xb0, "b0" }, { 0xb1, "b1" }, + { 0xb2, "b2" }, { 0xb9, "b9" }, + { 0xba, "ba" }, { 0xbb, "bb" }, + { 0xbc, "bc" }, { 0xbd, "bd" }, + { 0xbe, "be" }, { 0xbf, "bf" }, + { 0xc0, "c0" }, { 0xc1, "c1" }, + { 0xc2, "c2" }, { 0xc9, "c9" }, + { 0xca, "ca" }, { 0xcb, "cb" }, + { 0xcc, "cc" }, { 0xcd, "cd" }, + { 0xce, "ce" }, { 0xcf, "cf" }, + { 0xd0, "d0" }, { 0xd1, "d1" }, + { 0xd2, "d2" }, { 0xd9, "d9" }, + { 0xda, "da" }, { 0xdb, "db" }, + { 0xdc, "dc" }, { 0xdd, "dd" }, + { 0xde, "de" }, { 0xdf, "df" }, + { 0xe0, "e0" }, { 0xe1, "e1" }, + { 0xe2, "e2" }, { 0xe9, "e9" }, + { 0xea, "ea" }, { 0xeb, "eb" }, + { 0xec, "ec" }, { 0xed, "ed" }, + { 0xee, "ee" }, { 0xef, "ef" }, + { 0xf0, "f0" }, { 0xf1, "f1" }, + { 0xf2, "f2" }, { 0xf9, "f9" }, + { 0xfa, "fa" }, { 0xfb, "fb" }, + { 0xfc, "fc" }, { 0xfd, "fd" }, + { 0xfe, "fe" }, { 0xff, "ff" }, + { 0x100, "100" }, { 0x101, "101" }, + { 0x109, "109" }, { 0x10a, "10a" }, + { 0x10f, "10f" }, + { 0x110, "110" }, { 0x111, "111" }, + { 0x119, "119" }, { 0x11a, "11a" }, + { 0x11f, "11f" }, { 0x120, "120" }, + { 0x1ff, "1ff" }, { 0x200, "200" }, + { 0xfff, "fff" }, { 0x1000, "1000" }, + { UINT16_MAX, "ffff" }, + { UINT16_MAX + 1, "10000" }, + { UINT32_MAX, "ffffffff" }, + { (uint64_t)UINT32_MAX + 1, "100000000" }, + { INT64_MIN + 7, "-7ffffffffffffff9" }, + { INT64_MAX - 6, "7ffffffffffffff9" }, + { INT64_MIN + 6, "-7ffffffffffffffa" }, + { INT64_MAX - 5, "7ffffffffffffffa" }, + { INT64_MIN + 2, "-7ffffffffffffffe" }, + { INT64_MAX - 1, "7ffffffffffffffe" }, + { INT64_MIN + 1, "-7fffffffffffffff" }, + { INT64_MAX, "7fffffffffffffff" }, + { INT64_MIN, "-8000000000000000" }, +}; + +static const char *str_end(const char *str) +{ + for (;; ++str) if (*str == '\0') return str; +} + +void test_main() +{ + { + char buffer[KERNAUX_UTOA_MIN_BUFFER_SIZE + 3]; + + for ( + size_t index = 0; + index < sizeof(utoa_cases) / sizeof(utoa_cases[0]); + ++index + ) { + const char *const end1 = kernaux_utoa( + utoa_cases[index].value, + buffer, + utoa_cases[index].base, + NULL + ); + assert(strcmp(buffer, utoa_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + + const char *const end2 = kernaux_utoa( + utoa_cases[index].value, + buffer, + utoa_cases[index].base, + "" + ); + assert(strcmp(buffer, utoa_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + + const char *const end3 = kernaux_utoa( + utoa_cases[index].value, + buffer, + utoa_cases[index].base, + "foo" + ); + assert(strncmp(buffer, "foo", 3) == 0); + assert(strcmp(&buffer[3], utoa_cases[index].result) == 0); + assert(end3 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_ITOA_MIN_BUFFER_SIZE + 3]; + + for ( + size_t index = 0; + index < sizeof(utoa_cases) / sizeof(utoa_cases[0]); + ++index + ) { + if (utoa_cases[index].value > (uint64_t)INT64_MAX) continue; + + const int64_t value = utoa_cases[index].value; + const int base = utoa_cases[index].base; + + const char *const end1 = kernaux_itoa(value, buffer, base, NULL); + assert(strcmp(buffer, utoa_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + + const char *const end2 = kernaux_itoa(value, buffer, base, ""); + assert(strcmp(buffer, utoa_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + + const char *const end3 = kernaux_itoa(value, buffer, base, "foo"); + assert(strncmp(buffer, "foo", 3) == 0); + assert(strcmp(&buffer[3], utoa_cases[index].result) == 0); + assert(end3 == str_end(buffer)); + + if (value <= 0 || base < 2 || base > 36) continue; + + const char *const end4 = kernaux_itoa(-value, buffer, base, NULL); + assert(buffer[0] == '-'); + assert(strcmp(&buffer[1], utoa_cases[index].result) == 0); + assert(end4 == str_end(buffer)); + + const char *const end5 = kernaux_itoa(-value, buffer, base, ""); + assert(buffer[0] == '-'); + assert(strcmp(&buffer[1], utoa_cases[index].result) == 0); + assert(end5 == str_end(buffer)); + + const char *const end6 = kernaux_itoa(-value, buffer, base, "foo"); + assert(strncmp(buffer, "-foo", 4) == 0); + assert(strcmp(&buffer[4], utoa_cases[index].result) == 0); + assert(end6 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_UTOA2_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(utoa2_cases) / sizeof(utoa2_cases[0]); + ++index + ) { + const char *const end1 = + kernaux_utoa2(utoa2_cases[index].value, buffer); + assert(strncmp(buffer, "0b", 2) == 0); + assert(strcmp(&buffer[2], utoa2_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_ITOA2_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(itoa2_cases) / sizeof(itoa2_cases[0]); + ++index + ) { + const int64_t value = itoa2_cases[index].value; + + const char *const end1 = kernaux_itoa2(value, buffer); + if (value >= 0) { + assert(strncmp(buffer, "0b", 2) == 0); + assert(strcmp(&buffer[2], itoa2_cases[index].result) == 0); + } else { + assert(strncmp(buffer, "-0b", 3) == 0); + assert(strcmp(&buffer[3], &itoa2_cases[index].result[1]) == 0); + } + assert(end1 == str_end(buffer)); + + if (value <= 0) continue; + + const char *const end2 = kernaux_itoa2(-value, buffer); + assert(strncmp(buffer, "-0b", 3) == 0); + assert(strcmp(&buffer[3], itoa2_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_UTOA8_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(utoa8_cases) / sizeof(utoa8_cases[0]); + ++index + ) { + const char *const end1 = + kernaux_utoa8(utoa8_cases[index].value, buffer); + assert(strncmp(buffer, "0o", 2) == 0); + assert(strcmp(&buffer[2], utoa8_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_ITOA8_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(itoa8_cases) / sizeof(itoa8_cases[0]); + ++index + ) { + const int64_t value = itoa8_cases[index].value; + + const char *const end1 = kernaux_itoa8(value, buffer); + if (value >= 0) { + assert(strncmp(buffer, "0o", 2) == 0); + assert(strcmp(&buffer[2], itoa8_cases[index].result) == 0); + } else { + assert(strncmp(buffer, "-0o", 3) == 0); + assert(strcmp(&buffer[3], &itoa8_cases[index].result[1]) == 0); + } + assert(end1 == str_end(buffer)); + + if (value <= 0) continue; + + const char *const end2 = kernaux_itoa8(-value, buffer); + assert(strncmp(buffer, "-0o", 3) == 0); + assert(strcmp(&buffer[3], itoa8_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_UTOA10_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(utoa10_cases) / sizeof(utoa10_cases[0]); + ++index + ) { + const char *const end1 = + kernaux_utoa10(utoa10_cases[index].value, buffer); + assert(strcmp(buffer, utoa10_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_ITOA10_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(itoa10_cases) / sizeof(itoa10_cases[0]); + ++index + ) { + const int64_t value = itoa10_cases[index].value; + + const char *const end1 = kernaux_itoa10(value, buffer); + assert(strcmp(buffer, itoa10_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + + if (value <= 0) continue; + + const char *const end2 = kernaux_itoa10(-value, buffer); + assert(buffer[0] == '-'); + assert(strcmp(&buffer[1], itoa10_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_UTOA16_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(utoa16_cases) / sizeof(utoa16_cases[0]); + ++index + ) { + const char *const end1 = + kernaux_utoa16(utoa16_cases[index].value, buffer); + assert(strncmp(buffer, "0x", 2) == 0); + assert(strcmp(&buffer[2], utoa16_cases[index].result) == 0); + assert(end1 == str_end(buffer)); + } + } + + { + char buffer[KERNAUX_ITOA16_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(itoa16_cases) / sizeof(itoa16_cases[0]); + ++index + ) { + const int64_t value = itoa16_cases[index].value; + + const char *const end1 = kernaux_itoa16(value, buffer); + if (value >= 0) { + assert(strncmp(buffer, "0x", 2) == 0); + assert(strcmp(&buffer[2], itoa16_cases[index].result) == 0); + } else { + assert(strncmp(buffer, "-0x", 3) == 0); + assert(strcmp(&buffer[3], &itoa16_cases[index].result[1]) == 0); + } + assert(end1 == str_end(buffer)); + + if (value <= 0) continue; + + const char *const end2 = kernaux_itoa16(-value, buffer); + assert(strncmp(buffer, "-0x", 3) == 0); + assert(strcmp(&buffer[3], itoa16_cases[index].result) == 0); + assert(end2 == str_end(buffer)); + } + } +} diff --git a/tests/test_ntoa_assert.c b/tests/test_ntoa_assert.c new file mode 100644 index 0000000..433e5bb --- /dev/null +++ b/tests/test_ntoa_assert.c @@ -0,0 +1,154 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#define VALID_LONG_PREFIX "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +#define TOO_LONG_PREFIX "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +static jmp_buf jmpbuf; + +static unsigned int assert_count_exp = 0; +static unsigned int assert_count_ctr = 0; + +static const char *assert_last_file = NULL; +static int assert_last_line = 0; +static const char *assert_last_msg = NULL; + +static void assert_cb( + const char *const file, + const int line, + const char *const msg +) { + ++assert_count_ctr; + + assert_last_file = file; + assert_last_line = line; + assert_last_msg = msg; + + longjmp(jmpbuf, 1); +} + +static void test_utoa_assert(char *const buffer, const int base) +{ + if (setjmp(jmpbuf) == 0) { + kernaux_utoa(0, buffer, base, NULL); + } else { + // cppcheck-suppress assignmentInAssert + assert(assert_count_ctr == ++assert_count_exp); + assert(strstr(assert_last_file, "src/ntoa.c") != NULL); + assert(assert_last_line != 0); + assert(assert_last_msg != NULL); + + assert_last_file = NULL; + assert_last_line = 0; + assert_last_msg = NULL; + } +} + +static void test_itoa_assert(char *const buffer, const int base) +{ + if (setjmp(jmpbuf) == 0) { + kernaux_itoa(0, buffer, base, NULL); + } else { + // cppcheck-suppress assignmentInAssert + assert(assert_count_ctr == ++assert_count_exp); + assert(strstr(assert_last_file, "src/ntoa.c") != NULL); + assert(assert_last_line != 0); + assert(assert_last_msg != NULL); + + assert_last_file = NULL; + assert_last_line = 0; + assert_last_msg = NULL; + } +} + +static const char *str_end(const char *str) +{ + for (;; ++str) if (*str == '\0') return str; +} + +void test_main() +{ + kernaux_assert_cb = assert_cb; + + if (setjmp(jmpbuf) != 0) abort(); + + { + char buffer[KERNAUX_UTOA_MIN_BUFFER_SIZE + KERNAUX_NTOA_MAX_PREFIX_LEN]; + + const char *const end1 = + kernaux_utoa(123, buffer, 'd', VALID_LONG_PREFIX); + assert(strcmp(buffer, VALID_LONG_PREFIX"123") == 0); + assert(end1 == str_end(buffer)); + assert(assert_count_ctr == assert_count_exp); + assert(assert_last_file == NULL); + assert(assert_last_line == 0); + assert(assert_last_msg == NULL); + + if (setjmp(jmpbuf) == 0) { + kernaux_utoa(123, buffer, 'd', TOO_LONG_PREFIX); + } else { + assert(strcmp(buffer, VALID_LONG_PREFIX) == 0); + // cppcheck-suppress assignmentInAssert + assert(assert_count_ctr == ++assert_count_exp); + assert(strstr(assert_last_file, "src/ntoa.c") != NULL); + assert(assert_last_line != 0); + assert(strcmp(assert_last_msg, "prefix is too long") == 0); + assert_last_file = NULL; + assert_last_line = 0; + assert_last_msg = NULL; + } + + test_utoa_assert(NULL, 'd'); + test_utoa_assert(buffer, 0); + test_utoa_assert(buffer, 1); + test_utoa_assert(buffer, -1); + test_utoa_assert(buffer, 37); + test_utoa_assert(buffer, -37); + } + + if (setjmp(jmpbuf) != 0) abort(); + + { + char buffer[KERNAUX_ITOA_MIN_BUFFER_SIZE + KERNAUX_NTOA_MAX_PREFIX_LEN]; + + const char *const end1 = + kernaux_itoa(123, buffer, 'd', VALID_LONG_PREFIX); + assert(strcmp(buffer, VALID_LONG_PREFIX"123") == 0); + assert(end1 == str_end(buffer)); + assert(assert_count_ctr == assert_count_exp); + assert(assert_last_file == NULL); + assert(assert_last_line == 0); + assert(assert_last_msg == NULL); + + if (setjmp(jmpbuf) == 0) { + kernaux_itoa(123, buffer, 'd', TOO_LONG_PREFIX); + } else { + assert(strcmp(buffer, VALID_LONG_PREFIX) == 0); + // cppcheck-suppress assignmentInAssert + assert(assert_count_ctr == ++assert_count_exp); + assert(strstr(assert_last_file, "src/ntoa.c") != NULL); + assert(assert_last_line != 0); + assert(strcmp(assert_last_msg, "prefix is too long") == 0); + assert_last_file = NULL; + assert_last_line = 0; + assert_last_msg = NULL; + } + + test_itoa_assert(NULL, 'd'); + test_itoa_assert(buffer, 0); + test_itoa_assert(buffer, 1); + test_itoa_assert(buffer, -1); + test_itoa_assert(buffer, 37); + test_itoa_assert(buffer, -37); + } +} diff --git a/tests/test_pfa.c b/tests/test_pfa.c new file mode 100644 index 0000000..70a3620 --- /dev/null +++ b/tests/test_pfa.c @@ -0,0 +1,86 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +void test_main() +{ + struct KernAux_PFA pfa; + + KernAux_PFA_initialize(&pfa); + + for (size_t index = 0; index < KERNAUX_PFA_PAGES_COUNT_MAX; ++index) { + assert(!KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + KernAux_PFA_mark_available(&pfa, 0, 654335); + KernAux_PFA_mark_available(&pfa, 1048576, 134086655); + KernAux_PFA_mark_unavailable(&pfa, 4194304, 6291455); // [4 MB, 6 MB) + + for (size_t index = 0; index < 159; ++index) { + assert(KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + for (size_t index = 159; index < 256; ++index) { + assert(!KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + for (size_t index = 256; index < 1024; ++index) { // [1 MB, 4 MB) + assert(KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + for (size_t index = 1024; index < 1536; ++index) { // [4 MB, 6 MB) + assert(!KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + for (size_t index = 1536; index < 32736; ++index) { // [6 MB, ~127 MB) + assert(KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + for (size_t index = 32736; index < KERNAUX_PFA_PAGES_COUNT_MAX; ++index) { + assert(!KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)); + } + + { + const size_t page_addr = KernAux_PFA_alloc_pages(&pfa, 1); + + assert(page_addr != 0); + assert(page_addr % KERNAUX_PFA_PAGE_SIZE == 0); + assert(!KernAux_PFA_is_available(&pfa, page_addr)); + + KernAux_PFA_free_pages(&pfa, page_addr, 1); + + assert(KernAux_PFA_is_available(&pfa, page_addr)); + } + + { + const size_t page_addr = + KernAux_PFA_alloc_pages(&pfa, 10 * KERNAUX_PFA_PAGE_SIZE); + + assert(page_addr != 0); + assert(page_addr % KERNAUX_PFA_PAGE_SIZE == 0); + + for (size_t index = 0, addr = page_addr; index < 10; ++index) { + assert(!KernAux_PFA_is_available(&pfa, addr)); + addr += KERNAUX_PFA_PAGE_SIZE; + } + + KernAux_PFA_free_pages(&pfa, page_addr, 10 * KERNAUX_PFA_PAGE_SIZE); + + for (size_t index = 0, addr = page_addr; index < 10; ++index) { + assert(KernAux_PFA_is_available(&pfa, addr)); + addr += KERNAUX_PFA_PAGE_SIZE; + } + } + + for (size_t index = 0; index < KERNAUX_PFA_PAGES_COUNT_MAX; ++index) { + if (KernAux_PFA_is_available(&pfa, index * KERNAUX_PFA_PAGE_SIZE)) { + assert(KernAux_PFA_alloc_pages(&pfa, 1) != 0); + } + } + + assert(KernAux_PFA_alloc_pages(&pfa, 1) == 0); +} diff --git a/tests/test_pfa_assert.c b/tests/test_pfa_assert.c new file mode 100644 index 0000000..78ce5ac --- /dev/null +++ b/tests/test_pfa_assert.c @@ -0,0 +1,99 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +static unsigned int count = 0; +static jmp_buf jmpbuf; + +static void assert_cb( + const char *const file KERNAUX_UNUSED, + const int line KERNAUX_UNUSED, + const char *const str KERNAUX_UNUSED +) { + ++count; + longjmp(jmpbuf, 1); +} + +void test_main() +{ + if (setjmp(jmpbuf) != 0) abort(); + + kernaux_assert_cb = assert_cb; + + struct KernAux_PFA pfa; + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_initialize(&pfa); + } else { + assert(count == 0); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_initialize(NULL); + } else { + assert(count == 1); + } + + if (setjmp(jmpbuf) == 0) { + assert(!KernAux_PFA_is_available(NULL, KERNAUX_PFA_PAGE_SIZE)); + } else { + assert(count == 2); + } + + if (setjmp(jmpbuf) == 0) { + assert(!KernAux_PFA_is_available(&pfa, 123)); + } else { + assert(count == 3); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_mark_available(NULL, 0, KERNAUX_PFA_PAGE_SIZE); + } else { + assert(count == 4); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_mark_available(&pfa, KERNAUX_PFA_PAGE_SIZE, 0); + } else { + assert(count == 5); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_mark_unavailable(NULL, 0, KERNAUX_PFA_PAGE_SIZE); + } else { + assert(count == 6); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_mark_unavailable(&pfa, KERNAUX_PFA_PAGE_SIZE, 0); + } else { + assert(count == 7); + } + + if (setjmp(jmpbuf) == 0) { + assert(KernAux_PFA_alloc_pages(NULL, KERNAUX_PFA_PAGE_SIZE) == 0); + } else { + assert(count == 8); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_free_pages(NULL, KERNAUX_PFA_PAGE_SIZE, KERNAUX_PFA_PAGE_SIZE); + } else { + assert(count == 9); + } + + if (setjmp(jmpbuf) == 0) { + KernAux_PFA_free_pages(&pfa, 123, KERNAUX_PFA_PAGE_SIZE); + } else { + assert(count == 10); + } +} diff --git a/tests/test_printf.c b/tests/test_printf.c new file mode 100644 index 0000000..75f8ec5 --- /dev/null +++ b/tests/test_printf.c @@ -0,0 +1,34 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +void test_main() +{ + char buffer[1000]; + + // Sanity check + { + memset(buffer, 0xff, sizeof(buffer)); + const int result = + kernaux_snprintf(buffer, sizeof(buffer), "%s", "Hello, World!"); + fprintf(stderr, "%d:%s\n", result, buffer); + assert(result == 13); + assert(strcmp(buffer, "Hello, World!") == 0); + } + + // i386 requires "(long long)0" instead of just "0" + { + memset(buffer, 0xff, sizeof(buffer)); + const int result = + kernaux_snprintf(buffer, sizeof(buffer), "%#.0llx", (long long)0); + fprintf(stderr, "%d:%s\n", result, buffer); + assert(result == 0); + assert(strcmp(buffer, "") == 0); + } +} diff --git a/tests/test_units_human.c b/tests/test_units_human.c new file mode 100644 index 0000000..0dc4488 --- /dev/null +++ b/tests/test_units_human.c @@ -0,0 +1,169 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include + +static void test_raw( + unsigned long long value, + enum KernAux_Unit unit, + const char *result +); +static void test_dec( + unsigned long long value, + enum KernAux_Unit unit, + enum KernAux_UnitPrefixDec prefix, + const char *result +); +static void test_bin( + unsigned long long value, + enum KernAux_Unit unit, + enum KernAux_UnitPrefixBin prefix, + const char *result +); + +void test_main() +{ + test_raw(0, KERNAUX_UNIT_BIT, "0 bit"); + test_raw(0, KERNAUX_UNIT_BYTE, "0 Byte"); + test_raw(1, KERNAUX_UNIT_BIT, "1 bit"); + test_raw(1, KERNAUX_UNIT_BYTE, "1 Byte"); + test_raw(2, KERNAUX_UNIT_BIT, "2 bit"); + test_raw(2, KERNAUX_UNIT_BYTE, "2 Byte"); + test_raw(10, KERNAUX_UNIT_BIT, "10 bit"); + test_raw(10, KERNAUX_UNIT_BYTE, "10 Byte"); + test_raw(UINT8_MAX, KERNAUX_UNIT_BIT, "255 bit"); + test_raw(UINT8_MAX, KERNAUX_UNIT_BYTE, "255 Byte"); + test_raw(UINT16_MAX, KERNAUX_UNIT_BIT, "65535 bit"); + test_raw(UINT16_MAX, KERNAUX_UNIT_BYTE, "65535 Byte"); + test_raw(UINT32_MAX, KERNAUX_UNIT_BIT, "4294967295 bit"); + test_raw(UINT32_MAX, KERNAUX_UNIT_BYTE, "4294967295 Byte"); + test_raw(UINT64_MAX, KERNAUX_UNIT_BIT, "18446744073709551615 bit"); + test_raw(UINT64_MAX, KERNAUX_UNIT_BYTE, "18446744073709551615 Byte"); + + test_dec(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "0 kbit"); + test_dec(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "0 kB"); + test_dec(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "1 kbit"); + test_dec(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "1 kB"); + test_dec(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "2 kbit"); + test_dec(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "2 kB"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "255 kbit"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "255 kB"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "65535 kbit"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "65535 kB"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "4294967295 kbit"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "4294967295 kB"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KILO, "18446744073709551615 kbit"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KILO, "18446744073709551615 kB"); + + test_dec(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "0 Mbit"); + test_dec(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "0 MB"); + test_dec(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "1 Mbit"); + test_dec(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "1 MB"); + test_dec(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "2 Mbit"); + test_dec(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "2 MB"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "255 Mbit"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "255 MB"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "65535 Mbit"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "65535 MB"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "4294967295 Mbit"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "4294967295 MB"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEGA, "18446744073709551615 Mbit"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEGA, "18446744073709551615 MB"); + + test_dec(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "0 Gbit"); + test_dec(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "0 GB"); + test_dec(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "1 Gbit"); + test_dec(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "1 GB"); + test_dec(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "2 Gbit"); + test_dec(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "2 GB"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "255 Gbit"); + test_dec(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "255 GB"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "65535 Gbit"); + test_dec(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "65535 GB"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "4294967295 Gbit"); + test_dec(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "4294967295 GB"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIGA, "18446744073709551615 Gbit"); + test_dec(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIGA, "18446744073709551615 GB"); + + test_bin(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "0 Kibit"); + test_bin(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "0 KiB"); + test_bin(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "1 Kibit"); + test_bin(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "1 KiB"); + test_bin(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "2 Kibit"); + test_bin(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "2 KiB"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "255 Kibit"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "255 KiB"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "65535 Kibit"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "65535 KiB"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "4294967295 Kibit"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "4294967295 KiB"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_KIBI, "18446744073709551615 Kibit"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_KIBI, "18446744073709551615 KiB"); + + test_bin(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "0 Mibit"); + test_bin(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "0 MiB"); + test_bin(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "1 Mibit"); + test_bin(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "1 MiB"); + test_bin(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "2 Mibit"); + test_bin(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "2 MiB"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "255 Mibit"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "255 MiB"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "65535 Mibit"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "65535 MiB"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "4294967295 Mibit"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "4294967295 MiB"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_MEBI, "18446744073709551615 Mibit"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_MEBI, "18446744073709551615 MiB"); + + test_bin(0, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "0 Gibit"); + test_bin(0, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "0 GiB"); + test_bin(1, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "1 Gibit"); + test_bin(1, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "1 GiB"); + test_bin(2, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "2 Gibit"); + test_bin(2, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "2 GiB"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "255 Gibit"); + test_bin(UINT8_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "255 GiB"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "65535 Gibit"); + test_bin(UINT16_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "65535 GiB"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "4294967295 Gibit"); + test_bin(UINT32_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "4294967295 GiB"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BIT, KERNAUX_UNITPFX_GIBI, "18446744073709551615 Gibit"); + test_bin(UINT64_MAX, KERNAUX_UNIT_BYTE, KERNAUX_UNITPFX_GIBI, "18446744073709551615 GiB"); +} + +void test_raw( + const unsigned long long value, + const enum KernAux_Unit unit, + const char *const result +) { + char buffer[64]; + assert(kernaux_units_human_raw(value, unit, buffer, sizeof(buffer))); + assert(strcmp(buffer, result) == 0); +} + +void test_dec( + const unsigned long long value, + const enum KernAux_Unit unit, + const enum KernAux_UnitPrefixDec prefix, + const char *const result +) { + char buffer[64]; + assert(kernaux_units_human_dec(value, unit, prefix, buffer, sizeof(buffer))); + assert(strcmp(buffer, result) == 0); +} + +void test_bin( + const unsigned long long value, + const enum KernAux_Unit unit, + const enum KernAux_UnitPrefixBin prefix, + const char *const result +) { + char buffer[64]; + assert(kernaux_units_human_bin(value, unit, prefix, buffer, sizeof(buffer))); + assert(strcmp(buffer, result) == 0); +}