mirror of
https://github.com/tailix/libkernaux.git
synced 2025-02-17 15:45:32 -05:00
mruby: implement KernAux.snprintf1 (with some bug)
This commit is contained in:
parent
081dc4019b
commit
7698d0f3c6
5 changed files with 193 additions and 4 deletions
45
pkgs/mruby/src/dynarg.c
Normal file
45
pkgs/mruby/src/dynarg.c
Normal 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
35
pkgs/mruby/src/dynarg.h
Normal 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
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Reference in a new issue