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); \
34static st_table *exported_object_table = NULL;
35VALUE rb_memory_view_exported_object_registry =
Qundef;
38exported_object_registry_mark_key_i(st_data_t key, st_data_t value, st_data_t data)
45exported_object_registry_mark(
void *ptr)
48 st_foreach(exported_object_table, exported_object_registry_mark_key_i, 0);
52exported_object_registry_free(
void *ptr)
55 st_clear(exported_object_table);
56 st_free_table(exported_object_table);
57 exported_object_table = NULL;
61const 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,
72exported_object_add_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
86exported_object_dec_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
100register_exported_object(
VALUE obj)
103 st_update(exported_object_table, (st_data_t)obj, exported_object_add_ref, 0);
109unregister_exported_object(
VALUE obj)
112 if (exported_object_table)
113 st_update(exported_object_table, (st_data_t)obj, exported_object_dec_ref, 0);
119static ID id_memory_view;
136 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
137 if (!
NIL_P(entry_obj)) {
138 rb_warning(
"Duplicated registration of memory view to %"PRIsVALUE, klass);
152 const ssize_t ndim = view->
ndim;
153 const ssize_t *shape = view->
shape;
154 const ssize_t *strides = view->
strides;
157 for (i = ndim - 1; i >= 0; --i) {
158 if (strides[i] != n)
return false;
168 const ssize_t ndim = view->
ndim;
169 const ssize_t *shape = view->
shape;
170 const ssize_t *strides = view->
strides;
173 for (i = 0; i < ndim; ++i) {
174 if (strides[i] != n)
return false;
184 ssize_t i, n = item_size;
186 for (i = ndim - 1; i >= 0; --i) {
192 for (i = 0; i < ndim; ++i) {
220#ifdef HAVE_TRUE_LONG_LONG
221static const char native_types[] =
"sSiIlLqQjJ";
223static const char native_types[] =
"sSiIlLjJ";
225static const char endianness_types[] =
"sSiIlLqQjJ";
234get_format_size(
const char *format,
bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count,
const char **next_format,
VALUE *error)
243 *endianness = ENDIANNESS_NATIVE;
246 const int type_char = *format;
253 if (strchr(native_types, type_char)) {
260 rb_sprintf(
"Unable to specify native size for '%c'", type_char));
268 if (!strchr(endianness_types, type_char)) {
271 rb_sprintf(
"Unable to specify endianness for '%c'", type_char));
275 if (*endianness != ENDIANNESS_NATIVE) {
276 *error =
rb_exc_new_cstr(rb_eArgError,
"Unable to use both '<' and '>' multiple times");
279 *endianness = (format[i] ==
'<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
292 if (
'0' <= ch && ch <=
'9') {
294 while (
'0' <= (ch = format[i]) && ch <=
'9') {
301 *next_format = &format[i];
314 STRUCT_ALIGNOF(
short, *alignment);
315 return sizeof(short);
321 STRUCT_ALIGNOF(int16_t, *alignment);
326 STRUCT_ALIGNOF(
int, *alignment);
332 STRUCT_ALIGNOF(
long, *alignment);
339 STRUCT_ALIGNOF(int32_t, *alignment);
345 STRUCT_ALIGNOF(
float, *alignment);
346 return sizeof(float);
354 STRUCT_ALIGNOF(int64_t, *alignment);
360 STRUCT_ALIGNOF(
double, *alignment);
361 return sizeof(double);
365 STRUCT_ALIGNOF(intptr_t, *alignment);
366 return sizeof(intptr_t);
371 *error =
rb_exc_new_str(rb_eArgError, rb_sprintf(
"Invalid type character '%c'", type_char));
378calculate_padding(ssize_t total, ssize_t alignment_size)
380 if (alignment_size > 1) {
381 ssize_t res = total % alignment_size;
383 return alignment_size - res;
392 size_t *n_members,
const char **err)
394 if (format == NULL)
return 1;
399 bool alignment =
false;
400 ssize_t max_alignment_size = 0;
402 const char *p = format;
417 bool native_size_p =
false;
418 ssize_t alignment_size = 0;
419 endianness_t endianness = ENDIANNESS_NATIVE;
421 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
426 if (max_alignment_size < alignment_size) {
427 max_alignment_size = alignment_size;
430 const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
431 total += padding + size * count;
439 if (alignment && max_alignment_size > 0) {
440 const ssize_t padding = calculate_padding(total, max_alignment_size);
444 if (members && n_members) {
447 ssize_t i = 0, offset = 0;
448 const char *p = format;
450 const int type_char = *p;
453 ssize_t alignment_size = 0;
454 endianness_t endianness = ENDIANNESS_NATIVE;
456 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
458 const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
461 if (type_char !=
'x') {
462#ifdef WORDS_BIGENDIAN
463 bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
465 bool little_endian_p = (endianness != ENDIANNESS_BIG);
473 little_endian_p =
true;
479 little_endian_p =
false;
487 .native_size_p = native_size_p,
488 .little_endian_p = little_endian_p,
495 offset += size * count;
516 uint8_t *ptr = view->
data;
518 if (view->
ndim == 1) {
520 return ptr + indices[0] * stride;
523 assert(view->
shape != NULL);
529 for (i = 0; i < view->
ndim; ++i) {
530 stride *= view->
shape[i];
532 for (i = 0; i < view->
ndim; ++i) {
533 stride /= view->
shape[i];
534 ptr += indices[i] * stride;
539 for (i = 0; i < view->
ndim; ++i) {
540 ptr += indices[i] * view->
strides[i];
545 for (i = 0; i < view->
ndim; ++i) {
546 ptr += indices[i] * view->
strides[i];
557switch_endianness(uint8_t *buf, ssize_t
len)
563 uint8_t *q = buf +
len - 1;
581#ifdef WORDS_BIGENDIAN
587 const uint8_t *p = ptr + member->
offset + i * member->
size;
589 if (member->
format ==
'c') {
592 else if (member->
format ==
'C') {
593 return INT2FIX(*(
unsigned char *)p);
617 if (!native_endian_p) {
619 switch_endianness((uint8_t *)&val, member->
size);
678#if SIZEOF_INT64_T == SIZEOF_LONG
690#if SIZEOF_UINT64_T == SIZEOF_LONG
703 return INTPTR2NUM(val.iptr);
706 return UINTPTR2NUM(val.uptr);
717 if (ptr == NULL)
return Qnil;
718 if (member == NULL)
return Qnil;
721 return extract_item_member(ptr, member, i);
730 if (ptr == NULL)
return Qnil;
731 if (members == NULL)
return Qnil;
732 if (n_members == 0)
return Qnil;
734 if (n_members == 1 && members[0].repeat == 1) {
735 return rb_memory_view_extract_item_member(ptr, members, 0);
740 for (i = 0; i < n_members; ++i) {
741 for (j = 0; j < members[i].
repeat; ++j) {
742 VALUE v = extract_item_member(ptr, &members[i], j);
760 "Unable to parse item format at %"PRIdSIZE
" in \"%s\"",
772 if (view->
format == NULL) {
773 return INT2FIX(*(uint8_t *)ptr);
784lookup_memory_view_entry(
VALUE klass)
786 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
787 while (
NIL_P(entry_obj)) {
793 entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
796 if (! rb_typeddata_is_kind_of(entry_obj, &memory_view_entry_data_type))
825 bool rv = (*entry->
get_func)(obj, view, flags);
828 register_exported_object(view->
obj);
847 unregister_exported_object(view->
obj);
860 exported_object_table = rb_init_identtable();
865 0, &rb_memory_view_exported_object_registry_data_type,
866 exported_object_table);
867 rb_vm_register_global_object(obj);
868 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 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.
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.
void rb_warning(const char *fmt,...)
Issues a warning.
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
VALUE rb_cObject
Object class.
VALUE rb_cBasicObject
BasicObject class.
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
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().
int len
Length of the buffer.
const signed char ruby_digit36_to_number_table[]
Character to number mapping like ‘'a’->10,'b'->11etc.
#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.
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
#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.
struct rb_memory_view_t::@63 item_desc
Description of each components.
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.
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.