Ruby: protect from memory leak in case of exception

This commit is contained in:
Alex Kotov 2022-06-14 13:34:36 +03:00
parent 8cbbba15b2
commit 88469daa71
Signed by: kotovalexarian
GPG Key ID: 553C0EBBEB5D5F08
2 changed files with 124 additions and 20 deletions

View File

@ -2,6 +2,7 @@
#include "dynarg.h"
#include <stddef.h>
#include <stdlib.h>
#include <mruby/array.h>

View File

@ -2,23 +2,77 @@
#include "dynarg.h"
#include <stddef.h>
#include <stdlib.h>
#ifdef KERNAUX_VERSION_WITH_PRINTF
static ID rb_intern_freeze = Qnil;
/*************
* ::KernAux *
*************/
static VALUE rb_KernAux = Qnil;
static VALUE rb_KernAux_snprintf1(int argc, const VALUE *argv, VALUE self);
static ID rb_intern_freeze = Qnil;
static VALUE rb_KernAux = Qnil;
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 *
********/
void init_printf()
{
rb_gc_register_mark_object(ID2SYM(rb_intern_freeze = rb_intern("freeze")));
rb_gc_register_mark_object(rb_KernAux = rb_define_module("KernAux"));
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);
}
// FIXME: rewrite to ensure no memory leak on exception.
/*************
* ::KernAux *
*************/
VALUE rb_KernAux_snprintf1(
const int argc,
const VALUE *const argv_rb,
@ -82,32 +136,72 @@ VALUE rb_KernAux_snprintf1(
char *const str = malloc(size);
if (!str) rb_raise(rb_eNoMemError, "snprintf1 buffer malloc");
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");
}
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 (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);
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);
} else {
slen = dynarg.use_dbl
? kernaux_snprintf(str, size, format, spec.width, dynarg.dbl)
: kernaux_snprintf(str, size, format, spec.width, dynarg.arg);
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 (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);
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 = dynarg.use_dbl
? kernaux_snprintf(str, size, format, dynarg.dbl)
: kernaux_snprintf(str, size, format, dynarg.arg);
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);
}
}
const VALUE output_rb =
rb_funcall(rb_str_new2(str), rb_intern_freeze, 0);
free(str);
rb_funcall(rb_str_new2(userdata->str), rb_intern_freeze, 0);
const VALUE result_rb = rb_ary_new2(2);
rb_ary_push(result_rb, output_rb);
@ -115,4 +209,13 @@ VALUE rb_KernAux_snprintf1(
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);
}
#endif // KERNAUX_VERSION_WITH_PRINTF