1
0
Fork 0
mirror of https://github.com/tailix/libkernaux.git synced 2024-10-30 11:54:01 -04:00

Ruby: Add methods KernAux.utoa, .itoa

This commit is contained in:
Alex Kotov 2022-01-30 03:12:09 +05:00
parent 3d677837cf
commit 33eb48ddb0
Signed by: kotovalexarian
GPG key ID: 553C0EBBEB5D5F08
6 changed files with 429 additions and 1 deletions

View file

@ -44,7 +44,7 @@ void init_cmdline()
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));
rb_define_class_under(rb_KernAux, "Error", rb_eRuntimeError));
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 =

View file

@ -11,6 +11,8 @@ unless have_var 'kernaux_assert_cb', 'kernaux.h'
raise 'kernaux_assert_cb not found'
end
have_func 'kernaux_utoa'
have_func 'kernaux_itoa'
have_func 'kernaux_utoa10'
have_func 'kernaux_itoa10'
have_func 'kernaux_utoa16'

View file

@ -1,6 +1,12 @@
#include <kernaux.h>
#include <ruby.h>
#ifdef HAVE_KERNAUX_UTOA
static VALUE rb_KernAux_utoa(VALUE self, VALUE number, VALUE base);
#endif
#ifdef HAVE_KERNAUX_ITOA
static VALUE rb_KernAux_itoa(VALUE self, VALUE number, VALUE base);
#endif
#ifdef HAVE_KERNAUX_UTOA10
static VALUE rb_KernAux_utoa10(VALUE self, VALUE number);
#endif
@ -15,17 +21,50 @@ static VALUE rb_KernAux_itoa16(VALUE self, VALUE number);
#endif
static ID rb_intern_LESS = Qnil;
static ID rb_intern_b = Qnil;
static ID rb_intern_B = Qnil;
static ID rb_intern_freeze = 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 = Qnil;
static VALUE rb_KernAux_Error = Qnil;
static VALUE rb_KernAux_InvalidNtoaBaseError = Qnil;
void init_ntoa()
{
rb_gc_register_mark_object(ID2SYM(rb_intern_LESS = rb_intern("<")));
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_freeze = rb_intern("freeze")));
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 = rb_define_module("KernAux"));
rb_gc_register_mark_object(rb_KernAux_Error =
rb_define_class_under(rb_KernAux, "Error", rb_eRuntimeError));
rb_gc_register_mark_object(rb_KernAux_InvalidNtoaBaseError =
rb_define_class_under(rb_KernAux, "InvalidNtoaBaseError",
rb_KernAux_Error));
#ifdef HAVE_KERNAUX_UTOA
rb_define_singleton_method(rb_KernAux, "utoa", rb_KernAux_utoa, 2);
#endif
#ifdef HAVE_KERNAUX_ITOA
rb_define_singleton_method(rb_KernAux, "itoa", rb_KernAux_itoa, 2);
#endif
#ifdef HAVE_KERNAUX_UTOA10
rb_define_singleton_method(rb_KernAux, "utoa10", rb_KernAux_utoa10, 1);
#endif
@ -40,6 +79,77 @@ void init_ntoa()
#endif
}
#ifdef HAVE_KERNAUX_UTOA
VALUE rb_KernAux_utoa(
const VALUE self_rb __attribute__((unused)),
const VALUE number_rb,
const VALUE base_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");
}
int base = 0;
if (TYPE(base_rb) == T_SYMBOL) {
const ID base_id = SYM2ID(base_rb);
if (base_id == rb_intern_b) base = 'b';
else if (base_id == rb_intern_B) base = 'B';
else if (base_id == rb_intern_h) base = 'h';
else if (base_id == rb_intern_H) base = 'H';
else if (base_id == rb_intern_o) base = 'o';
else if (base_id == rb_intern_O) base = 'O';
else if (base_id == rb_intern_d) base = 'd';
else if (base_id == rb_intern_D) base = 'D';
else if (base_id == rb_intern_x) base = 'x';
else if (base_id == rb_intern_X) base = 'X';
else {
rb_raise(rb_KernAux_InvalidNtoaBaseError, "invalid base");
}
} else {
base = NUM2INT(base_rb);
}
char buffer[KERNAUX_UTOA_BUFFER_SIZE];
kernaux_utoa(NUM2ULL(number_rb), buffer, base);
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
}
#endif
#ifdef HAVE_KERNAUX_ITOA
VALUE rb_KernAux_itoa(
const VALUE self_rb __attribute__((unused)),
const VALUE number_rb,
const VALUE base_rb
) {
RB_INTEGER_TYPE_P(number_rb);
int base = 0;
if (TYPE(base_rb) == T_SYMBOL) {
const ID base_id = SYM2ID(base_rb);
if (base_id == rb_intern_b) base = 'b';
else if (base_id == rb_intern_B) base = 'B';
else if (base_id == rb_intern_h) base = 'h';
else if (base_id == rb_intern_H) base = 'H';
else if (base_id == rb_intern_o) base = 'o';
else if (base_id == rb_intern_O) base = 'O';
else if (base_id == rb_intern_d) base = 'd';
else if (base_id == rb_intern_D) base = 'D';
else if (base_id == rb_intern_x) base = 'x';
else if (base_id == rb_intern_X) base = 'X';
else {
rb_raise(rb_KernAux_InvalidNtoaBaseError, "invalid base");
}
} else {
base = NUM2INT(base_rb);
}
char buffer[KERNAUX_ITOA_BUFFER_SIZE];
kernaux_itoa(NUM2LL(number_rb), buffer, base);
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

@ -55,4 +55,12 @@ module KernAux
# @see .cmdline
#
class CmdlineError < Error; end
##
# Raised when {.utoa} or {.itoa} base is invalid.
#
# @see .utoa
# @see .itoa
#
class InvalidNtoaBaseError < Error; end
end

View file

@ -0,0 +1,161 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe KernAux, '.itoa' do
if described_class.singleton_class.method_defined? :itoa
subject(:itoa) { described_class.itoa number, base }
let(:number) { rand((-2**63)..(2**63 - 1)) }
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 }
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 base }
end
context 'when number is max int64_t' do
let(:number) { 2**63 - 1 }
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 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
end
end

View file

@ -0,0 +1,147 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe KernAux, '.utoa' do
if described_class.singleton_class.method_defined? :utoa
subject(:utoa) { described_class.utoa number, base }
let(:number) { rand 0..(2**64 - 1) }
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 }
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 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
end
end