mirror of
https://github.com/tailix/libkernaux.git
synced 2024-11-13 11:04:27 -05:00
Ruby: move printf to separate extension file
This commit is contained in:
parent
f9d4a37165
commit
f7476784ce
2 changed files with 143 additions and 124 deletions
|
@ -13,6 +13,9 @@ static VALUE rb_KernAux_Error = Qnil;
|
|||
|
||||
static void assert_cb(const char *file, int line, const char *str);
|
||||
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
void init_printf();
|
||||
#endif // HAVE_KERNAUX_SNPRINTF
|
||||
#ifdef HAVE_KERNAUX_CMDLINE
|
||||
void init_cmdline();
|
||||
#endif // HAVE_KERNAUX_CMDLINE
|
||||
|
@ -28,12 +31,12 @@ static VALUE rb_KernAux_utoa10(VALUE self, VALUE number);
|
|||
#ifdef HAVE_KERNAUX_ITOA10
|
||||
static VALUE rb_KernAux_itoa10(VALUE self, VALUE number);
|
||||
#endif
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
static VALUE rb_KernAux_snprintf1(int argc, const VALUE *argv, VALUE self);
|
||||
#endif
|
||||
|
||||
void Init_default()
|
||||
{
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
init_printf();
|
||||
#endif // HAVE_KERNAUX_SNPRINTF
|
||||
#ifdef HAVE_KERNAUX_CMDLINE
|
||||
init_cmdline();
|
||||
#endif // HAVE_KERNAUX_CMDLINE
|
||||
|
@ -62,10 +65,6 @@ void Init_default()
|
|||
#ifdef HAVE_KERNAUX_ITOA10
|
||||
rb_define_singleton_method(rb_KernAux, "itoa10", rb_KernAux_itoa10, 1);
|
||||
#endif
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
rb_define_singleton_method(rb_KernAux, "snprintf1",
|
||||
rb_KernAux_snprintf1, -1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void assert_cb(const char *const file, const int line, const char *const str)
|
||||
|
@ -129,120 +128,3 @@ VALUE rb_KernAux_itoa10(
|
|||
return rb_funcall(rb_str_new2(buffer), rb_intern_freeze, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
// TODO: is this implementation correct?
|
||||
// FIXME: rewrite to ensure no memory leak on exception.
|
||||
VALUE rb_KernAux_snprintf1(
|
||||
const int argc,
|
||||
const VALUE *const argv_rb,
|
||||
const VALUE self __attribute__((unused))
|
||||
) {
|
||||
if (argc != 2 && argc != 3) rb_raise(rb_eArgError, "expected 2 or 3 args");
|
||||
|
||||
const VALUE size_rb = argv_rb[0];
|
||||
VALUE format_rb = argv_rb[1];
|
||||
|
||||
const int size = NUM2INT(size_rb);
|
||||
const char *const format = StringValueCStr(format_rb);
|
||||
|
||||
if (size < 0) rb_raise(rb_eRangeError, "expected non-negative size");
|
||||
|
||||
const char *fmt = format;
|
||||
|
||||
while (*fmt && *fmt != '%') ++fmt;
|
||||
if (*(fmt++) != '%') rb_raise(rb_eArgError, "invalid format");
|
||||
|
||||
// Mimic printf behavior.
|
||||
{
|
||||
bool flags;
|
||||
do {
|
||||
if (*fmt == '0' || *fmt == '-' || *fmt == '+' || *fmt == ' ' ||
|
||||
*fmt == '#')
|
||||
{
|
||||
++fmt;
|
||||
flags = true;
|
||||
} else {
|
||||
flags = false;
|
||||
}
|
||||
} while (flags);
|
||||
}
|
||||
if (*fmt >= '0' && *fmt <= '9') {
|
||||
while (*fmt >= '0' && *fmt <= '9') ++fmt;
|
||||
} else if (*fmt == '*') {
|
||||
++fmt;
|
||||
}
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
if (*fmt >= '0' && *fmt <= '9') {
|
||||
while (*fmt >= '0' && *fmt <= '9') ++fmt;
|
||||
} else if (*fmt == '*') {
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
if (*fmt == 'l') {
|
||||
++fmt;
|
||||
if (*fmt == 'l') ++fmt;
|
||||
} else if (*fmt == 'h') {
|
||||
++fmt;
|
||||
if (*fmt == 'h') ++fmt;
|
||||
} else if (*fmt == 't' || *fmt == 'j' || *fmt == 'z') {
|
||||
++fmt;
|
||||
}
|
||||
|
||||
const char c = *fmt;
|
||||
|
||||
if (*fmt == '%') ++fmt;
|
||||
while (*fmt) {
|
||||
if (*(fmt++) == '%') rb_raise(rb_eArgError, "invalid format");
|
||||
}
|
||||
|
||||
bool use_dbl = false;
|
||||
double dbl;
|
||||
union {
|
||||
const char *str;
|
||||
long long ll;
|
||||
unsigned long long ull;
|
||||
char chr;
|
||||
} __attribute__((packed)) arg = { .str = "" };
|
||||
|
||||
if (argc == 3) {
|
||||
VALUE arg_rb = argv_rb[2];
|
||||
|
||||
if (c == 'd' || c == 'i') {
|
||||
RB_INTEGER_TYPE_P(arg_rb);
|
||||
arg.ll = NUM2LL(arg_rb);
|
||||
} else if (c == 'u' || c == 'x' || c == 'X' || c == 'o' || c == 'b') {
|
||||
RB_INTEGER_TYPE_P(arg_rb);
|
||||
arg.ull = NUM2ULL(arg_rb);
|
||||
} else if (c == 'f' || c == 'F' ||
|
||||
c == 'e' || c == 'E' ||
|
||||
c == 'g' || c == 'G')
|
||||
{
|
||||
RB_FLOAT_TYPE_P(arg_rb);
|
||||
use_dbl = true;
|
||||
dbl = NUM2DBL(arg_rb);
|
||||
} else if (c == 'c') {
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
arg.chr = *StringValuePtr(arg_rb);
|
||||
} else if (c == 's') {
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
arg.str = StringValueCStr(arg_rb);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
const VALUE output_rb =
|
||||
rb_funcall(rb_str_new2(str), rb_intern_freeze, 0);
|
||||
free(str);
|
||||
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
|
137
pkgs/ruby/ext/default/printf.c
Normal file
137
pkgs/ruby/ext/default/printf.c
Normal file
|
@ -0,0 +1,137 @@
|
|||
#include <kernaux.h>
|
||||
#include <ruby.h>
|
||||
|
||||
#ifdef HAVE_KERNAUX_SNPRINTF
|
||||
|
||||
static VALUE rb_KernAux_snprintf1(int argc, const VALUE *argv, VALUE self);
|
||||
|
||||
static ID rb_intern_freeze;
|
||||
|
||||
static VALUE rb_KernAux = Qnil;
|
||||
|
||||
void init_printf()
|
||||
{
|
||||
rb_gc_register_mark_object(rb_intern_freeze = rb_intern("freeze"));
|
||||
|
||||
rb_gc_register_mark_object(rb_KernAux = rb_define_module("KernAux"));
|
||||
|
||||
rb_define_singleton_method(rb_KernAux, "snprintf1",
|
||||
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,
|
||||
const VALUE *const argv_rb,
|
||||
const VALUE self __attribute__((unused))
|
||||
) {
|
||||
if (argc != 2 && argc != 3) rb_raise(rb_eArgError, "expected 2 or 3 args");
|
||||
|
||||
const VALUE size_rb = argv_rb[0];
|
||||
VALUE format_rb = argv_rb[1];
|
||||
|
||||
const int size = NUM2INT(size_rb);
|
||||
const char *const format = StringValueCStr(format_rb);
|
||||
|
||||
if (size < 0) rb_raise(rb_eRangeError, "expected non-negative size");
|
||||
|
||||
const char *fmt = format;
|
||||
|
||||
while (*fmt && *fmt != '%') ++fmt;
|
||||
if (*(fmt++) != '%') rb_raise(rb_eArgError, "invalid format");
|
||||
|
||||
// Mimic printf behavior.
|
||||
{
|
||||
bool flags;
|
||||
do {
|
||||
if (*fmt == '0' || *fmt == '-' || *fmt == '+' || *fmt == ' ' ||
|
||||
*fmt == '#')
|
||||
{
|
||||
++fmt;
|
||||
flags = true;
|
||||
} else {
|
||||
flags = false;
|
||||
}
|
||||
} while (flags);
|
||||
}
|
||||
if (*fmt >= '0' && *fmt <= '9') {
|
||||
while (*fmt >= '0' && *fmt <= '9') ++fmt;
|
||||
} else if (*fmt == '*') {
|
||||
++fmt;
|
||||
}
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
if (*fmt >= '0' && *fmt <= '9') {
|
||||
while (*fmt >= '0' && *fmt <= '9') ++fmt;
|
||||
} else if (*fmt == '*') {
|
||||
++fmt;
|
||||
}
|
||||
}
|
||||
if (*fmt == 'l') {
|
||||
++fmt;
|
||||
if (*fmt == 'l') ++fmt;
|
||||
} else if (*fmt == 'h') {
|
||||
++fmt;
|
||||
if (*fmt == 'h') ++fmt;
|
||||
} else if (*fmt == 't' || *fmt == 'j' || *fmt == 'z') {
|
||||
++fmt;
|
||||
}
|
||||
|
||||
const char c = *fmt;
|
||||
|
||||
if (*fmt == '%') ++fmt;
|
||||
while (*fmt) {
|
||||
if (*(fmt++) == '%') rb_raise(rb_eArgError, "invalid format");
|
||||
}
|
||||
|
||||
bool use_dbl = false;
|
||||
double dbl;
|
||||
union {
|
||||
const char *str;
|
||||
long long ll;
|
||||
unsigned long long ull;
|
||||
char chr;
|
||||
} __attribute__((packed)) arg = { .str = "" };
|
||||
|
||||
if (argc == 3) {
|
||||
VALUE arg_rb = argv_rb[2];
|
||||
|
||||
if (c == 'd' || c == 'i') {
|
||||
RB_INTEGER_TYPE_P(arg_rb);
|
||||
arg.ll = NUM2LL(arg_rb);
|
||||
} else if (c == 'u' || c == 'x' || c == 'X' || c == 'o' || c == 'b') {
|
||||
RB_INTEGER_TYPE_P(arg_rb);
|
||||
arg.ull = NUM2ULL(arg_rb);
|
||||
} else if (c == 'f' || c == 'F' ||
|
||||
c == 'e' || c == 'E' ||
|
||||
c == 'g' || c == 'G')
|
||||
{
|
||||
RB_FLOAT_TYPE_P(arg_rb);
|
||||
use_dbl = true;
|
||||
dbl = NUM2DBL(arg_rb);
|
||||
} else if (c == 'c') {
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
arg.chr = *StringValuePtr(arg_rb);
|
||||
} else if (c == 's') {
|
||||
Check_Type(arg_rb, T_STRING);
|
||||
arg.str = StringValueCStr(arg_rb);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
const VALUE output_rb =
|
||||
rb_funcall(rb_str_new2(str), rb_intern_freeze, 0);
|
||||
free(str);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#endif // HAVE_KERNAUX_SNPRINTF
|
Loading…
Reference in a new issue