mirror of
https://github.com/tailix/libkernaux.git
synced 2025-04-07 17:32:45 -04:00
Ruby: implement width for printf
This commit is contained in:
parent
f7476784ce
commit
3b2da38480
4 changed files with 92 additions and 20 deletions
|
@ -26,7 +26,7 @@ VALUE rb_KernAux_snprintf1(
|
|||
const VALUE *const argv_rb,
|
||||
const VALUE self __attribute__((unused))
|
||||
) {
|
||||
if (argc != 2 && argc != 3) rb_raise(rb_eArgError, "expected 2 or 3 args");
|
||||
if (argc < 2 || argc > 4) rb_raise(rb_eArgError, "expected 2, 3 or 4 args");
|
||||
|
||||
const VALUE size_rb = argv_rb[0];
|
||||
VALUE format_rb = argv_rb[1];
|
||||
|
@ -41,6 +41,8 @@ VALUE rb_KernAux_snprintf1(
|
|||
while (*fmt && *fmt != '%') ++fmt;
|
||||
if (*(fmt++) != '%') rb_raise(rb_eArgError, "invalid format");
|
||||
|
||||
bool has_width = false;
|
||||
|
||||
// Mimic printf behavior.
|
||||
{
|
||||
bool flags;
|
||||
|
@ -59,6 +61,7 @@ VALUE rb_KernAux_snprintf1(
|
|||
while (*fmt >= '0' && *fmt <= '9') ++fmt;
|
||||
} else if (*fmt == '*') {
|
||||
++fmt;
|
||||
has_width = true;
|
||||
}
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
|
@ -85,6 +88,9 @@ VALUE rb_KernAux_snprintf1(
|
|||
if (*(fmt++) == '%') rb_raise(rb_eArgError, "invalid format");
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
if (has_width && argc > 2) width = NUM2INT(argv_rb[2]);
|
||||
|
||||
bool use_dbl = false;
|
||||
double dbl;
|
||||
union {
|
||||
|
@ -94,8 +100,8 @@ VALUE rb_KernAux_snprintf1(
|
|||
char chr;
|
||||
} __attribute__((packed)) arg = { .str = "" };
|
||||
|
||||
if (argc == 3) {
|
||||
VALUE arg_rb = argv_rb[2];
|
||||
if (argc == (has_width ? 4 : 3)) {
|
||||
VALUE arg_rb = argv_rb[has_width ? 3 : 2];
|
||||
|
||||
if (c == 'd' || c == 'i') {
|
||||
RB_INTEGER_TYPE_P(arg_rb);
|
||||
|
@ -121,9 +127,18 @@ VALUE rb_KernAux_snprintf1(
|
|||
|
||||
char *const str = malloc(size);
|
||||
if (!str) rb_raise(rb_eNoMemError, "snprintf1 buffer malloc");
|
||||
const int slen = use_dbl
|
||||
? kernaux_snprintf(str, size, format, dbl)
|
||||
: kernaux_snprintf(str, size, format, arg);
|
||||
|
||||
int slen;
|
||||
if (has_width) {
|
||||
slen = use_dbl
|
||||
? kernaux_snprintf(str, size, format, width, dbl)
|
||||
: kernaux_snprintf(str, size, format, width, arg);
|
||||
} else {
|
||||
slen = use_dbl
|
||||
? kernaux_snprintf(str, size, format, dbl)
|
||||
: kernaux_snprintf(str, size, format, arg);
|
||||
}
|
||||
|
||||
const VALUE output_rb =
|
||||
rb_funcall(rb_str_new2(str), rb_intern_freeze, 0);
|
||||
free(str);
|
||||
|
|
|
@ -33,13 +33,7 @@ module KernAux
|
|||
end.join.freeze
|
||||
end
|
||||
|
||||
def self.sprintf1(format, arg = nil)
|
||||
if arg.nil?
|
||||
snprintf1(SPRINTF1_BUFFER_SIZE, format).first
|
||||
else
|
||||
snprintf1(SPRINTF1_BUFFER_SIZE, format, arg).first
|
||||
end
|
||||
end
|
||||
def self.sprintf1(...) = snprintf1(SPRINTF1_BUFFER_SIZE, ...).first
|
||||
|
||||
##
|
||||
# Our base class for runtime errors.
|
||||
|
|
|
@ -33,7 +33,7 @@ RSpec.describe KernAux, '.snprintf1' do
|
|||
end
|
||||
|
||||
context 'when size has invalid type' do
|
||||
let(:size) { '10_000' }
|
||||
let(:size) { '10000' }
|
||||
|
||||
specify { expect { snprintf1 }.to raise_error TypeError }
|
||||
end
|
||||
|
@ -93,7 +93,68 @@ RSpec.describe KernAux, '.snprintf1' do
|
|||
end
|
||||
|
||||
context 'when size has invalid type' do
|
||||
let(:size) { '10_000' }
|
||||
let(:size) { '10000' }
|
||||
|
||||
specify { expect { snprintf1 }.to raise_error TypeError }
|
||||
end
|
||||
|
||||
context 'when size is negative' do
|
||||
let(:size) { -1 }
|
||||
|
||||
specify do
|
||||
expect { snprintf1 }.to \
|
||||
raise_error RangeError, 'expected non-negative size'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when format doesn\'t include "%" char' do
|
||||
let(:format) { 'foo' }
|
||||
|
||||
specify do
|
||||
expect { snprintf1 }.to raise_error ArgumentError, 'invalid format'
|
||||
end
|
||||
end
|
||||
|
||||
context 'when format includes more than two "%" chars' do
|
||||
let(:format) { '%%%' }
|
||||
|
||||
specify do
|
||||
expect { snprintf1 }.to raise_error ArgumentError, 'invalid format'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 4 arguments' do
|
||||
subject(:snprintf1) { described_class.snprintf1 size, format, width, arg }
|
||||
|
||||
let(:format) { '%*s' }
|
||||
let(:width) { 20 }
|
||||
let(:arg) { 'Hello, World!' }
|
||||
|
||||
it { is_expected.to be_instance_of Array }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to all be_frozen }
|
||||
|
||||
specify { expect(snprintf1.size).to equal 2 }
|
||||
specify { expect(snprintf1[0]).to be_instance_of String }
|
||||
specify { expect(snprintf1[1]).to be_instance_of Integer }
|
||||
specify { expect(snprintf1[0]).to eq arg.rjust(width, ' ') }
|
||||
specify { expect(snprintf1[1]).to eq width }
|
||||
|
||||
context 'with leading and trailing spaces' do
|
||||
let(:format) { ' %*s ' }
|
||||
|
||||
specify { expect(snprintf1[0]).to eq " #{arg.rjust(width, ' ')} " }
|
||||
end
|
||||
|
||||
context 'with "%*%" format' do
|
||||
let(:format) { '%*%' }
|
||||
|
||||
specify { expect(snprintf1[0]).to eq '%' }
|
||||
end
|
||||
|
||||
context 'when size has invalid type' do
|
||||
let(:size) { '10000' }
|
||||
|
||||
specify { expect { snprintf1 }.to raise_error TypeError }
|
||||
end
|
||||
|
@ -127,21 +188,21 @@ RSpec.describe KernAux, '.snprintf1' do
|
|||
context 'with 0 arguments' do
|
||||
specify do
|
||||
expect { described_class.snprintf1 }.to \
|
||||
raise_error ArgumentError, 'expected 2 or 3 args'
|
||||
raise_error ArgumentError, 'expected 2, 3 or 4 args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 1 argument' do
|
||||
specify do
|
||||
expect { described_class.snprintf1 size }.to \
|
||||
raise_error ArgumentError, 'expected 2 or 3 args'
|
||||
raise_error ArgumentError, 'expected 2, 3 or 4 args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 4 arguments' do
|
||||
context 'with 5 arguments' do
|
||||
specify do
|
||||
expect { described_class.snprintf1 size, '%s', 'foo', 'bar' }.to \
|
||||
raise_error ArgumentError, 'expected 2 or 3 args'
|
||||
expect { described_class.snprintf1 size, '%*s', 20, 'foo', 'bar' }.to \
|
||||
raise_error ArgumentError, 'expected 2, 3 or 4 args'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -106,6 +106,8 @@ int main()
|
|||
test("123.456789", "%f", 123.456789);
|
||||
#endif
|
||||
|
||||
test("%", "%*%", 20);
|
||||
|
||||
// - flag
|
||||
// ...
|
||||
test("42", "%0-d", 42);
|
||||
|
|
Loading…
Add table
Reference in a new issue