From 92ecb5f7ebcd961a46bc17dd5724ba180deabdee Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Mon, 24 Jan 2022 02:40:13 +0500 Subject: [PATCH] Add funcs "kernaux_utoa16" and "kernaux_itoa16" --- ChangeLog | 1 + include/kernaux/ntoa.h | 9 + pkgs/ruby/ext/default/extconf.rb | 2 + pkgs/ruby/ext/default/ntoa.c | 39 ++++ pkgs/ruby/spec/lib/kernaux/itoa16_spec.rb | 63 +++++++ pkgs/ruby/spec/lib/kernaux/utoa16_spec.rb | 45 +++++ pkgs/rust/src/lib.rs | 28 +++ src/ntoa.c | 31 +++ tests/test_ntoa.c | 220 ++++++++++++++++++++-- 9 files changed, 423 insertions(+), 15 deletions(-) create mode 100644 pkgs/ruby/spec/lib/kernaux/itoa16_spec.rb create mode 100644 pkgs/ruby/spec/lib/kernaux/utoa16_spec.rb diff --git a/ChangeLog b/ChangeLog index 36980d8..bbae576 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ * include/kernaux/libc.h: Add funcs "atoi", "isdigit", "isspace" * include/kernaux/ntoa.h: Rename const "KERNAUX_ITOA_BUFFER_SIZE" to "KERNAUX_ITOA10_BUFFER_SIZE" + * include/kernaux/ntoa.h: Add funcs "kernaux_utoa16" and "kernaux_itoa16" 2022-01-22 Alex Kotov diff --git a/include/kernaux/ntoa.h b/include/kernaux/ntoa.h index 665946f..a0e29fc 100644 --- a/include/kernaux/ntoa.h +++ b/include/kernaux/ntoa.h @@ -8,12 +8,21 @@ extern "C" { #include // uint64_t: "18446744073709551615" +// int64_t: "9223372036854775807" // int64_t: "-9223372036854775808" #define KERNAUX_ITOA10_BUFFER_SIZE 21 +// uint64_t: "ffffffffffffffff" +// int64_t: "7fffffffffffffff" +// int64_t: "-8000000000000000" +#define KERNAUX_ITOA16_BUFFER_SIZE 18 + void kernaux_utoa10(uint64_t value, char *buffer); void kernaux_itoa10(int64_t value, char *buffer); +void kernaux_utoa16(uint64_t value, char *buffer); +void kernaux_itoa16(int64_t value, char *buffer); + #ifdef __cplusplus } #endif diff --git a/pkgs/ruby/ext/default/extconf.rb b/pkgs/ruby/ext/default/extconf.rb index 78773b8..78d2a1e 100644 --- a/pkgs/ruby/ext/default/extconf.rb +++ b/pkgs/ruby/ext/default/extconf.rb @@ -13,6 +13,8 @@ end have_func 'kernaux_utoa10' have_func 'kernaux_itoa10' +have_func 'kernaux_utoa16' +have_func 'kernaux_itoa16' have_func 'kernaux_snprintf' diff --git a/pkgs/ruby/ext/default/ntoa.c b/pkgs/ruby/ext/default/ntoa.c index 4a808a5..cc84446 100644 --- a/pkgs/ruby/ext/default/ntoa.c +++ b/pkgs/ruby/ext/default/ntoa.c @@ -7,6 +7,12 @@ static VALUE rb_KernAux_utoa10(VALUE self, VALUE number); #ifdef HAVE_KERNAUX_ITOA10 static VALUE rb_KernAux_itoa10(VALUE self, VALUE number); #endif +#ifdef HAVE_KERNAUX_UTOA16 +static VALUE rb_KernAux_utoa16(VALUE self, VALUE number); +#endif +#ifdef HAVE_KERNAUX_ITOA16 +static VALUE rb_KernAux_itoa16(VALUE self, VALUE number); +#endif static ID rb_intern_LESS = Qnil; static ID rb_intern_freeze = Qnil; @@ -24,6 +30,12 @@ void init_ntoa() #ifdef HAVE_KERNAUX_ITOA10 rb_define_singleton_method(rb_KernAux, "itoa10", rb_KernAux_itoa10, 1); #endif +#ifdef HAVE_KERNAUX_UTOA16 + rb_define_singleton_method(rb_KernAux, "utoa16", rb_KernAux_utoa16, 1); +#endif +#ifdef HAVE_KERNAUX_ITOA16 + rb_define_singleton_method(rb_KernAux, "itoa16", rb_KernAux_itoa16, 1); +#endif } #ifdef HAVE_KERNAUX_UTOA10 @@ -52,3 +64,30 @@ VALUE rb_KernAux_itoa10( return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0); } #endif + +#ifdef HAVE_KERNAUX_UTOA16 +VALUE rb_KernAux_utoa16( + const VALUE self_rb __attribute__((unused)), + const VALUE number_rb +) { + RB_INTEGER_TYPE_P(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_ITOA16_BUFFER_SIZE]; + kernaux_utoa16(NUM2ULL(number_rb), buffer); + return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0); +} +#endif + +#ifdef HAVE_KERNAUX_ITOA16 +VALUE rb_KernAux_itoa16( + const VALUE self_rb __attribute__((unused)), + const VALUE number_rb +) { + RB_INTEGER_TYPE_P(number_rb); + char buffer[KERNAUX_ITOA16_BUFFER_SIZE]; + kernaux_itoa16(NUM2LL(number_rb), buffer); + return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0); +} +#endif diff --git a/pkgs/ruby/spec/lib/kernaux/itoa16_spec.rb b/pkgs/ruby/spec/lib/kernaux/itoa16_spec.rb new file mode 100644 index 0000000..215bb98 --- /dev/null +++ b/pkgs/ruby/spec/lib/kernaux/itoa16_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe KernAux, '.itoa16' do + if described_class.singleton_class.method_defined? :itoa16 + subject(:itoa16) { described_class.itoa16 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 16 } + + 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 16 } + end + + context 'when number is max int64_t' do + let(:number) { 2**63 - 1 } + + it { is_expected.to eq 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 + end +end diff --git a/pkgs/ruby/spec/lib/kernaux/utoa16_spec.rb b/pkgs/ruby/spec/lib/kernaux/utoa16_spec.rb new file mode 100644 index 0000000..acc3fd4 --- /dev/null +++ b/pkgs/ruby/spec/lib/kernaux/utoa16_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe KernAux, '.utoa16' do + if described_class.singleton_class.method_defined? :utoa16 + 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 number.to_s 16 } + + 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 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 + end +end diff --git a/pkgs/rust/src/lib.rs b/pkgs/rust/src/lib.rs index 42c00f0..10e28d7 100644 --- a/pkgs/rust/src/lib.rs +++ b/pkgs/rust/src/lib.rs @@ -7,6 +7,10 @@ extern "C" { fn kernaux_utoa10(value: u64, buffer: *mut c_char); #[cfg(test)] fn kernaux_itoa10(value: i64, buffer: *mut c_char); + #[cfg(test)] + fn kernaux_utoa16(value: u64, buffer: *mut c_char); + #[cfg(test)] + fn kernaux_itoa16(value: i64, buffer: *mut c_char); } #[cfg(test)] @@ -38,4 +42,28 @@ mod tests { unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); assert_eq!(result, "-123"); } + + #[test] + fn utoa16() { + let mut buffer: [i8; 1000] = [0; 1000]; + unsafe { kernaux_utoa16(0x123, buffer.as_mut_ptr()) }; + let result = + unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + assert_eq!(result, "123"); + } + + #[test] + fn itoa16() { + let mut buffer: [i8; 1000] = [0; 1000]; + unsafe { kernaux_itoa16(0x123, buffer.as_mut_ptr()) }; + let result = + unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + assert_eq!(result, "123"); + + let mut buffer: [i8; 1000] = [0; 1000]; + unsafe { kernaux_itoa16(-0x123, buffer.as_mut_ptr()) }; + let result = + unsafe { CStr::from_ptr(buffer.as_ptr()) }.to_str().unwrap(); + assert_eq!(result, "-123"); + } } diff --git a/src/ntoa.c b/src/ntoa.c index b615506..b43da50 100644 --- a/src/ntoa.c +++ b/src/ntoa.c @@ -33,3 +33,34 @@ void kernaux_itoa10(int64_t value, char *buffer) kernaux_utoa10(-value, buffer); } } + +void kernaux_utoa16(uint64_t value, char *buffer) +{ + char *pos = buffer; + + if (value == 0) *(pos++) = '0'; + + while (value > 0) { + char mod = value % 16; + *(pos++) = mod < 10 ? mod + '0' : mod - 10 + 'a'; + value = value / 16; + } + + *(pos--) = '\0'; + + while (buffer < pos) { + const char tmp = *buffer; + *(buffer++) = *pos; + *(pos--) = tmp; + } +} + +void kernaux_itoa16(int64_t value, char *buffer) +{ + if (value >= 0) { + kernaux_utoa16(value, buffer); + } else { + *(buffer++) = '-'; + kernaux_utoa16(-value, buffer); + } +} diff --git a/tests/test_ntoa.c b/tests/test_ntoa.c index 4ad9c06..d45cd5b 100644 --- a/tests/test_ntoa.c +++ b/tests/test_ntoa.c @@ -5,6 +5,7 @@ #include #include +#include #include static const struct { @@ -94,26 +95,215 @@ static const struct { { 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" }, +}; + int main() { - char buffer[KERNAUX_ITOA10_BUFFER_SIZE]; + { + char buffer[KERNAUX_ITOA10_BUFFER_SIZE]; - for ( - size_t index = 0; - index < sizeof(utoa10_cases) / sizeof(utoa10_cases[0]); - ++index - ) { - kernaux_utoa10(utoa10_cases[index].value, buffer); - assert(strcmp(buffer, utoa10_cases[index].result) == 0); + for ( + size_t index = 0; + index < sizeof(utoa10_cases) / sizeof(utoa10_cases[0]); + ++index + ) { + kernaux_utoa10(utoa10_cases[index].value, buffer); + assert(strcmp(buffer, utoa10_cases[index].result) == 0); + } + + for ( + size_t index = 0; + index < sizeof(itoa10_cases) / sizeof(itoa10_cases[0]); + ++index + ) { + const int64_t value = itoa10_cases[index].value; + + kernaux_itoa10(value, buffer); + assert(strcmp(buffer, itoa10_cases[index].result) == 0); + + if (value <= 0) continue; + + kernaux_itoa10(-value, buffer); + assert(buffer[0] == '-'); + assert(strcmp(&buffer[1], itoa10_cases[index].result) == 0); + } } - for ( - size_t index = 0; - index < sizeof(itoa10_cases) / sizeof(itoa10_cases[0]); - ++index - ) { - kernaux_itoa10(itoa10_cases[index].value, buffer); - assert(strcmp(buffer, itoa10_cases[index].result) == 0); + { + char buffer[KERNAUX_ITOA16_BUFFER_SIZE]; + + for ( + size_t index = 0; + index < sizeof(utoa16_cases) / sizeof(utoa16_cases[0]); + ++index + ) { + kernaux_utoa16(utoa16_cases[index].value, buffer); + assert(strcmp(buffer, utoa16_cases[index].result) == 0); + } + + for ( + size_t index = 0; + index < sizeof(itoa16_cases) / sizeof(itoa16_cases[0]); + ++index + ) { + const int64_t value = itoa16_cases[index].value; + + kernaux_itoa16(value, buffer); + assert(strcmp(buffer, itoa16_cases[index].result) == 0); + + if (value <= 0) continue; + + kernaux_itoa16(-value, buffer); + assert(buffer[0] == '-'); + assert(strcmp(&buffer[1], itoa16_cases[index].result) == 0); + } } return 0;