Ruby 4.1.0dev (2026-08-28 revision 5eb9a6925b805a17dced976d5b741afe5086aab1)
memory_view.c (5eb9a6925b805a17dced976d5b741afe5086aab1)
1/**********************************************************************
2
3 memory_view.c - Memory View
4
5 Copyright (C) 2020 Kenta Murata <mrkn@mrkn.jp>
6
7**********************************************************************/
8
9#include "internal.h"
10#include "internal/hash.h"
11#include "internal/variable.h"
12#include "internal/vm.h"
13#include "ruby/memory_view.h"
14#include "ruby/util.h"
15#include "vm_sync.h"
16
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
23#else
24# define INTPTR2NUM INT2NUM
25# define UINTPTR2NUM UINT2NUM
26#endif
27
28
29#define STRUCT_ALIGNOF(T, result) do { \
30 (result) = RUBY_ALIGNOF(T); \
31} while(0)
32
33// Exported Object Registry
34
35static st_table *exported_object_table = NULL;
36VALUE rb_memory_view_exported_object_registry = Qundef;
37
38static int
39exported_object_registry_mark_key_i(st_data_t key, st_data_t value, st_data_t data)
40{
41 rb_gc_mark(key);
42 return ST_CONTINUE;
43}
44
45static void
46exported_object_registry_mark(void *ptr)
47{
48 // Don't use RB_VM_LOCK_ENTER here. It is unnecessary during GC.
49 st_foreach(exported_object_table, exported_object_registry_mark_key_i, 0);
50}
51
52static void
53exported_object_registry_free(void *ptr)
54{
55 RB_VM_LOCKING() {
56 st_clear(exported_object_table);
57 st_free_table(exported_object_table);
58 exported_object_table = NULL;
59 }
60}
61
62const rb_data_type_t rb_memory_view_exported_object_registry_data_type = {
63 "memory_view/exported_object_registry",
64 {
65 exported_object_registry_mark,
66 exported_object_registry_free,
67 0,
68 },
69 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
70};
71
72static int
73exported_object_add_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
74{
75 ASSERT_vm_locking();
76
77 if (existing) {
78 *val += 1;
79 }
80 else {
81 *val = 1;
82 }
83 return ST_CONTINUE;
84}
85
86static int
87exported_object_dec_ref(st_data_t *key, st_data_t *val, st_data_t arg, int existing)
88{
89 ASSERT_vm_locking();
90
91 if (existing) {
92 *val -= 1;
93 if (*val == 0) {
94 return ST_DELETE;
95 }
96 }
97 return ST_CONTINUE;
98}
99
100static void
101register_exported_object(VALUE obj)
102{
103 RB_VM_LOCKING() {
104 st_update(exported_object_table, (st_data_t)obj, exported_object_add_ref, 0);
105 RB_OBJ_WRITTEN(rb_memory_view_exported_object_registry, Qundef, obj);
106 }
107}
108
109static void
110unregister_exported_object(VALUE obj)
111{
112 RB_VM_LOCKING() {
113 if (exported_object_table)
114 st_update(exported_object_table, (st_data_t)obj, exported_object_dec_ref, 0);
115 }
116}
117
118// MemoryView
119
120static ID id_memory_view;
121
122static const rb_data_type_t memory_view_entry_data_type = {
123 "memory_view/entry",
124 {
125 0,
126 0,
127 0,
128 },
129 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
130};
131
132/* Register memory view functions for the given class */
133bool
135{
136 Check_Type(klass, T_CLASS);
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);
140 return false;
141 }
142 else {
143 entry_obj = TypedData_Wrap_Struct(0, &memory_view_entry_data_type, (void *)entry);
144 rb_ivar_set(klass, id_memory_view, entry_obj);
145 return true;
146 }
147}
148
149/* Examine whether the given memory view has row-major order strides. */
150bool
152{
153 const ssize_t ndim = view->ndim;
154 const ssize_t *shape = view->shape;
155 const ssize_t *strides = view->strides;
156 ssize_t n = view->item_size;
157 ssize_t i;
158 if (!strides) {
159 return true;
160 }
161 if (ndim == 1) {
162 return strides[0] == n;
163 }
164 for (i = ndim - 1; i >= 0; --i) {
165 if (strides[i] != n) return false;
166 n *= shape[i];
167 }
168 return true;
169}
170
171/* Examine whether the given memory view has column-major order strides. */
172bool
174{
175 const ssize_t ndim = view->ndim;
176 const ssize_t *shape = view->shape;
177 const ssize_t *strides = view->strides;
178 ssize_t i;
179 if (strides) {
180 ssize_t n = view->item_size;
181 if (ndim == 1) {
182 return strides[0] == n;
183 }
184 for (i = 0; i < ndim; ++i) {
185 if (strides[i] != n) return false;
186 n *= shape[i];
187 }
188 return true;
189 }
190 else {
191 if (ndim == 1) {
192 return true;
193 }
194 if (!shape) {
195 return false;
196 }
197
198 bool trivial = true;
199 for (i = 0; i < ndim; ++i) {
200 if (shape[i] > 1) {
201 if (!trivial) {
202 return false;
203 }
204 trivial = false;
205 }
206 }
207 return true;
208 }
209}
210
211/* Initialize strides array to represent the specified contiguous array. */
212void
213rb_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)
214{
215 ssize_t i, n = item_size;
216 if (row_major_p) {
217 for (i = ndim - 1; i >= 0; --i) {
218 strides[i] = n;
219 n *= shape[i];
220 }
221 }
222 else { // column-major
223 for (i = 0; i < ndim; ++i) {
224 strides[i] = n;
225 n *= shape[i];
226 }
227 }
228}
229
230/* Initialize view to expose a simple byte array */
231bool
232rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly)
233{
234 view->obj = obj;
235 view->data = data;
236 view->byte_size = len;
237 view->readonly = readonly;
238 view->format = NULL;
239 view->item_size = 1;
240 view->item_desc.components = NULL;
241 view->item_desc.length = 0;
242 view->ndim = 1;
243 view->shape = NULL;
244 view->strides = NULL;
245 view->sub_offsets = NULL;
246 view->private_data = NULL;
247
248 return true;
249}
250
251#ifdef HAVE_TRUE_LONG_LONG
252static const char native_types[] = "sSiIlLqQjJ";
253#else
254static const char native_types[] = "sSiIlLjJ";
255#endif
256static const char endianness_types[] = "sSiIlLqQjJ";
257
258typedef enum {
259 ENDIANNESS_NATIVE,
260 ENDIANNESS_LITTLE,
261 ENDIANNESS_BIG
262} endianness_t;
263
264static ssize_t
265get_format_size(const char *format, bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count, const char **next_format, VALUE *error)
266{
267 RUBY_ASSERT(format != NULL);
268 RUBY_ASSERT(native_p != NULL);
269 RUBY_ASSERT(endianness != NULL);
270 RUBY_ASSERT(count != NULL);
271 RUBY_ASSERT(next_format != NULL);
272
273 *native_p = false;
274 *endianness = ENDIANNESS_NATIVE;
275 *count = 1;
276
277 const int type_char = *format;
278
279 int i = 1;
280 while (format[i]) {
281 switch (format[i]) {
282 case '!':
283 case '_':
284 if (strchr(native_types, type_char)) {
285 *native_p = true;
286 ++i;
287 }
288 else {
289 if (error) {
290 *error = rb_exc_new_str(rb_eArgError,
291 rb_sprintf("Unable to specify native size for '%c'", type_char));
292 }
293 return -1;
294 }
295 continue;
296
297 case '<':
298 case '>':
299 if (!strchr(endianness_types, type_char)) {
300 if (error) {
301 *error = rb_exc_new_str(rb_eArgError,
302 rb_sprintf("Unable to specify endianness for '%c'", type_char));
303 }
304 return -1;
305 }
306 if (*endianness != ENDIANNESS_NATIVE) {
307 *error = rb_exc_new_cstr(rb_eArgError, "Unable to use both '<' and '>' multiple times");
308 return -1;
309 }
310 *endianness = (format[i] == '<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
311 ++i;
312 continue;
313
314 default:
315 break;
316 }
317
318 break;
319 }
320
321 // parse count
322 int ch = format[i];
323 if ('0' <= ch && ch <= '9') {
324 ssize_t n = 0;
325 while ('0' <= (ch = format[i]) && ch <= '9') {
326 n = 10*n + ruby_digit36_to_number_table[ch];
327 ++i;
328 }
329 *count = n;
330 }
331
332 *next_format = &format[i];
333
334 switch (type_char) {
335 case 'x': // padding
336 return 1;
337
338 case 'c': // signed char
339 case 'C': // unsigned char
340 return sizeof(char);
341
342 case 's': // s for int16_t, s! for signed short
343 case 'S': // S for uint16_t, S! for unsigned short
344 if (*native_p) {
345 STRUCT_ALIGNOF(short, *alignment);
346 return sizeof(short);
347 }
348 // fall through
349
350 case 'n': // n for big-endian 16bit unsigned integer
351 case 'v': // v for little-endian 16bit unsigned integer
352 STRUCT_ALIGNOF(int16_t, *alignment);
353 return 2;
354
355 case 'i': // i and i! for signed int
356 case 'I': // I and I! for unsigned int
357 STRUCT_ALIGNOF(int, *alignment);
358 return sizeof(int);
359
360 case 'l': // l for int32_t, l! for signed long
361 case 'L': // L for uint32_t, L! for unsigned long
362 if (*native_p) {
363 STRUCT_ALIGNOF(long, *alignment);
364 return sizeof(long);
365 }
366 // fall through
367
368 case 'N': // N for big-endian 32bit unsigned integer
369 case 'V': // V for little-endian 32bit unsigned integer
370 STRUCT_ALIGNOF(int32_t, *alignment);
371 return 4;
372
373 case 'f': // f for native float
374 case 'e': // e for little-endian float
375 case 'g': // g for big-endian float
376 STRUCT_ALIGNOF(float, *alignment);
377 return sizeof(float);
378
379 case 'q': // q for int64_t, q! for signed long long
380 case 'Q': // Q for uint64_t, Q! for unsigned long long
381 if (*native_p) {
382 STRUCT_ALIGNOF(LONG_LONG, *alignment);
383 return sizeof(LONG_LONG);
384 }
385 STRUCT_ALIGNOF(int64_t, *alignment);
386 return 8;
387
388 case 'd': // d for native double
389 case 'E': // E for little-endian double
390 case 'G': // G for big-endian double
391 STRUCT_ALIGNOF(double, *alignment);
392 return sizeof(double);
393
394 case 'j': // j for intptr_t
395 case 'J': // J for uintptr_t
396 STRUCT_ALIGNOF(intptr_t, *alignment);
397 return sizeof(intptr_t);
398
399 default:
400 *alignment = -1;
401 if (error) {
402 *error = rb_exc_new_str(rb_eArgError, rb_sprintf("Invalid type character '%c'", type_char));
403 }
404 return -1;
405 }
406}
407
408static inline ssize_t
409calculate_padding(ssize_t total, ssize_t alignment_size)
410{
411 if (alignment_size > 1) {
412 ssize_t res = total % alignment_size;
413 if (res > 0) {
414 return alignment_size - res;
415 }
416 }
417 return 0;
418}
419
420ssize_t
423 size_t *n_members, const char **err)
424{
425 if (format == NULL) return 1;
426
427 VALUE error = Qnil;
428 ssize_t total = 0;
429 size_t len = 0;
430 bool alignment = false;
431 ssize_t max_alignment_size = 0;
432
433 const char *p = format;
434 if (*p == '|') { // alignment specifier
435 alignment = true;
436 ++format;
437 ++p;
438 }
439 while (*p) {
440 const char *q = p;
441
442 // ignore spaces
443 if (ISSPACE(*p)) {
444 while (ISSPACE(*p)) ++p;
445 continue;
446 }
447
448 bool native_size_p = false;
449 ssize_t alignment_size = 0;
450 endianness_t endianness = ENDIANNESS_NATIVE;
451 ssize_t count = 0;
452 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
453 if (size < 0) {
454 if (err) *err = q;
455 return -1;
456 }
457 if (max_alignment_size < alignment_size) {
458 max_alignment_size = alignment_size;
459 }
460
461 const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
462 total += padding + size * count;
463
464 if (*q != 'x') {
465 ++len;
466 }
467 }
468
469 // adjust total size with the alignment size of the largest element
470 if (alignment && max_alignment_size > 0) {
471 const ssize_t padding = calculate_padding(total, max_alignment_size);
472 total += padding;
473 }
474
475 if (members && n_members) {
477
478 ssize_t i = 0, offset = 0;
479 const char *p = format;
480 while (*p) {
481 const int type_char = *p;
482
483 bool native_size_p;
484 ssize_t alignment_size = 0;
485 endianness_t endianness = ENDIANNESS_NATIVE;
486 ssize_t count = 0;
487 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
488
489 const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
490 offset += padding;
491
492 if (type_char != 'x') {
493#ifdef WORDS_BIGENDIAN
494 bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
495#else
496 bool little_endian_p = (endianness != ENDIANNESS_BIG);
497#endif
498
499 switch (type_char) {
500 case 'e':
501 case 'E':
502 case 'v':
503 case 'V':
504 little_endian_p = true;
505 break;
506 case 'g':
507 case 'G':
508 case 'n':
509 case 'N':
510 little_endian_p = false;
511 break;
512 default:
513 break;
514 }
515
517 .format = type_char,
518 .native_size_p = native_size_p,
519 .little_endian_p = little_endian_p,
520 .offset = offset,
521 .size = size,
522 .repeat = count
523 };
524 }
525
526 offset += size * count;
527 }
528
529 *members = buf;
530 *n_members = len;
531 }
532
533 return total;
534}
535
536/* Return the item size. */
537ssize_t
538rb_memory_view_item_size_from_format(const char *format, const char **err)
539{
540 return rb_memory_view_parse_item_format(format, NULL, NULL, err);
541}
542
543/* Return the pointer to the item located by the given indices. */
544void *
546{
547 uint8_t *ptr = view->data;
548
549 if (view->ndim == 1) {
550 ssize_t stride = view->strides != NULL ? view->strides[0] : view->item_size;
551 return ptr + indices[0] * stride;
552 }
553
554 assert(view->shape != NULL);
555
556 ssize_t i;
557 if (view->strides == NULL) {
558 // row-major contiguous array
559 ssize_t stride = view->item_size;
560 for (i = 0; i < view->ndim; ++i) {
561 stride *= view->shape[i];
562 }
563 for (i = 0; i < view->ndim; ++i) {
564 stride /= view->shape[i];
565 ptr += indices[i] * stride;
566 }
567 }
568 else if (view->sub_offsets == NULL) {
569 // flat strided array
570 for (i = 0; i < view->ndim; ++i) {
571 ptr += indices[i] * view->strides[i];
572 }
573 }
574 else {
575 // indirect strided array
576 for (i = 0; i < view->ndim; ++i) {
577 ptr += indices[i] * view->strides[i];
578 if (view->sub_offsets[i] >= 0) {
579 ptr = *(uint8_t **)ptr + view->sub_offsets[i];
580 }
581 }
582 }
583
584 return ptr;
585}
586
587static void
588switch_endianness(uint8_t *buf, ssize_t len)
589{
590 RUBY_ASSERT(buf != NULL);
591 RUBY_ASSERT(len >= 0);
592
593 uint8_t *p = buf;
594 uint8_t *q = buf + len - 1;
595
596 while (q - p > 0) {
597 uint8_t t = *p;
598 *p = *q;
599 *q = t;
600 ++p;
601 --q;
602 }
603}
604
605static inline VALUE
606extract_item_member(const uint8_t *ptr, const rb_memory_view_item_component_t *member, const size_t i)
607{
608 RUBY_ASSERT(ptr != NULL);
609 RUBY_ASSERT(member != NULL);
610 RUBY_ASSERT(i < member->repeat);
611
612#ifdef WORDS_BIGENDIAN
613 const bool native_endian_p = !member->little_endian_p;
614#else
615 const bool native_endian_p = member->little_endian_p;
616#endif
617
618 const uint8_t *p = ptr + member->offset + i * member->size;
619
620 if (member->format == 'c') {
621 return INT2FIX(*(char *)p);
622 }
623 else if (member->format == 'C') {
624 return INT2FIX(*(unsigned char *)p);
625 }
626
627 union {
628 short s;
629 unsigned short us;
630 int i;
631 unsigned int ui;
632 long l;
633 unsigned long ul;
634 LONG_LONG ll;
635 unsigned LONG_LONG ull;
636 int16_t i16;
637 uint16_t u16;
638 int32_t i32;
639 uint32_t u32;
640 int64_t i64;
641 uint64_t u64;
642 intptr_t iptr;
643 uintptr_t uptr;
644 float f;
645 double d;
646 } val;
647
648 if (!native_endian_p) {
649 MEMCPY(&val, p, uint8_t, member->size);
650 switch_endianness((uint8_t *)&val, member->size);
651 }
652 else {
653 MEMCPY(&val, p, uint8_t, member->size);
654 }
655
656 switch (member->format) {
657 case 's':
658 if (member->native_size_p) {
659 return INT2FIX(val.s);
660 }
661 else {
662 return INT2FIX(val.i16);
663 }
664
665 case 'S':
666 case 'n':
667 case 'v':
668 if (member->native_size_p) {
669 return UINT2NUM(val.us);
670 }
671 else {
672 return INT2FIX(val.u16);
673 }
674
675 case 'i':
676 return INT2NUM(val.i);
677
678 case 'I':
679 return UINT2NUM(val.ui);
680
681 case 'l':
682 if (member->native_size_p) {
683 return LONG2NUM(val.l);
684 }
685 else {
686 return LONG2NUM(val.i32);
687 }
688
689 case 'L':
690 case 'N':
691 case 'V':
692 if (member->native_size_p) {
693 return ULONG2NUM(val.ul);
694 }
695 else {
696 return ULONG2NUM(val.u32);
697 }
698
699 case 'f':
700 case 'e':
701 case 'g':
702 return DBL2NUM(val.f);
703
704 case 'q':
705 if (member->native_size_p) {
706 return LL2NUM(val.ll);
707 }
708 else {
709#if SIZEOF_INT64_T == SIZEOF_LONG
710 return LONG2NUM(val.i64);
711#else
712 return LL2NUM(val.i64);
713#endif
714 }
715
716 case 'Q':
717 if (member->native_size_p) {
718 return ULL2NUM(val.ull);
719 }
720 else {
721#if SIZEOF_UINT64_T == SIZEOF_LONG
722 return ULONG2NUM(val.u64);
723#else
724 return ULL2NUM(val.u64);
725#endif
726 }
727
728 case 'd':
729 case 'E':
730 case 'G':
731 return DBL2NUM(val.d);
732
733 case 'j':
734 return INTPTR2NUM(val.iptr);
735
736 case 'J':
737 return UINTPTR2NUM(val.uptr);
738
739 default:
741 }
742}
743
744/* Return a value of the extracted member. */
745VALUE
746rb_memory_view_extract_item_member(const void *ptr, const rb_memory_view_item_component_t *member, const size_t i)
747{
748 if (ptr == NULL) return Qnil;
749 if (member == NULL) return Qnil;
750 if (i >= member->repeat) return Qnil;
751
752 return extract_item_member(ptr, member, i);
753}
754
755/* Return a value that consists of item members.
756 * When an item is a single member, the return value is a single value.
757 * When an item consists of multiple members, an array will be returned. */
758VALUE
759rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members)
760{
761 if (ptr == NULL) return Qnil;
762 if (members == NULL) return Qnil;
763 if (n_members == 0) return Qnil;
764
765 if (n_members == 1 && members[0].repeat == 1) {
766 return rb_memory_view_extract_item_member(ptr, members, 0);
767 }
768
769 size_t i, j;
770 VALUE item = rb_ary_new();
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);
774 rb_ary_push(item, v);
775 }
776 }
777
778 return item;
779}
780
781void
783{
784 if (view->item_desc.components == NULL) {
785 const char *err;
786 rb_memory_view_item_component_t **p_components =
788 ssize_t n = rb_memory_view_parse_item_format(view->format, p_components, &view->item_desc.length, &err);
789 if (n < 0) {
790 rb_raise(rb_eRuntimeError,
791 "Unable to parse item format at %"PRIdSIZE" in \"%s\"",
792 (err - view->format), view->format);
793 }
794 }
795}
796
797/* Return a value that consists of item members in the given memory view. */
798VALUE
799rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices)
800{
801 void *ptr = rb_memory_view_get_item_pointer(view, indices);
802
803 if (view->format == NULL) {
804 return INT2FIX(*(uint8_t *)ptr);
805 }
806
807 if (view->item_desc.components == NULL) {
809 }
810
812}
813
814static const rb_memory_view_entry_t *
815lookup_memory_view_entry(VALUE klass)
816{
817 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view, Qnil);
818 while (NIL_P(entry_obj)) {
819 klass = rb_class_superclass(klass);
820
821 if (klass == rb_cBasicObject || klass == rb_cObject)
822 return NULL;
823
824 entry_obj = rb_ivar_lookup(klass, id_memory_view, Qnil);
825 }
826
827 if (! rb_typeddata_is_kind_of(entry_obj, &memory_view_entry_data_type))
828 return NULL;
829
830 return (const rb_memory_view_entry_t *)RTYPEDDATA_DATA(entry_obj);
831}
832
833/* Examine whether the given object supports memory view. */
834bool
836{
837 VALUE klass = CLASS_OF(obj);
838 const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
839 if (entry)
840 return (* entry->available_p_func)(obj);
841 else
842 return false;
843}
844
845/* Obtain a memory view from obj, and substitute the information to view. */
846bool
848{
849 VALUE klass = CLASS_OF(obj);
850 const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
851 if (entry) {
852 if (!(*entry->available_p_func)(obj)) {
853 return false;
854 }
855
856 bool rv = (*entry->get_func)(obj, view, flags);
857 if (rv) {
858 view->_memory_view_entry = entry;
859 register_exported_object(view->obj);
860 }
861 return rv;
862 }
863 else
864 return false;
865}
866
867/* Release the memory view obtained from obj. */
868bool
870{
871 const rb_memory_view_entry_t *entry = view->_memory_view_entry;
872 if (entry) {
873 bool rv = true;
874 if (entry->release_func) {
875 rv = (*entry->release_func)(view->obj, view);
876 }
877 if (rv) {
878 unregister_exported_object(view->obj);
879 view->obj = Qnil;
881 }
882 return rv;
883 }
884 else
885 return false;
886}
887
888void
889Init_MemoryView(void)
890{
891 exported_object_table = rb_init_identtable();
892
893 // exported_object_table is referred through rb_memory_view_exported_object_registry
894 // in -test-/memory_view extension.
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;
900
901 id_memory_view = rb_intern_const("__memory_view__");
902}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define LONG_LONG
Definition long_long.h:38
#define ISSPACE
Old name of rb_isspace.
Definition ctype.h:88
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define ULONG2NUM
Old name of RB_ULONG2NUM.
Definition long.h:60
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define LL2NUM
Old name of RB_LL2NUM.
Definition long_long.h:30
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define LONG2NUM
Old name of RB_LONG2NUM.
Definition long.h:50
#define ULL2NUM
Old name of RB_ULL2NUM.
Definition long_long.h:31
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define NIL_P
Old name of RB_NIL_P.
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
#define UINT2NUM
Old name of RB_UINT2NUM.
Definition int.h:46
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
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.
Definition error.c:1482
void rb_warning(const char *fmt,...)
Issues a warning.
Definition error.c:499
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
Definition object.c:2306
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:58
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:468
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.
Definition string.h:1671
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.
Definition variable.c:2059
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
int len
Length of the buffer.
Definition io.h:8
const signed char ruby_digit36_to_number_table[]
Character to number mapping like ‘'a’->10,'b'->11etc.
Definition util.c:60
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
Memory View.
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.
Definition rtypeddata.h:106
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:122
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:557
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:238
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.
Definition memory_view.h:45
bool native_size_p
:FIXME: what is a "native" size is unclear.
Definition memory_view.h:50
size_t size
The component's size.
Definition memory_view.h:59
size_t repeat
How many numbers of components are there.
Definition memory_view.h:65
size_t offset
The component's offset.
Definition memory_view.h:56
bool little_endian_p
Endian of the component.
Definition memory_view.h:53
A MemoryView structure, rb_memory_view_t, is used for exporting objects' MemoryView.
Definition memory_view.h:77
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.
Definition memory_view.h:84
VALUE obj
The original object that has the memory exported via this memory view.
Definition memory_view.h:81
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.
Definition memory_view.h:87
bool readonly
true for readonly memory, false for writable memory.
Definition memory_view.h:90
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:425