HEX
Server: Apache
System: Linux s198.coreserver.jp 5.15.0-151-generic #161-Ubuntu SMP Tue Jul 22 14:25:40 UTC 2025 x86_64
User: nagasaki (10062)
PHP: 7.1.33
Disabled: NONE
Upload Files
File: //usr/local/rvm/src/ruby-3.0.2/ext/-test-/memory_view/memory_view.c
#include "ruby.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)
{
    extern VALUE rb_memory_view_exported_object_registry;
    extern const rb_data_type_t rb_memory_view_exported_object_registry_data_type;

    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)
{
    if (view->shape) xfree((void *)view->shape);
    if (view->strides) 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;
}

void
Init_memory_view(void)
{
    rb_ext_ractor_safe(true);
    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
}