Ruby: memory_view.c Source File

Ruby 3.4.5p51 (2025-07-16 revision 20cda200d3ce092571d0b5d342dadca69636cb0f)

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