10#include "internal/hash.h"
11#include "internal/variable.h"
12#include "internal/vm.h"
17#if SIZEOF_INTPTR_T == SIZEOF_LONG_LONG
18# define INTPTR2NUM LL2NUM
19# define UINTPTR2NUM ULL2NUM
20#elif SIZEOF_INTPTR_T == SIZEOF_LONG
21# define INTPTR2NUM LONG2NUM
22# define UINTPTR2NUM ULONG2NUM
24# define INTPTR2NUM INT2NUM
25# define UINTPTR2NUM UINT2NUM
29#define STRUCT_ALIGNOF(T, result) do { \
30 (result) = RUBY_ALIGNOF(T); \
35static st_table *exported_object_table = NULL;
36VALUE rb_memory_view_exported_object_registry =
Qundef;
39exported_object_registry_mark_key_i(st_data_t key, st_data_t value, st_data_t data)
46exported_object_registry_mark(
void *ptr)
49 st_foreach(exported_object_table, exported_object_registry_mark_key_i, 0);
53exported_object_registry_free(
void *ptr)
56 st_clear(exported_object_table);
57 st_free_table(exported_object_table);
58 exported_object_table = NULL;
62const rb_data_type_t rb_memory_view_exported_object_registry_data_type = {
63 "memory_view/exported_object_registry",
65 exported_object_registry_mark,
66 exported_object_registry_free,
73exported_object_add_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
87exported_object_dec_ref(st_data_t *key, st_data_t *val, st_data_t arg,
int existing)
101register_exported_object(
VALUE obj)
104 st_update(exported_object_table, (st_data_t)obj, exported_object_add_ref, 0);
110unregister_exported_object(
VALUE obj)
113 if (exported_object_table)
114 st_update(exported_object_table, (st_data_t)obj, exported_object_dec_ref, 0);
120static ID id_memory_view;
129 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
137 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
138 if (!
NIL_P(entry_obj)) {
139 rb_warning(
"Duplicated registration of memory view to %"PRIsVALUE, klass);
153 const ssize_t ndim = view->
ndim;
154 const ssize_t *shape = view->
shape;
155 const ssize_t *strides = view->
strides;
162 return strides[0] == n;
164 for (i = ndim - 1; i >= 0; --i) {
165 if (strides[i] != n)
return false;
175 const ssize_t ndim = view->
ndim;
176 const ssize_t *shape = view->
shape;
177 const ssize_t *strides = view->
strides;
182 return strides[0] == n;
184 for (i = 0; i < ndim; ++i) {
185 if (strides[i] != n)
return false;
199 for (i = 0; i < ndim; ++i) {
215 ssize_t i, n = item_size;
217 for (i = ndim - 1; i >= 0; --i) {
223 for (i = 0; i < ndim; ++i) {
251#ifdef HAVE_TRUE_LONG_LONG
252static const char native_types[] =
"sSiIlLqQjJ";
254static const char native_types[] =
"sSiIlLjJ";
256static const char endianness_types[] =
"sSiIlLqQjJ";
265get_format_size(
const char *format,
bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count,
const char **next_format,
VALUE *error)
274 *endianness = ENDIANNESS_NATIVE;
277 const int type_char = *format;
284 if (strchr(native_types, type_char)) {
291 rb_sprintf(
"Unable to specify native size for '%c'", type_char));
299 if (!strchr(endianness_types, type_char)) {
302 rb_sprintf(
"Unable to specify endianness for '%c'", type_char));
306 if (*endianness != ENDIANNESS_NATIVE) {
307 *error =
rb_exc_new_cstr(rb_eArgError,
"Unable to use both '<' and '>' multiple times");
310 *endianness = (format[i] ==
'<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
323 if (
'0' <= ch && ch <=
'9') {
325 while (
'0' <= (ch = format[i]) && ch <=
'9') {
332 *next_format = &format[i];
345 STRUCT_ALIGNOF(
short, *alignment);
346 return sizeof(short);
352 STRUCT_ALIGNOF(int16_t, *alignment);
357 STRUCT_ALIGNOF(
int, *alignment);
363 STRUCT_ALIGNOF(
long, *alignment);
370 STRUCT_ALIGNOF(int32_t, *alignment);
376 STRUCT_ALIGNOF(
float, *alignment);
377 return sizeof(float);
385 STRUCT_ALIGNOF(int64_t, *alignment);
391 STRUCT_ALIGNOF(
double, *alignment);
392 return sizeof(double);
396 STRUCT_ALIGNOF(intptr_t, *alignment);
397 return sizeof(intptr_t);
402 *error =
rb_exc_new_str(rb_eArgError, rb_sprintf(
"Invalid type character '%c'", type_char));
409calculate_padding(ssize_t total, ssize_t alignment_size)
411 if (alignment_size > 1) {
412 ssize_t res = total % alignment_size;
414 return alignment_size - res;
423 size_t *n_members,
const char **err)
425 if (format == NULL)
return 1;
430 bool alignment =
false;
431 ssize_t max_alignment_size = 0;
433 const char *p = format;
448 bool native_size_p =
false;
449 ssize_t alignment_size = 0;
450 endianness_t endianness = ENDIANNESS_NATIVE;
452 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
457 if (max_alignment_size < alignment_size) {
458 max_alignment_size = alignment_size;
461 const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
462 total += padding + size * count;
470 if (alignment && max_alignment_size > 0) {
471 const ssize_t padding = calculate_padding(total, max_alignment_size);
475 if (members && n_members) {
478 ssize_t i = 0, offset = 0;
479 const char *p = format;
481 const int type_char = *p;
484 ssize_t alignment_size = 0;
485 endianness_t endianness = ENDIANNESS_NATIVE;
487 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
489 const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
492 if (type_char !=
'x') {
493#ifdef WORDS_BIGENDIAN
494 bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
496 bool little_endian_p = (endianness != ENDIANNESS_BIG);
504 little_endian_p =
true;
510 little_endian_p =
false;
518 .native_size_p = native_size_p,
519 .little_endian_p = little_endian_p,
526 offset += size * count;
547 uint8_t *ptr = view->
data;
549 if (view->
ndim == 1) {
551 return ptr + indices[0] * stride;
554 assert(view->
shape != NULL);
560 for (i = 0; i < view->
ndim; ++i) {
561 stride *= view->
shape[i];
563 for (i = 0; i < view->
ndim; ++i) {
564 stride /= view->
shape[i];
565 ptr += indices[i] * stride;
570 for (i = 0; i < view->
ndim; ++i) {
571 ptr += indices[i] * view->
strides[i];
576 for (i = 0; i < view->
ndim; ++i) {
577 ptr += indices[i] * view->
strides[i];
588switch_endianness(uint8_t *buf, ssize_t
len)
594 uint8_t *q = buf +
len - 1;
612#ifdef WORDS_BIGENDIAN
618 const uint8_t *p = ptr + member->
offset + i * member->
size;
620 if (member->
format ==
'c') {
623 else if (member->
format ==
'C') {
624 return INT2FIX(*(
unsigned char *)p);
648 if (!native_endian_p) {
650 switch_endianness((uint8_t *)&val, member->
size);
709#if SIZEOF_INT64_T == SIZEOF_LONG
721#if SIZEOF_UINT64_T == SIZEOF_LONG
734 return INTPTR2NUM(val.iptr);
737 return UINTPTR2NUM(val.uptr);
748 if (ptr == NULL)
return Qnil;
749 if (member == NULL)
return Qnil;
752 return extract_item_member(ptr, member, i);
761 if (ptr == NULL)
return Qnil;
762 if (members == NULL)
return Qnil;
763 if (n_members == 0)
return Qnil;
765 if (n_members == 1 && members[0].repeat == 1) {
766 return rb_memory_view_extract_item_member(ptr, members, 0);
771 for (i = 0; i < n_members; ++i) {
772 for (j = 0; j < members[i].
repeat; ++j) {
773 VALUE v = extract_item_member(ptr, &members[i], j);
791 "Unable to parse item format at %"PRIdSIZE
" in \"%s\"",
803 if (view->
format == NULL) {
804 return INT2FIX(*(uint8_t *)ptr);
815lookup_memory_view_entry(
VALUE klass)
817 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
818 while (
NIL_P(entry_obj)) {
824 entry_obj = rb_ivar_lookup(klass, id_memory_view,
Qnil);
827 if (! rb_typeddata_is_kind_of(entry_obj, &memory_view_entry_data_type))
856 bool rv = (*entry->
get_func)(obj, view, flags);
859 register_exported_object(view->
obj);
878 unregister_exported_object(view->
obj);
891 exported_object_table = rb_init_identtable();
896 0, &rb_memory_view_exported_object_registry_data_type,
897 exported_object_table);
898 rb_vm_register_global_object(obj);
899 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.
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.
struct rb_memory_view_t::@65 item_desc
Description of each components.
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.