Main: include/kernaux/ntoa.h: Add octal functions "kernaux_[u|i]toa8"

This commit is contained in:
Alex Kotov 2022-05-30 22:33:53 +03:00
parent 1d7e6e782b
commit 673f7c4b37
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
14 changed files with 471 additions and 5 deletions

View File

@ -7,7 +7,8 @@
"KERNAUX_ITOA_MIN_BUFFER_SIZE"
* include/kernaux/ntoa.h: Functions "kernaux_[u|i]toa[10|16]" return the end
of a buffer
* include/kernaux/ntoa.h: functions "kernaux_[u|i]toa16" put default prefix
* include/kernaux/ntoa.h: Functions "kernaux_[u|i]toa16" put default prefix
* include/kernaux/ntoa.h: Add octal functions "kernaux_[u|i]toa8"
2022-05-28 Alex Kotov <kotovalexarian@gmail.com>

View File

@ -7,6 +7,7 @@ extern "C" {
#include <stdint.h>
#define KERNAUX_NTOA_DEFAULT_PREFIX_8 "0o"
#define KERNAUX_NTOA_DEFAULT_PREFIX_16 "0x"
// "1111111111111111111111111111111111111111111111111111111111111111"
@ -16,6 +17,13 @@ extern "C" {
// "-1000000000000000000000000000000000000000000000000000000000000000"
#define KERNAUX_ITOA_MIN_BUFFER_SIZE (65 + 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)
@ -33,6 +41,9 @@ extern "C" {
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_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);

View File

@ -14,6 +14,9 @@
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_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);
@ -38,6 +41,11 @@ void init_ntoa(mrb_state *const mrb)
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, "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",
@ -115,6 +123,41 @@ mrb_value rb_KernAux_itoa(mrb_state *mrb, mrb_value self)
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;

View File

@ -24,6 +24,14 @@ def test_itoax(number, base, prefix, expected)
common_assert expected, KernAux.itoa(number, base, prefix)
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
@ -195,6 +203,27 @@ assert 'KernAux.itoa' do
end
end
assert 'KernAux.utoa8' do
test_utoa8 0, '0o0'
test_utoa8 01, '0o1'
test_utoa8 0123, '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
end
assert 'KernAux.itoa8' do
test_itoa8 0, '0o0'
test_itoa8 01, '0o1'
test_itoa8(-01, '-0o1')
test_itoa8 0123, '0o123'
test_itoa8(-0123, '-0o123')
test_itoa8 2**31 - 1, "0o#{(2**31 - 1).to_s(8)}"
test_itoa8(-2**31, "-0o#{(2**31).to_s(8)}")
end
assert 'KernAux.utoa10' do
test_utoa10 0, '0'
test_utoa10 1, '1'

View File

@ -13,6 +13,8 @@ end
have_func 'kernaux_utoa'
have_func 'kernaux_itoa'
have_func 'kernaux_utoa8'
have_func 'kernaux_itoa8'
have_func 'kernaux_utoa10'
have_func 'kernaux_itoa10'
have_func 'kernaux_utoa16'

View File

@ -9,6 +9,12 @@ static VALUE rb_KernAux_utoa(int argc, const VALUE *argv, VALUE self);
#ifdef HAVE_KERNAUX_ITOA
static VALUE rb_KernAux_itoa(int argc, const VALUE *argv, VALUE self);
#endif
#ifdef HAVE_KERNAUX_UTOA8
static VALUE rb_KernAux_utoa8(VALUE self, VALUE number);
#endif
#ifdef HAVE_KERNAUX_ITOA8
static VALUE rb_KernAux_itoa8(VALUE self, VALUE number);
#endif
#ifdef HAVE_KERNAUX_UTOA10
static VALUE rb_KernAux_utoa10(VALUE self, VALUE number);
#endif
@ -75,6 +81,12 @@ void init_ntoa()
#ifdef HAVE_KERNAUX_ITOA
rb_define_singleton_method(rb_KernAux, "itoa", rb_KernAux_itoa, -1);
#endif
#ifdef HAVE_KERNAUX_UTOA8
rb_define_singleton_method(rb_KernAux, "utoa8", rb_KernAux_utoa8, 1);
#endif
#ifdef HAVE_KERNAUX_ITOA8
rb_define_singleton_method(rb_KernAux, "itoa8", rb_KernAux_itoa8, 1);
#endif
#ifdef HAVE_KERNAUX_UTOA10
rb_define_singleton_method(rb_KernAux, "utoa10", rb_KernAux_utoa10, 1);
#endif
@ -168,6 +180,33 @@ VALUE rb_KernAux_itoa(const int argc, const VALUE *argv, const VALUE self)
}
#endif
#ifdef HAVE_KERNAUX_UTOA8
VALUE rb_KernAux_utoa8(
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_UTOA8_BUFFER_SIZE];
kernaux_utoa8(NUM2ULL(number_rb), buffer);
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
}
#endif
#ifdef HAVE_KERNAUX_ITOA8
VALUE rb_KernAux_itoa8(
const VALUE self_rb __attribute__((unused)),
const VALUE number_rb
) {
RB_INTEGER_TYPE_P(number_rb);
char buffer[KERNAUX_ITOA8_BUFFER_SIZE];
kernaux_itoa8(NUM2LL(number_rb), buffer);
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
}
#endif
#ifdef HAVE_KERNAUX_UTOA10
VALUE rb_KernAux_utoa10(
const VALUE self_rb __attribute__((unused)),

View File

@ -172,6 +172,26 @@ module KernAux
# @see .utoa Convert unsigned integers
##
##
# @!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.

View File

@ -0,0 +1,65 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe KernAux, '.itoa8' do
if described_class.singleton_class.method_defined? :itoa8
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
end
end

View File

@ -0,0 +1,45 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe KernAux, '.utoa8' do
if described_class.singleton_class.method_defined? :utoa8
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
end
end

View File

@ -3,6 +3,8 @@ use libc::{c_char, c_int};
pub const UTOA_MIN_BUFFER_SIZE: usize = 64 + 1;
pub const ITOA_MIN_BUFFER_SIZE: usize = 65 + 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;
@ -25,6 +27,10 @@ extern "C" {
prefix: *const 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"]
@ -142,6 +148,33 @@ mod tests {
assert_eq!(end, unsafe { buffer.as_mut_ptr().offset(7) });
}
#[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];

View File

@ -8,6 +8,8 @@ mod tests {
#[test]
fn test_ntoa() {
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");

View File

@ -1,14 +1,28 @@
use std::ffi::CStr;
use kernaux_sys::{
itoa10 as kernaux_itoa10, itoa16 as kernaux_itoa16,
utoa10 as kernaux_utoa10, utoa16 as kernaux_utoa16,
itoa10 as kernaux_itoa10, itoa16 as kernaux_itoa16, itoa8 as kernaux_itoa8,
utoa10 as kernaux_utoa10, utoa16 as kernaux_utoa16, utoa8 as kernaux_utoa8,
};
use kernaux_sys::{
ITOA10_BUFFER_SIZE, ITOA16_BUFFER_SIZE, UTOA10_BUFFER_SIZE,
UTOA16_BUFFER_SIZE,
ITOA10_BUFFER_SIZE, ITOA16_BUFFER_SIZE, ITOA8_BUFFER_SIZE,
UTOA10_BUFFER_SIZE, UTOA16_BUFFER_SIZE, UTOA8_BUFFER_SIZE,
};
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()) };
@ -41,6 +55,25 @@ pub fn itoa16(value: i64) -> String {
mod tests {
use super::*;
#[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");

View File

@ -62,6 +62,16 @@ char *kernaux_itoa(int64_t value, char *buffer, int base, const char *const pref
}
}
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);

View File

@ -127,6 +127,94 @@ static const struct {
{ "9ix", 36, 12345 }, { "9IX", -36, 12345 },
};
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;
@ -504,6 +592,51 @@ int main()
}
}
{
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];