mirror of https://github.com/tailix/libkernaux.git
Compare commits
6 Commits
bb64237fe4
...
4a44d0f20a
Author | SHA1 | Date |
---|---|---|
Alex Kotov | 4a44d0f20a | |
Alex Kotov | 18f3c70c1b | |
Alex Kotov | be761bd9f7 | |
Alex Kotov | 0d8bdec847 | |
Alex Kotov | f3f34e9057 | |
Alex Kotov | 187475826e |
|
@ -8,10 +8,11 @@ main_freebsd_task:
|
|||
name: Main (FreeBSD)
|
||||
only_if: $CIRRUS_BRANCH == 'master' || $CIRRUS_BASE_BRANCH == 'master'
|
||||
dependencies_script:
|
||||
- pkg install --yes autoconf automake libtool
|
||||
- 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-debug --enable-checks --enable-checks-pthreads CFLAGS='-O3'
|
||||
- ./configure --enable-debug --enable-checks-all CFLAGS='-O3'
|
||||
- make
|
||||
- sudo make install
|
||||
main_test_script:
|
||||
|
|
|
@ -76,7 +76,7 @@ jobs:
|
|||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --host='${{matrix.cross.arch}}-unknown-elf' --enable-checks --enable-checks-pthreads --enable-checks-python AR='${{matrix.cross.cprefix}}ar' CC='${{matrix.cross.cprefix}}gcc' LD='${{matrix.cross.cprefix}}ld' RANLIB='${{matrix.cross.cprefix}}ranlib'
|
||||
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
|
||||
|
@ -121,7 +121,7 @@ jobs:
|
|||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --host='i386-elf' ${{matrix.debug}} --enable-freestanding --with-libc AR="$(which i686-linux-gnu-ar)" CC="$(which i686-linux-gnu-gcc)" LD="$(which i686-linux-gnu-ld)" RANLIB="$(which i686-linux-gnu-ranlib)"
|
||||
run: ./configure --host='i386-elf' ${{matrix.debug}} --enable-freestanding --with-libc CC="$(which i686-linux-gnu-gcc)"
|
||||
- name: make
|
||||
run: make
|
||||
|
||||
|
|
|
@ -35,6 +35,8 @@ jobs:
|
|||
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
|
||||
|
|
|
@ -33,6 +33,8 @@ jobs:
|
|||
run: make
|
||||
- name: install
|
||||
run: sudo make install
|
||||
- name: ldconfig
|
||||
run: sudo ldconfig
|
||||
- working-directory: bindings/ruby
|
||||
name: setup
|
||||
run: ./bin/setup
|
||||
|
|
|
@ -32,6 +32,8 @@ jobs:
|
|||
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}}
|
||||
|
|
|
@ -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-debug --enable-checks-all CFLAGS='-O3'
|
||||
make
|
||||
doas make install
|
||||
- test: |
|
||||
cd libkernaux
|
||||
make check
|
|
@ -1,3 +1,11 @@
|
|||
2022-12-14 Alex Kotov <kotovalexarian@gmail.com>
|
||||
|
||||
* configure.ac: Enable shared library
|
||||
|
||||
2022-12-13 Alex Kotov <kotovalexarian@gmail.com>
|
||||
|
||||
* tests/Makefile.am: Fix FreeBSD builds
|
||||
|
||||
2022-12-12 Alex Kotov <kotovalexarian@gmail.com>
|
||||
|
||||
* examples/Makefile.am: Fix builds without pthreads
|
||||
|
|
|
@ -28,6 +28,7 @@ lib_LTLIBRARIES = libkernaux.la
|
|||
# Required files #
|
||||
##################
|
||||
|
||||
libkernaux_la_LDFLAGS = -version-info @PACKAGE_VERSION_SO@
|
||||
libkernaux_la_LIBADD =
|
||||
libkernaux_la_SOURCES = \
|
||||
src/assert.c \
|
||||
|
|
|
@ -184,6 +184,7 @@ Tips
|
|||
./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
|
||||
|
@ -210,13 +211,15 @@ without it in `$PATH`:
|
|||
```
|
||||
./configure \
|
||||
--host='i386-elf' \
|
||||
--disable-shared \
|
||||
--enable-freestanding \
|
||||
--with-libc \
|
||||
AR="$(which i386-elf-ar)" \
|
||||
CC="$(which i386-elf-gcc)" \
|
||||
RANLIB="$(which i386-elf-ranlib)"
|
||||
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`.
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
0:0:0
|
|
@ -27,7 +27,11 @@ Metrics/BlockLength:
|
|||
|
||||
Metrics/BlockNesting:
|
||||
Exclude:
|
||||
- 'test/printf.rb'
|
||||
- 'test/sprintf.rb'
|
||||
|
||||
Performance/CollectionLiteralInLoop:
|
||||
Exclude:
|
||||
- 'test/**/*.rb'
|
||||
|
||||
Security/Eval:
|
||||
Exclude:
|
||||
|
|
|
@ -2,4 +2,5 @@ MRuby::Build.new do |conf|
|
|||
conf.toolchain
|
||||
conf.enable_test
|
||||
conf.gem '.'
|
||||
conf.gem core: 'mruby-bin-mirb'
|
||||
end
|
||||
|
|
|
@ -7,24 +7,6 @@ module KernAux
|
|||
raise AssertError, "#{file}:#{line}:#{msg}"
|
||||
}
|
||||
|
||||
SPRINTF1_BUFFER_SIZE = 10_000
|
||||
|
||||
if Version.with_printf?
|
||||
def self.sprintf(*args)
|
||||
args.map do |arg|
|
||||
if arg.is_a? Array
|
||||
sprintf1(*arg)
|
||||
else
|
||||
arg
|
||||
end
|
||||
end.join.freeze
|
||||
end
|
||||
|
||||
def self.sprintf1(format, *args)
|
||||
snprintf1(SPRINTF1_BUFFER_SIZE, format, *args).first
|
||||
end
|
||||
end
|
||||
|
||||
class Error < RuntimeError; end
|
||||
class AssertError < Error; end
|
||||
class CmdlineError < Error; end
|
||||
|
|
|
@ -1,155 +1,147 @@
|
|||
#include "main.h"
|
||||
|
||||
#include "dynarg.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <mruby/array.h>
|
||||
#include <mruby/error.h>
|
||||
#include <mruby/presym.h>
|
||||
#include <mruby/string.h>
|
||||
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
struct snprintf1_userdata {
|
||||
const struct KernAux_PrintfFmt_Spec *spec;
|
||||
const struct DynArg *dynarg;
|
||||
mrb_int size;
|
||||
const char *format;
|
||||
char *str;
|
||||
};
|
||||
|
||||
static mrb_value rb_KernAux_snprintf1(mrb_state *mrb, mrb_value self);
|
||||
|
||||
static mrb_value snprintf1_protect(mrb_state *mrb, void *userdata);
|
||||
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, "snprintf1", rb_KernAux_snprintf1,
|
||||
MRB_ARGS_REQ(2) | MRB_ARGS_OPT(2));
|
||||
mrb_define_class_method(mrb, rb_KernAux, "sprintf", rb_KernAux_sprintf,
|
||||
MRB_ARGS_REQ(1) | MRB_ARGS_REST());
|
||||
}
|
||||
|
||||
mrb_value rb_KernAux_snprintf1(mrb_state *const mrb, mrb_value self)
|
||||
#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)
|
||||
{
|
||||
mrb_int size = 0;
|
||||
const char *format = NULL;
|
||||
mrb_value rest[3];
|
||||
mrb_bool rest_given[3];
|
||||
|
||||
mrb_get_args(
|
||||
mrb,
|
||||
"iz|o?o?o?",
|
||||
&size,
|
||||
&format,
|
||||
&rest[0], &rest_given[0],
|
||||
&rest[1], &rest_given[1],
|
||||
&rest[2], &rest_given[2]
|
||||
);
|
||||
|
||||
if (size < 0) mrb_raise(mrb, E_RANGE_ERROR, "expected non-negative size");
|
||||
|
||||
const char *fmt = format;
|
||||
|
||||
while (*fmt && *fmt != '%') ++fmt;
|
||||
if (*(fmt++) != '%') mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format");
|
||||
|
||||
struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create_out(&fmt);
|
||||
|
||||
while (*fmt) {
|
||||
if (*(fmt++) == '%') mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid format");
|
||||
}
|
||||
|
||||
int argc = 0;
|
||||
for (int i = 0; i < 3; ++i) if (rest_given[i]) ++argc;
|
||||
// FIXME: const
|
||||
char *format;
|
||||
mrb_value *args;
|
||||
mrb_int argc;
|
||||
mrb_get_args(mrb, "z*", &format, &args, &argc);
|
||||
|
||||
int arg_index = 0;
|
||||
if (spec.set_width && argc > arg_index) {
|
||||
KernAux_PrintfFmt_Spec_set_width(&spec, mrb_integer(rest[arg_index++]));
|
||||
}
|
||||
if (spec.set_precision && argc > arg_index) {
|
||||
KernAux_PrintfFmt_Spec_set_precision(&spec, mrb_integer(rest[arg_index++]));
|
||||
}
|
||||
mrb_value result = mrb_str_new_lit(mrb, "");
|
||||
|
||||
struct DynArg dynarg = DynArg_create();
|
||||
if (argc > arg_index) {
|
||||
mrb_value arg_rb = rest[arg_index];
|
||||
while (*format) {
|
||||
if (*format != '%') {
|
||||
mrb_str_cat(mrb, result, format, 1);
|
||||
++format;
|
||||
continue;
|
||||
}
|
||||
|
||||
// FIXME: unnecessary
|
||||
const char *const old_format = format;
|
||||
++format;
|
||||
struct KernAux_PrintfFmt_Spec spec =
|
||||
// FIXME: no type cast
|
||||
KernAux_PrintfFmt_Spec_create_out((const char**)&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) {
|
||||
mrb_int arg = mrb_integer(arg_rb);
|
||||
if (arg < 0) mrb_raise(mrb, E_ARGUMENT_ERROR, "expected non-negative argument");
|
||||
DynArg_use_unsigned_long_long(&dynarg, arg);
|
||||
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)
|
||||
{
|
||||
DynArg_use_double(&dynarg, mrb_as_float(mrb, arg_rb));
|
||||
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) {
|
||||
DynArg_use_char(&dynarg, *RSTRING_CSTR(mrb, arg_rb));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
char *const str = malloc(size);
|
||||
if (!str) mrb_raise(mrb, mrb_exc_get_id(mrb, MRB_ERROR_SYM(NoMemoryError)), "snprintf1 buffer malloc");
|
||||
char buffer[BUFFER_SIZE];
|
||||
int slen;
|
||||
|
||||
struct snprintf1_userdata userdata = {
|
||||
.spec = &spec,
|
||||
.dynarg = &dynarg,
|
||||
.size = size,
|
||||
.format = format,
|
||||
.str = str,
|
||||
};
|
||||
mrb_bool error;
|
||||
mrb_value result = mrb_protect_error(mrb, snprintf1_protect, &userdata, &error);
|
||||
// FIXME: it's a hack
|
||||
// TODO: convert printf format spec to string
|
||||
const char tmp = *format;
|
||||
*format = '\0';
|
||||
|
||||
free(str);
|
||||
|
||||
if (error) {
|
||||
mrb_exc_raise(mrb, result);
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
mrb_value snprintf1_protect(mrb_state *const mrb, void *const userdata_raw)
|
||||
{
|
||||
const struct snprintf1_userdata *const userdata = userdata_raw;
|
||||
|
||||
int slen;
|
||||
if (userdata->spec->set_width) {
|
||||
if (userdata->spec->set_precision) {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->arg);
|
||||
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 {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->arg);
|
||||
}
|
||||
} else {
|
||||
if (userdata->spec->set_precision) {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->arg);
|
||||
} else {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->arg);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*format = tmp;
|
||||
mrb_str_cat(mrb, result, buffer, slen);
|
||||
}
|
||||
|
||||
mrb_value output_rb =
|
||||
mrb_obj_freeze(mrb, mrb_str_cat_cstr(mrb, mrb_str_new_lit(mrb, ""), userdata->str));
|
||||
if (arg_index < argc) {
|
||||
mrb_raise(mrb, E_ARGUMENT_ERROR, "too many arguments");
|
||||
}
|
||||
|
||||
mrb_value values[2];
|
||||
values[0] = output_rb;
|
||||
values[1] = mrb_fixnum_value(slen);
|
||||
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, 2, values));
|
||||
return mrb_obj_freeze(mrb, result);
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
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
|
||||
|
||||
[
|
||||
['', 'using regular tests'],
|
||||
['_orig', 'using original tests'],
|
||||
].each do |(suffix, description)|
|
||||
assert description do
|
||||
printf_yml =
|
||||
File.expand_path("../../../../common/printf#{suffix}.yml", __FILE__)
|
||||
|
||||
YAML.load(File.read(printf_yml)).each do |test|
|
||||
expected = test['result']
|
||||
|
||||
args = test['args'].map do |arg|
|
||||
if arg.is_a? String
|
||||
arg
|
||||
else
|
||||
arg.map do |item|
|
||||
if item.is_a? Array
|
||||
if item.length == 1
|
||||
item[0]
|
||||
elsif item[0] == 'long long'
|
||||
item[1]
|
||||
else
|
||||
raise "Unknown format: #{args.inspect}"
|
||||
end
|
||||
elsif item.is_a?(Float) && item.round == item
|
||||
item.round
|
||||
else
|
||||
item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
assert "transforms #{args.inspect} to #{expected.inspect}" do
|
||||
assert_equal expected, KernAux.sprintf(*args)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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 tests'],
|
||||
['_orig', 'using original tests'],
|
||||
].each do |(suffix, description)|
|
||||
assert description do
|
||||
printf_yml =
|
||||
File.expand_path("../../../../common/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
|
|
@ -1,208 +1,140 @@
|
|||
#include "main.h"
|
||||
|
||||
#include "dynarg.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#define BUFFER_SIZE 4096
|
||||
|
||||
#ifdef KERNAUX_VERSION_WITH_PRINTF
|
||||
|
||||
/*************
|
||||
* ::KernAux *
|
||||
*************/
|
||||
|
||||
static VALUE rb_KernAux_snprintf1(int argc, const VALUE *argv, VALUE self);
|
||||
|
||||
static VALUE rb_KernAux_snprintf1_PROTECT(VALUE userdata);
|
||||
|
||||
/************************
|
||||
* ::KernAux::Snprintf1 *
|
||||
************************/
|
||||
|
||||
static VALUE rb_KernAux_Snprintf1 = Qnil;
|
||||
|
||||
static size_t rb_KernAux_Snprintf1_DSIZE(const void *ptr);
|
||||
|
||||
const rb_data_type_t rb_KernAux_Snprintf1_DTYPE = {
|
||||
.wrap_struct_name = "KernAux::Snprintf1",
|
||||
.parent = NULL,
|
||||
.data = NULL,
|
||||
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
||||
.function = {
|
||||
.dfree = RUBY_DEFAULT_FREE,
|
||||
.dsize = rb_KernAux_Snprintf1_DSIZE,
|
||||
.dmark = NULL,
|
||||
.dcompact = NULL,
|
||||
.reserved = { 0 },
|
||||
},
|
||||
};
|
||||
|
||||
struct rb_KernAux_Snprintf1_DATA {
|
||||
const struct KernAux_PrintfFmt_Spec *spec;
|
||||
const struct DynArg *dynarg;
|
||||
int size;
|
||||
const char *format;
|
||||
char *str;
|
||||
};
|
||||
|
||||
/********
|
||||
* Main *
|
||||
********/
|
||||
/**
|
||||
* 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_gc_register_mark_object(
|
||||
rb_KernAux_Snprintf1 =
|
||||
// @api private
|
||||
rb_define_class_under(rb_KernAux, "Snprintf1", rb_cObject));
|
||||
rb_funcall(rb_KernAux, rb_intern("private_constant"), 1, ID2SYM(rb_intern("Snprintf1")));
|
||||
|
||||
rb_define_singleton_method(rb_KernAux, "snprintf1",
|
||||
rb_KernAux_snprintf1, -1);
|
||||
rb_define_singleton_method(rb_KernAux, "sprintf", rb_KernAux_sprintf, -1);
|
||||
}
|
||||
|
||||
/*************
|
||||
* ::KernAux *
|
||||
*************/
|
||||
#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_snprintf1(
|
||||
const int argc,
|
||||
const VALUE *const argv_rb,
|
||||
const VALUE self KERNAUX_UNUSED
|
||||
) {
|
||||
if (argc < 2 || argc > 5) rb_raise(rb_eArgError, "expected 2, 3, 4 or 5 args");
|
||||
VALUE rb_KernAux_sprintf(const int argc, VALUE *const argv, VALUE self)
|
||||
{
|
||||
if (argc == 0) rb_raise(rb_eArgError, "too few arguments");
|
||||
|
||||
const VALUE size_rb = argv_rb[0];
|
||||
VALUE format_rb = argv_rb[1];
|
||||
// FIXME: const
|
||||
char *format = StringValueCStr(argv[0]);
|
||||
int arg_index = 1;
|
||||
VALUE result = rb_str_new_literal("");
|
||||
|
||||
const int size = NUM2INT(size_rb);
|
||||
const char *const format = StringValueCStr(format_rb);
|
||||
while (*format) {
|
||||
if (*format != '%') {
|
||||
rb_str_cat(result, format, 1);
|
||||
++format;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (size < 0) rb_raise(rb_eRangeError, "expected non-negative size");
|
||||
// FIXME: unnecessary
|
||||
const char *const old_format = format;
|
||||
++format;
|
||||
struct KernAux_PrintfFmt_Spec spec =
|
||||
// FIXME: no type cast
|
||||
KernAux_PrintfFmt_Spec_create_out((const char**)&format);
|
||||
|
||||
const char *fmt = 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));
|
||||
}
|
||||
|
||||
while (*fmt && *fmt != '%') ++fmt;
|
||||
if (*(fmt++) != '%') rb_raise(rb_eArgError, "invalid format");
|
||||
|
||||
struct KernAux_PrintfFmt_Spec spec = KernAux_PrintfFmt_Spec_create_out(&fmt);
|
||||
|
||||
while (*fmt) {
|
||||
if (*(fmt++) == '%') rb_raise(rb_eArgError, "invalid format");
|
||||
}
|
||||
|
||||
int arg_index = 2;
|
||||
if (spec.set_width && argc > arg_index) {
|
||||
KernAux_PrintfFmt_Spec_set_width(&spec, NUM2INT(argv_rb[arg_index++]));
|
||||
}
|
||||
if (spec.set_precision && argc > arg_index) {
|
||||
KernAux_PrintfFmt_Spec_set_precision(&spec, NUM2INT(argv_rb[arg_index++]));
|
||||
}
|
||||
|
||||
struct DynArg dynarg = DynArg_create();
|
||||
if (argc > arg_index) {
|
||||
VALUE arg_rb = argv_rb[arg_index];
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
char *const str = malloc(size);
|
||||
if (!str) rb_raise(rb_eNoMemError, "snprintf1 buffer malloc");
|
||||
char buffer[BUFFER_SIZE];
|
||||
int slen;
|
||||
|
||||
struct rb_KernAux_Snprintf1_DATA *userdata;
|
||||
VALUE userdata_rb = TypedData_Make_Struct(
|
||||
rb_KernAux_Snprintf1,
|
||||
struct rb_KernAux_Snprintf1_DATA,
|
||||
&rb_KernAux_Snprintf1_DTYPE,
|
||||
userdata
|
||||
);
|
||||
if (NIL_P(userdata_rb) || userdata == NULL) {
|
||||
free(str);
|
||||
rb_raise(rb_eNoMemError, "snprintf1 userdata alloc");
|
||||
}
|
||||
// FIXME: it's a hack
|
||||
// TODO: convert printf format spec to string
|
||||
const char tmp = *format;
|
||||
*format = '\0';
|
||||
|
||||
userdata->spec = &spec;
|
||||
userdata->dynarg = &dynarg;
|
||||
userdata->size = size;
|
||||
userdata->format = format;
|
||||
userdata->str = str;
|
||||
|
||||
int state = 0;
|
||||
VALUE result =
|
||||
rb_protect(rb_KernAux_snprintf1_PROTECT, userdata_rb, &state);
|
||||
|
||||
free(str);
|
||||
|
||||
if (state == 0) {
|
||||
return result;
|
||||
} else {
|
||||
rb_jump_tag(state);
|
||||
}
|
||||
}
|
||||
|
||||
VALUE rb_KernAux_snprintf1_PROTECT(VALUE userdata_rb)
|
||||
{
|
||||
const struct rb_KernAux_Snprintf1_DATA *userdata = NULL;
|
||||
TypedData_Get_Struct(
|
||||
userdata_rb,
|
||||
struct rb_KernAux_Snprintf1_DATA,
|
||||
&rb_KernAux_Snprintf1_DTYPE,
|
||||
userdata
|
||||
);
|
||||
|
||||
int slen;
|
||||
if (userdata->spec->set_width) {
|
||||
if (userdata->spec->set_precision) {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->spec->precision, userdata->dynarg->arg);
|
||||
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 {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->width, userdata->dynarg->arg);
|
||||
}
|
||||
} else {
|
||||
if (userdata->spec->set_precision) {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->spec->precision, userdata->dynarg->arg);
|
||||
} else {
|
||||
slen = userdata->dynarg->use_dbl
|
||||
? kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->dbl)
|
||||
: kernaux_snprintf(userdata->str, userdata->size, userdata->format, userdata->dynarg->arg);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*format = tmp;
|
||||
rb_str_cat(result, buffer, slen);
|
||||
}
|
||||
|
||||
const VALUE output_rb =
|
||||
rb_funcall(rb_str_new2(userdata->str), rb_intern_freeze, 0);
|
||||
if (arg_index < argc) rb_raise(rb_eArgError, "too many arguments");
|
||||
|
||||
const VALUE result_rb = rb_ary_new2(2);
|
||||
rb_ary_push(result_rb, output_rb);
|
||||
rb_ary_push(result_rb, INT2NUM(slen));
|
||||
return rb_funcall(result_rb, rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
/************************
|
||||
* ::KernAux::Snprintf1 *
|
||||
************************/
|
||||
|
||||
size_t rb_KernAux_Snprintf1_DSIZE(const void *const ptr)
|
||||
{
|
||||
return sizeof(struct rb_KernAux_Snprintf1_DATA);
|
||||
return rb_funcall(result, rb_intern_freeze, 0);
|
||||
}
|
||||
|
||||
#endif // KERNAUX_VERSION_WITH_PRINTF
|
||||
|
|
|
@ -17,4 +17,3 @@ require_relative 'kernaux/assert'
|
|||
require_relative 'kernaux/cmdline'
|
||||
require_relative 'kernaux/errors'
|
||||
require_relative 'kernaux/ntoa'
|
||||
require_relative 'kernaux/printf'
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# rubocop:disable Style/Documentation
|
||||
begin; end
|
||||
|
||||
module KernAux
|
||||
# Buffer size for {.sprintf1}.
|
||||
# @todo Make it dynamic.
|
||||
SPRINTF1_BUFFER_SIZE = 10_000
|
||||
|
||||
# @!scope class
|
||||
|
||||
# @!parse [ruby]
|
||||
|
||||
if Version.with_printf?
|
||||
##
|
||||
# Typical `printf`.
|
||||
#
|
||||
# @param args [Array<String,
|
||||
# Array<(String, Object)>,
|
||||
# Array<(String, Integer, Object)>>]
|
||||
# @return [String] formatted output
|
||||
#
|
||||
# @example
|
||||
# KernAux.sprintf 'foo', ['%*s', 5, 'bar'], 'car', ['%d', 123]
|
||||
# #=> "foo barcar123"
|
||||
#
|
||||
def self.sprintf(*args)
|
||||
args.map do |arg|
|
||||
if arg.is_a? Array
|
||||
sprintf1(*arg)
|
||||
else
|
||||
arg
|
||||
end
|
||||
end.join.freeze
|
||||
end
|
||||
|
||||
##
|
||||
# `printf` for single formatting parameter.
|
||||
#
|
||||
# @param format [String] formatting string
|
||||
# @return [String] formatted output
|
||||
#
|
||||
# @see .sprintf Multiple formatting parameters
|
||||
#
|
||||
# @example
|
||||
# KernAux.sprintf1 '%%' #=> "%"
|
||||
# KernAux.sprintf1 '%s', 'foo' #=> "foo"
|
||||
# KernAux.sprintf1 '%5s', 'foo' #=> " foo"
|
||||
# KernAux.sprintf1 '%*s', 5, 'foo' #=> " foo"
|
||||
#
|
||||
def self.sprintf1(format, *args)
|
||||
snprintf1(SPRINTF1_BUFFER_SIZE, format, *args).first
|
||||
end
|
||||
|
||||
##
|
||||
# @!method snprintf1(buffer_size, format, ...)
|
||||
# `printf` for single formatting parameter with manual buffer size.
|
||||
#
|
||||
# @param buffer_size [Integer] buffer size (including terminating null
|
||||
# character)
|
||||
# @param format [String] formatting string
|
||||
# @return [Array<(String, Integer)>] formatted output and it's size
|
||||
#
|
||||
# @see .sprintf1 Automatic buffer size
|
||||
##
|
||||
end
|
||||
end
|
||||
|
||||
# rubocop:enable Style/Documentation
|
|
@ -1,280 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_printf? and RSpec.describe KernAux, '.snprintf1' do
|
||||
let(:size) { 10_000 }
|
||||
|
||||
context 'with 0 arguments' do
|
||||
specify do
|
||||
expect { described_class.snprintf1 }.to \
|
||||
raise_error ArgumentError, 'expected 2, 3, 4 or 5 args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 1 argument' do
|
||||
specify do
|
||||
expect { described_class.snprintf1 size }.to \
|
||||
raise_error ArgumentError, 'expected 2, 3, 4 or 5 args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 6 arguments' do
|
||||
specify do
|
||||
expect do
|
||||
described_class.snprintf1 size, '%*.*s', 20, 10, 'foo', 'bar'
|
||||
end.to \
|
||||
raise_error ArgumentError, 'expected 2, 3, 4 or 5 args'
|
||||
end
|
||||
end
|
||||
|
||||
context 'with 2 arguments' do
|
||||
subject(:snprintf1) { described_class.snprintf1 size, format }
|
||||
|
||||
let(:format) { '%%' }
|
||||
|
||||
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 '%' }
|
||||
specify { expect(snprintf1[1]).to eq 1 }
|
||||
|
||||
context 'with leading and trailing spaces' do
|
||||
let(:format) { ' %% ' }
|
||||
|
||||
specify { expect(snprintf1[0]).to eq ' % ' }
|
||||
end
|
||||
|
||||
context 'with "%s" format' do
|
||||
let(:format) { '%s' }
|
||||
|
||||
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
|
||||
|
||||
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 3 arguments' do
|
||||
subject(:snprintf1) { described_class.snprintf1 size, format, arg }
|
||||
|
||||
let(:format) { '%s' }
|
||||
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 }
|
||||
specify { expect(snprintf1[1]).to eq arg.size }
|
||||
|
||||
context 'with leading and trailing spaces' do
|
||||
let(:format) { ' %s ' }
|
||||
|
||||
specify { expect(snprintf1[0]).to eq " #{arg} " }
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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 5 arguments' do
|
||||
subject :snprintf1 do
|
||||
described_class.snprintf1 size, format, width, precision, arg
|
||||
end
|
||||
|
||||
let(:format) { '%*.*f' }
|
||||
let(:width) { 20 }
|
||||
let(:precision) { 3 }
|
||||
let(:arg) { 123.456789 }
|
||||
|
||||
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[1]).to eq width }
|
||||
|
||||
specify do
|
||||
expect(snprintf1[0]).to eq arg.round(precision).to_s.rjust(width, ' ')
|
||||
end
|
||||
|
||||
context 'with leading and trailing spaces' do
|
||||
let(:format) { ' %*.*f ' }
|
||||
|
||||
specify do
|
||||
expect(snprintf1[0]).to \
|
||||
eq " #{arg.round(precision).to_s.rjust(width, ' ')} "
|
||||
end
|
||||
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
|
||||
|
||||
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
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
KernAux::Version.with_printf? and RSpec.describe KernAux, '.sprintf1' do
|
||||
context 'with 1 argument' do
|
||||
subject(:sprintf1) { described_class.sprintf1 format }
|
||||
|
||||
let(:format) { '%%' }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq described_class.snprintf1(1000, format).first }
|
||||
end
|
||||
|
||||
context 'with 2 argument' do
|
||||
subject(:sprintf1) { described_class.sprintf1 format, arg }
|
||||
|
||||
let(:format) { '%s' }
|
||||
let(:arg) { 'Hello, World!' }
|
||||
|
||||
it { is_expected.to be_instance_of String }
|
||||
it { is_expected.to be_frozen }
|
||||
it { is_expected.to eq described_class.snprintf1(100, format, arg).first }
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
# 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 'for empty string value' do
|
||||
subject(:sprintf) { described_class.sprintf ['Hello testing%s'] }
|
||||
|
||||
it { is_expected.to eq 'Hello testing' }
|
||||
end
|
||||
|
||||
[
|
||||
['', 'using regular tests'],
|
||||
['_orig', 'using original tests'],
|
||||
].each do |(suffix, description)|
|
||||
context description do
|
||||
printf_yml = File.expand_path(
|
||||
"../../../../../../common/printf#{suffix}.yml",
|
||||
__dir__,
|
||||
)
|
||||
|
||||
YAML.safe_load_file(printf_yml).each do |test|
|
||||
expected = test['result']
|
||||
|
||||
args = test['args'].map do |arg|
|
||||
if arg.is_a? String
|
||||
arg
|
||||
else
|
||||
arg.map do |item|
|
||||
if item.is_a? Array
|
||||
if item.length == 1
|
||||
item[0]
|
||||
elsif item[0] == 'long long'
|
||||
item[1]
|
||||
else
|
||||
raise "Unknown format: #{args.inspect}"
|
||||
end
|
||||
else
|
||||
item
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "transforms #{args.inspect} to #{expected.inspect}" do
|
||||
expect(described_class.sprintf(*args)).to eq expected
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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 tests'],
|
||||
['_orig', 'using original tests'],
|
||||
].each do |(suffix, description)|
|
||||
context description do
|
||||
printf_yml = File.expand_path(
|
||||
"../../../../../common/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
|
|
@ -35,6 +35,8 @@ AC_CONFIG_FILES([
|
|||
|
||||
AM_INIT_AUTOMAKE([1.16 subdir-objects])
|
||||
|
||||
AC_SUBST([PACKAGE_VERSION_SO], m4_normalize(m4_include([VERSION_SO])))
|
||||
|
||||
|
||||
|
||||
###############
|
||||
|
@ -319,10 +321,8 @@ AS_IF([test "$enable_freestanding" = yes],
|
|||
##############
|
||||
|
||||
AC_LANG([C])
|
||||
|
||||
AM_PROG_AR
|
||||
AM_PROG_AS
|
||||
AC_PROG_CC
|
||||
|
||||
AC_C_INLINE
|
||||
|
||||
AC_HEADER_STDBOOL
|
||||
|
@ -380,7 +380,7 @@ AS_IF([test "$enable_checks_python" = yes -a "$enable_checks" = yes -a "$PYTHON_
|
|||
# Initialize Libtool #
|
||||
######################
|
||||
|
||||
LT_INIT([disable-shared])
|
||||
LT_INIT
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -20,9 +20,11 @@ CPPCHECK_INC = \
|
|||
-I$(top_srcdir)/include
|
||||
|
||||
CPPCHECK_SUPPRESS = \
|
||||
--suppress='constArgument:$(top_srcdir)/examples/macro_cast.c' \
|
||||
--suppress='unusedStructMember:$(top_srcdir)/examples/*.c' \
|
||||
--suppress='unusedStructMember:$(top_srcdir)/tests/test_multiboot2_info_*.c'
|
||||
--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 \
|
||||
|
|
|
@ -104,7 +104,7 @@ endif
|
|||
CLEANFILES += test_cmdline_gen.c
|
||||
|
||||
test_cmdline_gen.c: $(top_srcdir)/tests/cmdline_gen.py $(top_srcdir)/tests/cmdline_gen.jinja $(top_srcdir)/common/cmdline.yml
|
||||
$(PYTHON) $+ $@
|
||||
$(PYTHON) $(top_srcdir)/tests/cmdline_gen.py $(top_srcdir)/tests/cmdline_gen.jinja $(top_srcdir)/common/cmdline.yml test_cmdline_gen.c
|
||||
|
||||
############
|
||||
# test_elf #
|
||||
|
@ -338,7 +338,7 @@ 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)/common/printf_fmt.yml
|
||||
$(PYTHON) $+ $@
|
||||
$(PYTHON) $(top_srcdir)/tests/printf_fmt_gen.py $(top_srcdir)/tests/printf_fmt_gen.jinja $(top_srcdir)/common/printf_fmt.yml test_printf_fmt_gen.c
|
||||
|
||||
###################
|
||||
# test_printf_gen #
|
||||
|
@ -361,7 +361,7 @@ endif
|
|||
CLEANFILES += test_printf_gen.c
|
||||
|
||||
test_printf_gen.c: $(top_srcdir)/tests/printf_gen.py $(top_srcdir)/tests/printf_gen.jinja $(top_srcdir)/common/printf.yml $(top_srcdir)/common/printf_orig.yml
|
||||
$(PYTHON) $+ $@
|
||||
$(PYTHON) $(top_srcdir)/tests/printf_gen.py $(top_srcdir)/tests/printf_gen.jinja $(top_srcdir)/common/printf.yml $(top_srcdir)/common/printf_orig.yml test_printf_gen.c
|
||||
|
||||
####################
|
||||
# test_units_human #
|
||||
|
|
Loading…
Reference in New Issue