10 #include "internal/hash.h"
11 #include "internal/variable.h"
16 #if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
17 # define INTPTR2NUM LL2NUM
18 # define UINTPTR2NUM ULL2NUM
19 #elif SIZEOF_INTPTR_T == SIZEOF_LONG
20 # define INTPTR2NUM LONG2NUM
21 # define UINTPTR2NUM ULONG2NUM
23 # define INTPTR2NUM INT2NUM
24 # define UINTPTR2NUM UINT2NUM
28 #define STRUCT_ALIGNOF(T, result) do { \
29 (result) = RUBY_ALIGNOF(T); \
34 static st_table *exported_object_table = NULL;
35 VALUE rb_memory_view_exported_object_registry =
Qundef;
38 exported_object_registry_mark_key_i(st_data_t key, st_data_t value, st_data_t data)
45 exported_object_registry_mark(
void *
ptr)
48 st_foreach(exported_object_table, exported_object_registry_mark_key_i, 0);
52 exported_object_registry_free(
void *
ptr)
55 st_clear(exported_object_table);
56 st_free_table(exported_object_table);
57 exported_object_table = NULL;
61 const rb_data_type_t rb_memory_view_exported_object_registry_data_type = {
62 "memory_view/exported_object_registry",
64 exported_object_registry_mark,
65 exported_object_registry_free,
68 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
72 exported_object_add_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
86 exported_object_dec_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
100 register_exported_object(
VALUE obj)
103 st_update(exported_object_table, (st_data_t)obj, exported_object_add_ref, 0);
108 unregister_exported_object(
VALUE obj)
111 if (exported_object_table)
112 st_update(exported_object_table, (st_data_t)obj, exported_object_dec_ref, 0);
118 static ID id_memory_view;
127 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
135 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
136 if (!
NIL_P(entry_obj)) {
137 rb_warning(
"Duplicated registration of memory view to %"PRIsVALUE, klass);
151 const ssize_t ndim = view->
ndim;
152 const ssize_t *shape = view->
shape;
153 const ssize_t *strides = view->
strides;
156 for (i = ndim - 1; i >= 0; --i) {
157 if (strides[i] != n)
return false;
167 const ssize_t ndim = view->
ndim;
168 const ssize_t *shape = view->
shape;
169 const ssize_t *strides = view->
strides;
172 for (i = 0; i < ndim; ++i) {
173 if (strides[i] != n)
return false;
183 ssize_t i, n = item_size;
185 for (i = ndim - 1; i >= 0; --i) {
191 for (i = 0; i < ndim; ++i) {
219 #ifdef HAVE_TRUE_LONG_LONG
220 static const char native_types[] =
"sSiIlLqQjJ";
222 static const char native_types[] =
"sSiIlLjJ";
224 static const char endianness_types[] =
"sSiIlLqQjJ";
233 get_format_size(
const char *format,
bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count,
const char **next_format,
VALUE *error)
242 *endianness = ENDIANNESS_NATIVE;
245 const int type_char = *format;
252 if (strchr(native_types, type_char)) {
259 rb_sprintf(
"Unable to specify native size for '%c'", type_char));
267 if (!strchr(endianness_types, type_char)) {
270 rb_sprintf(
"Unable to specify endianness for '%c'", type_char));
274 if (*endianness != ENDIANNESS_NATIVE) {
278 *endianness = (format[i] ==
'<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
291 if (
'0' <= ch && ch <=
'9') {
293 while (
'0' <= (ch = format[i]) && ch <=
'9') {
300 *next_format = &format[i];
313 STRUCT_ALIGNOF(
short, *alignment);
314 return sizeof(short);
320 STRUCT_ALIGNOF(int16_t, *alignment);
325 STRUCT_ALIGNOF(
int, *alignment);
331 STRUCT_ALIGNOF(
long, *alignment);
338 STRUCT_ALIGNOF(int32_t, *alignment);
344 STRUCT_ALIGNOF(
float, *alignment);
345 return sizeof(float);
353 STRUCT_ALIGNOF(int64_t, *alignment);
359 STRUCT_ALIGNOF(
double, *alignment);
360 return sizeof(double);
364 STRUCT_ALIGNOF(intptr_t, *alignment);
365 return sizeof(intptr_t);
376 static inline ssize_t
377 calculate_padding(ssize_t total, ssize_t alignment_size)
379 if (alignment_size > 1) {
380 ssize_t res = total % alignment_size;
382 return alignment_size - res;
391 size_t *n_members,
const char **err)
393 if (format == NULL)
return 1;
398 bool alignment =
false;
399 ssize_t max_alignment_size = 0;
401 const char *p = format;
416 bool native_size_p =
false;
417 ssize_t alignment_size = 0;
418 endianness_t endianness = ENDIANNESS_NATIVE;
420 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
425 if (max_alignment_size < alignment_size) {
426 max_alignment_size = alignment_size;
429 const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
430 total += padding + size * count;
438 if (alignment && max_alignment_size > 0) {
439 const ssize_t padding = calculate_padding(total, max_alignment_size);
443 if (members && n_members) {
446 ssize_t i = 0, offset = 0;
447 const char *p = format;
449 const int type_char = *p;
452 ssize_t alignment_size = 0;
453 endianness_t endianness = ENDIANNESS_NATIVE;
455 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
457 const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
460 if (type_char !=
'x') {
461 #ifdef WORDS_BIGENDIAN
462 bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
464 bool little_endian_p = (endianness != ENDIANNESS_BIG);
472 little_endian_p =
true;
478 little_endian_p =
false;
486 .native_size_p = native_size_p,
487 .little_endian_p = little_endian_p,
494 offset += size * count;
517 if (view->
ndim == 1) {
519 return ptr + indices[0] * stride;
522 assert(view->
shape != NULL);
528 for (i = 0; i < view->
ndim; ++i) {
529 stride *= view->
shape[i];
531 for (i = 0; i < view->
ndim; ++i) {
532 stride /= view->
shape[i];
533 ptr += indices[i] * stride;
538 for (i = 0; i < view->
ndim; ++i) {
544 for (i = 0; i < view->
ndim; ++i) {
556 switch_endianness(uint8_t *buf, ssize_t
len)
562 uint8_t *q = buf +
len - 1;
580 #ifdef WORDS_BIGENDIAN
588 if (member->
format ==
'c') {
591 else if (member->
format ==
'C') {
592 return INT2FIX(*(
unsigned char *)p);
616 if (!native_endian_p) {
618 switch_endianness((uint8_t *)&val, member->
size);
677 #if SIZEOF_INT64_T == SIZEOF_LONG
689 #if SIZEOF_UINT64_T == SIZEOF_LONG
702 return INTPTR2NUM(val.iptr);
705 return UINTPTR2NUM(val.uptr);
717 if (member == NULL)
return Qnil;
720 return extract_item_member(
ptr, member, i);
730 if (members == NULL)
return Qnil;
731 if (n_members == 0)
return Qnil;
733 if (n_members == 1 && members[0].repeat == 1) {
734 return rb_memory_view_extract_item_member(
ptr, members, 0);
739 for (i = 0; i < n_members; ++i) {
740 for (j = 0; j < members[i].
repeat; ++j) {
741 VALUE v = extract_item_member(
ptr, &members[i], j);
759 "Unable to parse item format at %"PRIdSIZE
" in \"%s\"",
771 if (view->
format == NULL) {
783 lookup_memory_view_entry(
VALUE klass)
785 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
786 while (
NIL_P(entry_obj)) {
792 entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
824 bool rv = (*entry->
get_func)(obj, view, flags);
827 register_exported_object(view->
obj);
846 unregister_exported_object(view->
obj);
857 Init_MemoryView(
void)
859 exported_object_table = rb_init_identtable();
864 0, &rb_memory_view_exported_object_registry_data_type,
865 exported_object_table);
866 rb_vm_register_global_object(obj);
867 rb_memory_view_exported_object_registry = obj;
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define ISSPACE
Old name of rb_isspace.
#define xfree
Old name of ruby_xfree.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define ULONG2NUM
Old name of RB_ULONG2NUM.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define LL2NUM
Old name of RB_LL2NUM.
#define CLASS_OF
Old name of rb_class_of.
#define ALLOC_N
Old name of RB_ALLOC_N.
#define LONG2NUM
Old name of RB_LONG2NUM.
#define ULL2NUM
Old name of RB_ULL2NUM.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define NIL_P
Old name of RB_NIL_P.
#define DBL2NUM
Old name of rb_float_new.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define UINT2NUM
Old name of RB_UINT2NUM.
void rb_raise(VALUE exc_class, const char *fmt,...)
Exception entry point.
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
VALUE rb_eArgError
ArgumentError exception.
void rb_warning(const char *fmt,...)
Issues a warning.
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
VALUE rb_cBasicObject
BasicObject class.
void rb_gc_mark(VALUE obj)
Marks an object.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
int len
Length of the buffer.
const signed char ruby_digit36_to_number_table[]
Character to number mapping like ‘'a’->10,'b'->11` etc.
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
void * rb_memory_view_get_item_pointer(rb_memory_view_t *view, const ssize_t *indices)
Calculate the location of the item indicated by the given indices.
bool rb_memory_view_is_row_major_contiguous(const rb_memory_view_t *view)
Return true if the data in the MemoryView view is row-major contiguous.
bool rb_memory_view_get(VALUE obj, rb_memory_view_t *memory_view, int flags)
If the given obj supports to export a MemoryView that conforms the given flags, this function fills v...
VALUE rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members)
Return a value that consists of item members.
ssize_t rb_memory_view_item_size_from_format(const char *format, const char **err)
Calculate the number of bytes occupied by an element.
bool rb_memory_view_release(rb_memory_view_t *memory_view)
Release the given MemoryView view and decrement the reference count of memory_view->obj.
void rb_memory_view_fill_contiguous_strides(const ssize_t ndim, const ssize_t item_size, const ssize_t *const shape, const bool row_major_p, ssize_t *const strides)
Fill the strides array with byte-Strides of a contiguous array of the given shape with the given elem...
bool rb_memory_view_available_p(VALUE obj)
Return true if obj supports to export a MemoryView.
bool rb_memory_view_is_column_major_contiguous(const rb_memory_view_t *view)
Return true if the data in the MemoryView view is column-major contiguous.
bool rb_memory_view_register(VALUE klass, const rb_memory_view_entry_t *entry)
Associates the passed class with the passed memory view entry.
bool rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly)
Fill the members of view as an 1-dimensional byte array.
VALUE rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices)
ssize_t rb_memory_view_parse_item_format(const char *format, rb_memory_view_item_component_t **members, size_t *n_members, const char **err)
Deconstructs the passed format string, as describe in rb_memory_view_t::format.
void rb_memory_view_prepare_item_desc(rb_memory_view_t *view)
Fill the item_desc member of view.
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
This is the struct that holds necessary info for a struct.
Operations applied to a specific kind of a memory view.
rb_memory_view_get_func_t get_func
Exports a memory view from a Ruby object.
rb_memory_view_available_p_func_t available_p_func
Queries if an object understands memory view protocol.
rb_memory_view_release_func_t release_func
Releases a memory view that was previously generated using rb_memory_view_entry_t::get_func.
Memory view component metadata.
bool native_size_p
:FIXME: what is a "native" size is unclear.
size_t size
The component's size.
size_t repeat
How many numbers of components are there.
size_t offset
The component's offset.
bool little_endian_p
Endian of the component.
A MemoryView structure, rb_memory_view_t, is used for exporting objects' MemoryView.
const rb_memory_view_item_component_t * components
The array of rb_memory_view_item_component_t that describes the item structure.
ssize_t item_size
The number of bytes in each element.
const ssize_t * strides
ndim size array indicating the number of bytes to skip to go to the next element in each dimension.
ssize_t ndim
The number of dimension.
const struct rb_memory_view_entry * _memory_view_entry
DO NOT TOUCH THIS: The memory view entry for the internal use.
void * data
The pointer to the exported memory.
VALUE obj
The original object that has the memory exported via this memory view.
struct rb_memory_view_t::@58 item_desc
Description of each components.
const ssize_t * sub_offsets
The offset in each dimension when this memory view exposes a nested array.
const ssize_t * shape
ndim size array indicating the number of elements in each dimension.
void * private_data
The private data for managing this exported memory.
size_t length
The number of components in an item.
const char * format
A string to describe the format of an element, or NULL for unsigned bytes.
ssize_t byte_size
The number of bytes in data.
bool readonly
true for readonly memory, false for writable memory.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.