diff options
Diffstat (limited to 'ext/-test-/memory_view/memory_view.c')
| -rw-r--r-- | ext/-test-/memory_view/memory_view.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/ext/-test-/memory_view/memory_view.c b/ext/-test-/memory_view/memory_view.c new file mode 100644 index 0000000000..63f0beb81e --- /dev/null +++ b/ext/-test-/memory_view/memory_view.c @@ -0,0 +1,450 @@ +#include "ruby.h" + +#ifdef HAVE_RUBY_MEMORY_VIEW_H +#include "ruby/memory_view.h" + +#define STRUCT_ALIGNOF(T, result) do { \ + (result) = RUBY_ALIGNOF(T); \ +} while(0) + +static ID id_str; +static VALUE sym_format; +static VALUE sym_native_size_p; +static VALUE sym_offset; +static VALUE sym_size; +static VALUE sym_repeat; +static VALUE sym_obj; +static VALUE sym_byte_size; +static VALUE sym_readonly; +static VALUE sym_format; +static VALUE sym_item_size; +static VALUE sym_ndim; +static VALUE sym_shape; +static VALUE sym_strides; +static VALUE sym_sub_offsets; +static VALUE sym_endianness; +static VALUE sym_little_endian; +static VALUE sym_big_endian; + +static bool +exportable_string_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags) +{ + VALUE str = rb_ivar_get(obj, id_str); + rb_memory_view_init_as_byte_array(view, obj, RSTRING_PTR(str), RSTRING_LEN(str), true); + return true; +} + +static bool +exportable_string_memory_view_available_p(VALUE obj) +{ + VALUE str = rb_ivar_get(obj, id_str); + return !NIL_P(str); +} + +static const rb_memory_view_entry_t exportable_string_memory_view_entry = { + exportable_string_get_memory_view, + NULL, + exportable_string_memory_view_available_p +}; + +static VALUE +memory_view_available_p(VALUE mod, VALUE obj) +{ + return rb_memory_view_available_p(obj) ? Qtrue : Qfalse; +} + +static VALUE +memory_view_register(VALUE mod, VALUE obj) +{ + return rb_memory_view_register(obj, &exportable_string_memory_view_entry) ? Qtrue : Qfalse; +} + +static VALUE +memory_view_item_size_from_format(VALUE mod, VALUE format) +{ + const char *c_str = NULL; + if (!NIL_P(format)) + c_str = StringValueCStr(format); + const char *err = NULL; + ssize_t item_size = rb_memory_view_item_size_from_format(c_str, &err); + if (!err) + return rb_assoc_new(SSIZET2NUM(item_size), Qnil); + else + return rb_assoc_new(SSIZET2NUM(item_size), rb_str_new_cstr(err)); +} + +static VALUE +memory_view_parse_item_format(VALUE mod, VALUE format) +{ + const char *c_str = NULL; + if (!NIL_P(format)) + c_str = StringValueCStr(format); + const char *err = NULL; + + rb_memory_view_item_component_t *members; + size_t n_members; + ssize_t item_size = rb_memory_view_parse_item_format(c_str, &members, &n_members, &err); + + VALUE result = rb_ary_new_capa(3); + rb_ary_push(result, SSIZET2NUM(item_size)); + + if (!err) { + VALUE ary = rb_ary_new_capa((long)n_members); + size_t i; + for (i = 0; i < n_members; ++i) { + VALUE member = rb_hash_new(); + rb_hash_aset(member, sym_format, rb_str_new(&members[i].format, 1)); + rb_hash_aset(member, sym_native_size_p, members[i].native_size_p ? Qtrue : Qfalse); + rb_hash_aset(member, sym_endianness, members[i].little_endian_p ? sym_little_endian : sym_big_endian); + rb_hash_aset(member, sym_offset, SSIZET2NUM(members[i].offset)); + rb_hash_aset(member, sym_size, SSIZET2NUM(members[i].size)); + rb_hash_aset(member, sym_repeat, SSIZET2NUM(members[i].repeat)); + rb_ary_push(ary, member); + } + xfree(members); + rb_ary_push(result, ary); + rb_ary_push(result, Qnil); + } + else { + rb_ary_push(result, Qnil); // members + rb_ary_push(result, rb_str_new_cstr(err)); + } + + return result; +} + +static VALUE +memory_view_get_memory_view_info(VALUE mod, VALUE obj) +{ + rb_memory_view_t view; + + if (!rb_memory_view_get(obj, &view, 0)) { + return Qnil; + } + + VALUE hash = rb_hash_new(); + rb_hash_aset(hash, sym_obj, view.obj); + rb_hash_aset(hash, sym_byte_size, SSIZET2NUM(view.byte_size)); + rb_hash_aset(hash, sym_readonly, view.readonly ? Qtrue : Qfalse); + rb_hash_aset(hash, sym_format, view.format ? rb_str_new_cstr(view.format) : Qnil); + rb_hash_aset(hash, sym_item_size, SSIZET2NUM(view.item_size)); + rb_hash_aset(hash, sym_ndim, SSIZET2NUM(view.ndim)); + + if (view.shape) { + VALUE shape = rb_ary_new_capa(view.ndim); + rb_hash_aset(hash, sym_shape, shape); + } + else { + rb_hash_aset(hash, sym_shape, Qnil); + } + + if (view.strides) { + VALUE strides = rb_ary_new_capa(view.ndim); + rb_hash_aset(hash, sym_strides, strides); + } + else { + rb_hash_aset(hash, sym_strides, Qnil); + } + + if (view.sub_offsets) { + VALUE sub_offsets = rb_ary_new_capa(view.ndim); + rb_hash_aset(hash, sym_sub_offsets, sub_offsets); + } + else { + rb_hash_aset(hash, sym_sub_offsets, Qnil); + } + + rb_memory_view_release(&view); + + return hash; +} + +static VALUE +memory_view_fill_contiguous_strides(VALUE mod, VALUE ndim_v, VALUE item_size_v, VALUE shape_v, VALUE row_major_p) +{ + ssize_t i, ndim = NUM2SSIZET(ndim_v); + + Check_Type(shape_v, T_ARRAY); + ssize_t *shape = ALLOC_N(ssize_t, ndim); + for (i = 0; i < ndim; ++i) { + shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i)); + } + + ssize_t *strides = ALLOC_N(ssize_t, ndim); + rb_memory_view_fill_contiguous_strides(ndim, NUM2SSIZET(item_size_v), shape, RTEST(row_major_p), strides); + + VALUE result = rb_ary_new_capa(ndim); + for (i = 0; i < ndim; ++i) { + rb_ary_push(result, SSIZET2NUM(strides[i])); + } + + xfree(strides); + xfree(shape); + + return result; +} + +static VALUE +memory_view_get_ref_count(VALUE obj) +{ + if (rb_memory_view_exported_object_registry == Qundef) { + return Qnil; + } + + st_table *table; + TypedData_Get_Struct(rb_memory_view_exported_object_registry, st_table, + &rb_memory_view_exported_object_registry_data_type, + table); + + st_data_t count; + if (st_lookup(table, (st_data_t)obj, &count)) { + return ULL2NUM(count); + } + + return Qnil; +} + +static VALUE +memory_view_ref_count_while_exporting_i(VALUE obj, long n) +{ + if (n == 0) { + return memory_view_get_ref_count(obj); + } + + rb_memory_view_t view; + if (!rb_memory_view_get(obj, &view, 0)) { + return Qnil; + } + + VALUE ref_count = memory_view_ref_count_while_exporting_i(obj, n-1); + rb_memory_view_release(&view); + + return ref_count; +} + +static VALUE +memory_view_ref_count_while_exporting(VALUE mod, VALUE obj, VALUE n) +{ + Check_Type(n, T_FIXNUM); + return memory_view_ref_count_while_exporting_i(obj, FIX2LONG(n)); +} + +static VALUE +memory_view_extract_item_members(VALUE mod, VALUE str, VALUE format) +{ + StringValue(str); + StringValue(format); + + rb_memory_view_item_component_t *members; + size_t n_members; + const char *err = NULL; + (void)rb_memory_view_parse_item_format(RSTRING_PTR(format), &members, &n_members, &err); + if (err != NULL) { + rb_raise(rb_eArgError, "Unable to parse item format"); + } + + VALUE item = rb_memory_view_extract_item_members(RSTRING_PTR(str), members, n_members); + xfree(members); + + return item; +} + +static VALUE +expstr_initialize(VALUE obj, VALUE s) +{ + if (!NIL_P(s)) { + Check_Type(s, T_STRING); + } + rb_ivar_set(obj, id_str, s); + return Qnil; +} + +static bool +mdview_get_memory_view(VALUE obj, rb_memory_view_t *view, int flags) +{ + VALUE buf_v = rb_ivar_get(obj, id_str); + VALUE format_v = rb_ivar_get(obj, SYM2ID(sym_format)); + VALUE shape_v = rb_ivar_get(obj, SYM2ID(sym_shape)); + VALUE strides_v = rb_ivar_get(obj, SYM2ID(sym_strides)); + + const char *err; + const ssize_t item_size = rb_memory_view_item_size_from_format(RSTRING_PTR(format_v), &err); + if (item_size < 0) { + return false; + } + + ssize_t ndim = RARRAY_LEN(shape_v); + if (!NIL_P(strides_v) && RARRAY_LEN(strides_v) != ndim) { + rb_raise(rb_eArgError, "strides has an invalid dimension"); + } + + ssize_t *shape = ALLOC_N(ssize_t, ndim); + ssize_t *strides = ALLOC_N(ssize_t, ndim); + ssize_t i; + if (!NIL_P(strides_v)) { + for (i = 0; i < ndim; ++i) { + shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i)); + strides[i] = NUM2SSIZET(RARRAY_AREF(strides_v, i)); + } + } + else { + for (i = 0; i < ndim; ++i) { + shape[i] = NUM2SSIZET(RARRAY_AREF(shape_v, i)); + } + + i = ndim - 1; + strides[i] = item_size; + for (; i > 0; --i) { + strides[i-1] = strides[i] * shape[i]; + } + } + + rb_memory_view_init_as_byte_array(view, obj, RSTRING_PTR(buf_v), RSTRING_LEN(buf_v), true); + view->format = RSTRING_PTR(format_v); + view->item_size = item_size; + view->ndim = ndim; + view->shape = shape; + view->strides = strides; + view->sub_offsets = NULL; + + return true; +} + +static bool +mdview_release_memory_view(VALUE obj, rb_memory_view_t *view) +{ + xfree((void *)view->shape); + xfree((void *)view->strides); + + return true; +} + +static bool +mdview_memory_view_available_p(VALUE obj) +{ + return true; +} + +static const rb_memory_view_entry_t mdview_memory_view_entry = { + mdview_get_memory_view, + mdview_release_memory_view, + mdview_memory_view_available_p +}; + +static VALUE +mdview_initialize(VALUE obj, VALUE buf, VALUE format, VALUE shape, VALUE strides) +{ + Check_Type(buf, T_STRING); + StringValue(format); + Check_Type(shape, T_ARRAY); + if (!NIL_P(strides)) Check_Type(strides, T_ARRAY); + + rb_ivar_set(obj, id_str, buf); + rb_ivar_set(obj, SYM2ID(sym_format), format); + rb_ivar_set(obj, SYM2ID(sym_shape), shape); + rb_ivar_set(obj, SYM2ID(sym_strides), strides); + return Qnil; +} + +static VALUE +mdview_aref(VALUE obj, VALUE indices_v) +{ + Check_Type(indices_v, T_ARRAY); + + rb_memory_view_t view; + if (!rb_memory_view_get(obj, &view, 0)) { + rb_raise(rb_eRuntimeError, "rb_memory_view_get: failed"); + } + + if (RARRAY_LEN(indices_v) != view.ndim) { + rb_raise(rb_eKeyError, "Indices has an invalid dimension"); + } + + VALUE buf_indices; + ssize_t *indices = ALLOCV_N(ssize_t, buf_indices, view.ndim); + + ssize_t i; + for (i = 0; i < view.ndim; ++i) { + indices[i] = NUM2SSIZET(RARRAY_AREF(indices_v, i)); + } + + VALUE result = rb_memory_view_get_item(&view, indices); + ALLOCV_END(buf_indices); + rb_memory_view_release(&view); + + return result; +} + +#endif /* HAVE_RUBY_MEMORY_VIEW_H */ + +void +Init_memory_view(void) +{ + rb_ext_ractor_safe(true); +#ifdef HAVE_RUBY_MEMORY_VIEW_H + VALUE mMemoryViewTestUtils = rb_define_module("MemoryViewTestUtils"); + + rb_define_module_function(mMemoryViewTestUtils, "available?", memory_view_available_p, 1); + rb_define_module_function(mMemoryViewTestUtils, "register", memory_view_register, 1); + rb_define_module_function(mMemoryViewTestUtils, "item_size_from_format", memory_view_item_size_from_format, 1); + rb_define_module_function(mMemoryViewTestUtils, "parse_item_format", memory_view_parse_item_format, 1); + rb_define_module_function(mMemoryViewTestUtils, "get_memory_view_info", memory_view_get_memory_view_info, 1); + rb_define_module_function(mMemoryViewTestUtils, "fill_contiguous_strides", memory_view_fill_contiguous_strides, 4); + rb_define_module_function(mMemoryViewTestUtils, "ref_count_while_exporting", memory_view_ref_count_while_exporting, 2); + rb_define_module_function(mMemoryViewTestUtils, "extract_item_members", memory_view_extract_item_members, 2); + + VALUE cExportableString = rb_define_class_under(mMemoryViewTestUtils, "ExportableString", rb_cObject); + rb_define_method(cExportableString, "initialize", expstr_initialize, 1); + rb_memory_view_register(cExportableString, &exportable_string_memory_view_entry); + + VALUE cMDView = rb_define_class_under(mMemoryViewTestUtils, "MultiDimensionalView", rb_cObject); + rb_define_method(cMDView, "initialize", mdview_initialize, 4); + rb_define_method(cMDView, "[]", mdview_aref, 1); + rb_memory_view_register(cMDView, &mdview_memory_view_entry); + + id_str = rb_intern_const("__str__"); + sym_format = ID2SYM(rb_intern_const("format")); + sym_native_size_p = ID2SYM(rb_intern_const("native_size_p")); + sym_offset = ID2SYM(rb_intern_const("offset")); + sym_size = ID2SYM(rb_intern_const("size")); + sym_repeat = ID2SYM(rb_intern_const("repeat")); + sym_obj = ID2SYM(rb_intern_const("obj")); + sym_byte_size = ID2SYM(rb_intern_const("byte_size")); + sym_readonly = ID2SYM(rb_intern_const("readonly")); + sym_format = ID2SYM(rb_intern_const("format")); + sym_item_size = ID2SYM(rb_intern_const("item_size")); + sym_ndim = ID2SYM(rb_intern_const("ndim")); + sym_shape = ID2SYM(rb_intern_const("shape")); + sym_strides = ID2SYM(rb_intern_const("strides")); + sym_sub_offsets = ID2SYM(rb_intern_const("sub_offsets")); + sym_endianness = ID2SYM(rb_intern_const("endianness")); + sym_little_endian = ID2SYM(rb_intern_const("little_endian")); + sym_big_endian = ID2SYM(rb_intern_const("big_endian")); + +#ifdef WORDS_BIGENDIAN + rb_const_set(mMemoryViewTestUtils, rb_intern_const("NATIVE_ENDIAN"), sym_big_endian); +#else + rb_const_set(mMemoryViewTestUtils, rb_intern_const("NATIVE_ENDIAN"), sym_little_endian); +#endif + +#define DEF_ALIGNMENT_CONST(type, TYPE) do { \ + int alignment; \ + STRUCT_ALIGNOF(type, alignment); \ + rb_const_set(mMemoryViewTestUtils, rb_intern_const(#TYPE "_ALIGNMENT"), INT2FIX(alignment)); \ +} while(0) + + DEF_ALIGNMENT_CONST(short, SHORT); + DEF_ALIGNMENT_CONST(int, INT); + DEF_ALIGNMENT_CONST(long, LONG); + DEF_ALIGNMENT_CONST(LONG_LONG, LONG_LONG); + DEF_ALIGNMENT_CONST(int16_t, INT16); + DEF_ALIGNMENT_CONST(int32_t, INT32); + DEF_ALIGNMENT_CONST(int64_t, INT64); + DEF_ALIGNMENT_CONST(intptr_t, INTPTR); + DEF_ALIGNMENT_CONST(float, FLOAT); + DEF_ALIGNMENT_CONST(double, DOUBLE); + +#undef DEF_ALIGNMENT_CONST + +#endif /* HAVE_RUBY_MEMORY_VIEW_H */ +} |
