mruby: implement KernAux.snprintf1 (with some bug)

This commit is contained in:
Alex Kotov 2022-05-28 14:45:43 +03:00
parent 081dc4019b
commit 7698d0f3c6
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
5 changed files with 193 additions and 4 deletions

45
pkgs/mruby/src/dynarg.c Normal file
View File

@ -0,0 +1,45 @@
#include "dynarg.h"
struct DynArg DynArg_create()
{
struct DynArg dynarg;
DynArg_init(&dynarg);
return dynarg;
}
void DynArg_init(struct DynArg *const dynarg)
{
dynarg->use_dbl = false;
dynarg->dbl = 0.0;
dynarg->arg.str = "";
}
void DynArg_use_char(struct DynArg *const dynarg, const char chr)
{
dynarg->use_dbl = false;
dynarg->arg.chr = chr;
}
void DynArg_use_double(struct DynArg *const dynarg, const double dbl)
{
dynarg->use_dbl = true;
dynarg->dbl = dbl;
}
void DynArg_use_long_long(struct DynArg *const dynarg, const long long ll)
{
dynarg->use_dbl = false;
dynarg->arg.ll = ll;
}
void DynArg_use_str(struct DynArg *const dynarg, const char *const str)
{
dynarg->use_dbl = false;
dynarg->arg.str = str;
}
void DynArg_use_unsigned_long_long(struct DynArg *const dynarg, const unsigned long long ull)
{
dynarg->use_dbl = false;
dynarg->arg.ull = ull;
}

35
pkgs/mruby/src/dynarg.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef INCLUDED_DYNARG
#define INCLUDED_DYNARG
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
struct DynArg {
bool use_dbl;
double dbl;
// TODO: check if this will work on different endianness.
union {
char chr;
long long ll;
const char *str;
unsigned long long ull;
} __attribute__((packed)) arg;
};
struct DynArg DynArg_create();
void DynArg_init(struct DynArg *dynarg);
void DynArg_use_char(struct DynArg *dynarg, char chr);
void DynArg_use_double(struct DynArg *dynarg, double dbl);
void DynArg_use_long_long(struct DynArg *dynarg, long long ll);
void DynArg_use_str(struct DynArg *dynarg, const char *str);
void DynArg_use_unsigned_long_long(struct DynArg *dynarg, unsigned long long ull);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,3 +1,4 @@
#include "dynarg.h"
#include "main.h"
#include <kernaux.h>
@ -5,6 +6,9 @@
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/presym.h>
#include <mruby/string.h>
#include <stdlib.h>
static mrb_value rb_KernAux_snprintf1(mrb_state *mrb, mrb_value self);
@ -16,10 +20,103 @@ void init_printf(mrb_state *const mrb)
MRB_ARGS_REQ(2) | MRB_ARGS_OPT(2));
}
// FIXME: rewrite to ensure no memory leak on exception.
mrb_value rb_KernAux_snprintf1(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();
fmt = KernAux_PrintfFmt_Spec_parse(&spec, 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;
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++]));
}
struct DynArg dynarg = DynArg_create();
if (argc > arg_index) {
mrb_value arg_rb = rest[arg_index];
if (spec.type == KERNAUX_PRINTF_FMT_TYPE_INT) {
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);
} 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));
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_CHAR) {
DynArg_use_char(&dynarg, *RSTRING_CSTR(mrb, arg_rb));
} else if (spec.type == KERNAUX_PRINTF_FMT_TYPE_STR) {
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");
int slen;
if (spec.set_width) {
if (spec.set_precision) {
slen = dynarg.use_dbl
? kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.dbl)
: kernaux_snprintf(str, size, format, spec.width, spec.precision, dynarg.arg);
} else {
slen = dynarg.use_dbl
? kernaux_snprintf(str, size, format, spec.width, dynarg.dbl)
: kernaux_snprintf(str, size, format, spec.width, dynarg.arg);
}
} else {
if (spec.set_precision) {
slen = dynarg.use_dbl
? kernaux_snprintf(str, size, format, spec.precision, dynarg.dbl)
: kernaux_snprintf(str, size, format, spec.precision, dynarg.arg);
} else {
slen = dynarg.use_dbl
? kernaux_snprintf(str, size, format, dynarg.dbl)
: kernaux_snprintf(str, size, format, dynarg.arg);
}
}
mrb_value output_rb =
mrb_obj_freeze(mrb, mrb_str_cat_cstr(mrb, mrb_str_new_lit(mrb, ""), str));
free(str);
mrb_value values[2];
values[0] = mrb_obj_freeze(mrb, mrb_str_new_lit(mrb, ""));
values[1] = mrb_nil_value();
values[0] = output_rb;
values[1] = mrb_fixnum_value(slen);
return mrb_obj_freeze(mrb, mrb_ary_new_from_values(mrb, 2, values));
}

View File

@ -12,7 +12,20 @@ assert 'KernAux.sprintf' do
YAML.load(File.read(printf_yml)).each do |test|
expected = test['result']
args = test['args']
args = test['args'].map do |arg|
if arg.is_a? String
arg
else
arg.map do |item|
if item.is_a? Array
item[0]
else
item
end
end
end
end
assert "transforms #{args.inspect} to #{expected.inspect}" do
assert_equal expected, KernAux.sprintf(*args)

View File

@ -19,7 +19,6 @@ void init_printf()
rb_KernAux_snprintf1, -1);
}
// TODO: is this implementation correct?
// FIXME: rewrite to ensure no memory leak on exception.
VALUE rb_KernAux_snprintf1(
const int argc,