Ruby 4.1.0dev (2026-05-15 revision a8bcae043f931d9b79f1cb1fe2c021985d07b984)
memory_view.c (a8bcae043f931d9b79f1cb1fe2c021985d07b984)
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;
35VALUE 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_LOCKING() {
55 st_clear(exported_object_table);
56 st_free_table(exported_object_table);
57 exported_object_table = NULL;
58 }
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 | RUBY_TYPED_WB_PROTECTED
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_LOCKING() {
103 st_update(exported_object_table, (st_data_t)obj, exported_object_add_ref, 0);
104 RB_OBJ_WRITTEN(rb_memory_view_exported_object_registry, Qundef, obj);
105 }
106}
107
108static void
109unregister_exported_object(VALUE obj)
110{
111 RB_VM_LOCKING() {
112 if (exported_object_table)
113 st_update(exported_object_table, (st_data_t)obj, exported_object_dec_ref, 0);
114 }
115}
116
117// MemoryView
118
119static ID id_memory_view;
120
121static const rb_data_type_t memory_view_entry_data_type = {
122 "memory_view/entry",
123 {
124 0,
125 0,
126 0,
127 },
129};
130
131/* Register memory view functions for the given class */
132bool
134{
135 Check_Type(klass, T_CLASS);
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);
139 return false;
140 }
141 else {
142 entry_obj = TypedData_Wrap_Struct(0, &memory_view_entry_data_type, (void *)entry);
143 rb_ivar_set(klass, id_memory_view, entry_obj);
144 return true;
145 }
146}
147
148/* Examine whether the given memory view has row-major order strides. */
149bool
151{
152 const ssize_t ndim = view->ndim;
153 const ssize_t *shape = view->shape;
154 const ssize_t *strides = view->strides;
155 ssize_t n = view->item_size;
156 ssize_t i;
157 for (i = ndim - 1; i >= 0; --i) {
158 if (strides[i] != n) return false;
159 n *= shape[i];
160 }
161 return true;
162}
163
164/* Examine whether the given memory view has column-major order strides. */
165bool
167{
168 const ssize_t ndim = view->ndim;
169 const ssize_t *shape = view->shape;
170 const ssize_t *strides = view->strides;
171 ssize_t n = view->item_size;
172 ssize_t i;
173 for (i = 0; i < ndim; ++i) {
174 if (strides[i] != n) return false;
175 n *= shape[i];
176 }
177 return true;
178}
179
180/* Initialize strides array to represent the specified contiguous array. */
181void
182rb_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)
183{
184 ssize_t i, n = item_size;
185 if (row_major_p) {
186 for (i = ndim - 1; i >= 0; --i) {
187 strides[i] = n;
188 n *= shape[i];
189 }
190 }
191 else { // column-major
192 for (i = 0; i < ndim; ++i) {
193 strides[i] = n;
194 n *= shape[i];
195 }
196 }
197}
198
199/* Initialize view to expose a simple byte array */
200bool
201rb_memory_view_init_as_byte_array(rb_memory_view_t *view, VALUE obj, void *data, const ssize_t len, const bool readonly)
202{
203 view->obj = obj;
204 view->data = data;
205 view->byte_size = len;
206 view->readonly = readonly;
207 view->format = NULL;
208 view->item_size = 1;
209 view->item_desc.components = NULL;
210 view->item_desc.length = 0;
211 view->ndim = 1;
212 view->shape = NULL;
213 view->strides = NULL;
214 view->sub_offsets = NULL;
215 view->private_data = NULL;
216
217 return true;
218}
219
220#ifdef HAVE_TRUE_LONG_LONG
221static const char native_types[] = "sSiIlLqQjJ";
222#else
223static const char native_types[] = "sSiIlLjJ";
224#endif
225static const char endianness_types[] = "sSiIlLqQjJ";
226
227typedef enum {
228 ENDIANNESS_NATIVE,
229 ENDIANNESS_LITTLE,
230 ENDIANNESS_BIG
231} endianness_t;
232
233static ssize_t
234get_format_size(const char *format, bool *native_p, ssize_t *alignment, endianness_t *endianness, ssize_t *count, const char **next_format, VALUE *error)
235{
236 RUBY_ASSERT(format != NULL);
237 RUBY_ASSERT(native_p != NULL);
238 RUBY_ASSERT(endianness != NULL);
239 RUBY_ASSERT(count != NULL);
240 RUBY_ASSERT(next_format != NULL);
241
242 *native_p = false;
243 *endianness = ENDIANNESS_NATIVE;
244 *count = 1;
245
246 const int type_char = *format;
247
248 int i = 1;
249 while (format[i]) {
250 switch (format[i]) {
251 case '!':
252 case '_':
253 if (strchr(native_types, type_char)) {
254 *native_p = true;
255 ++i;
256 }
257 else {
258 if (error) {
259 *error = rb_exc_new_str(rb_eArgError,
260 rb_sprintf("Unable to specify native size for '%c'", type_char));
261 }
262 return -1;
263 }
264 continue;
265
266 case '<':
267 case '>':
268 if (!strchr(endianness_types, type_char)) {
269 if (error) {
270 *error = rb_exc_new_str(rb_eArgError,
271 rb_sprintf("Unable to specify endianness for '%c'", type_char));
272 }
273 return -1;
274 }
275 if (*endianness != ENDIANNESS_NATIVE) {
276 *error = rb_exc_new_cstr(rb_eArgError, "Unable to use both '<' and '>' multiple times");
277 return -1;
278 }
279 *endianness = (format[i] == '<') ? ENDIANNESS_LITTLE : ENDIANNESS_BIG;
280 ++i;
281 continue;
282
283 default:
284 break;
285 }
286
287 break;
288 }
289
290 // parse count
291 int ch = format[i];
292 if ('0' <= ch && ch <= '9') {
293 ssize_t n = 0;
294 while ('0' <= (ch = format[i]) && ch <= '9') {
295 n = 10*n + ruby_digit36_to_number_table[ch];
296 ++i;
297 }
298 *count = n;
299 }
300
301 *next_format = &format[i];
302
303 switch (type_char) {
304 case 'x': // padding
305 return 1;
306
307 case 'c': // signed char
308 case 'C': // unsigned char
309 return sizeof(char);
310
311 case 's': // s for int16_t, s! for signed short
312 case 'S': // S for uint16_t, S! for unsigned short
313 if (*native_p) {
314 STRUCT_ALIGNOF(short, *alignment);
315 return sizeof(short);
316 }
317 // fall through
318
319 case 'n': // n for big-endian 16bit unsigned integer
320 case 'v': // v for little-endian 16bit unsigned integer
321 STRUCT_ALIGNOF(int16_t, *alignment);
322 return 2;
323
324 case 'i': // i and i! for signed int
325 case 'I': // I and I! for unsigned int
326 STRUCT_ALIGNOF(int, *alignment);
327 return sizeof(int);
328
329 case 'l': // l for int32_t, l! for signed long
330 case 'L': // L for uint32_t, L! for unsigned long
331 if (*native_p) {
332 STRUCT_ALIGNOF(long, *alignment);
333 return sizeof(long);
334 }
335 // fall through
336
337 case 'N': // N for big-endian 32bit unsigned integer
338 case 'V': // V for little-endian 32bit unsigned integer
339 STRUCT_ALIGNOF(int32_t, *alignment);
340 return 4;
341
342 case 'f': // f for native float
343 case 'e': // e for little-endian float
344 case 'g': // g for big-endian float
345 STRUCT_ALIGNOF(float, *alignment);
346 return sizeof(float);
347
348 case 'q': // q for int64_t, q! for signed long long
349 case 'Q': // Q for uint64_t, Q! for unsigned long long
350 if (*native_p) {
351 STRUCT_ALIGNOF(LONG_LONG, *alignment);
352 return sizeof(LONG_LONG);
353 }
354 STRUCT_ALIGNOF(int64_t, *alignment);
355 return 8;
356
357 case 'd': // d for native double
358 case 'E': // E for little-endian double
359 case 'G': // G for big-endian double
360 STRUCT_ALIGNOF(double, *alignment);
361 return sizeof(double);
362
363 case 'j': // j for intptr_t
364 case 'J': // J for uintptr_t
365 STRUCT_ALIGNOF(intptr_t, *alignment);
366 return sizeof(intptr_t);
367
368 default:
369 *alignment = -1;
370 if (error) {
371 *error = rb_exc_new_str(rb_eArgError, rb_sprintf("Invalid type character '%c'", type_char));
372 }
373 return -1;
374 }
375}
376
377static inline ssize_t
378calculate_padding(ssize_t total, ssize_t alignment_size)
379{
380 if (alignment_size > 1) {
381 ssize_t res = total % alignment_size;
382 if (res > 0) {
383 return alignment_size - res;
384 }
385 }
386 return 0;
387}
388
389ssize_t
392 size_t *n_members, const char **err)
393{
394 if (format == NULL) return 1;
395
396 VALUE error = Qnil;
397 ssize_t total = 0;
398 size_t len = 0;
399 bool alignment = false;
400 ssize_t max_alignment_size = 0;
401
402 const char *p = format;
403 if (*p == '|') { // alignment specifier
404 alignment = true;
405 ++format;
406 ++p;
407 }
408 while (*p) {
409 const char *q = p;
410
411 // ignore spaces
412 if (ISSPACE(*p)) {
413 while (ISSPACE(*p)) ++p;
414 continue;
415 }
416
417 bool native_size_p = false;
418 ssize_t alignment_size = 0;
419 endianness_t endianness = ENDIANNESS_NATIVE;
420 ssize_t count = 0;
421 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, &error);
422 if (size < 0) {
423 if (err) *err = q;
424 return -1;
425 }
426 if (max_alignment_size < alignment_size) {
427 max_alignment_size = alignment_size;
428 }
429
430 const ssize_t padding = alignment ? calculate_padding(total, alignment_size) : 0;
431 total += padding + size * count;
432
433 if (*q != 'x') {
434 ++len;
435 }
436 }
437
438 // adjust total size with the alignment size of the largest element
439 if (alignment && max_alignment_size > 0) {
440 const ssize_t padding = calculate_padding(total, max_alignment_size);
441 total += padding;
442 }
443
444 if (members && n_members) {
446
447 ssize_t i = 0, offset = 0;
448 const char *p = format;
449 while (*p) {
450 const int type_char = *p;
451
452 bool native_size_p;
453 ssize_t alignment_size = 0;
454 endianness_t endianness = ENDIANNESS_NATIVE;
455 ssize_t count = 0;
456 const ssize_t size = get_format_size(p, &native_size_p, &alignment_size, &endianness, &count, &p, NULL);
457
458 const ssize_t padding = alignment ? calculate_padding(offset, alignment_size) : 0;
459 offset += padding;
460
461 if (type_char != 'x') {
462#ifdef WORDS_BIGENDIAN
463 bool little_endian_p = (endianness == ENDIANNESS_LITTLE);
464#else
465 bool little_endian_p = (endianness != ENDIANNESS_BIG);
466#endif
467
468 switch (type_char) {
469 case 'e':
470 case 'E':
471 case 'v':
472 case 'V':
473 little_endian_p = true;
474 break;
475 case 'g':
476 case 'G':
477 case 'n':
478 case 'N':
479 little_endian_p = false;
480 break;
481 default:
482 break;
483 }
484
486 .format = type_char,
487 .native_size_p = native_size_p,
488 .little_endian_p = little_endian_p,
489 .offset = offset,
490 .size = size,
491 .repeat = count
492 };
493 }
494
495 offset += size * count;
496 }
497
498 *members = buf;
499 *n_members = len;
500 }
501
502 return total;
503}
504
505/* Return the item size. */
506ssize_t
507rb_memory_view_item_size_from_format(const char *format, const char **err)
508{
509 return rb_memory_view_parse_item_format(format, NULL, NULL, err);
510}
511
512/* Return the pointer to the item located by the given indices. */
513void *
515{
516 uint8_t *ptr = view->data;
517
518 if (view->ndim == 1) {
519 ssize_t stride = view->strides != NULL ? view->strides[0] : view->item_size;
520 return ptr + indices[0] * stride;
521 }
522
523 assert(view->shape != NULL);
524
525 ssize_t i;
526 if (view->strides == NULL) {
527 // row-major contiguous array
528 ssize_t stride = view->item_size;
529 for (i = 0; i < view->ndim; ++i) {
530 stride *= view->shape[i];
531 }
532 for (i = 0; i < view->ndim; ++i) {
533 stride /= view->shape[i];
534 ptr += indices[i] * stride;
535 }
536 }
537 else if (view->sub_offsets == NULL) {
538 // flat strided array
539 for (i = 0; i < view->ndim; ++i) {
540 ptr += indices[i] * view->strides[i];
541 }
542 }
543 else {
544 // indirect strided array
545 for (i = 0; i < view->ndim; ++i) {
546 ptr += indices[i] * view->strides[i];
547 if (view->sub_offsets[i] >= 0) {
548 ptr = *(uint8_t **)ptr + view->sub_offsets[i];
549 }
550 }
551 }
552
553 return ptr;
554}
555
556static void
557switch_endianness(uint8_t *buf, ssize_t len)
558{
559 RUBY_ASSERT(buf != NULL);
560 RUBY_ASSERT(len >= 0);
561
562 uint8_t *p = buf;
563 uint8_t *q = buf + len - 1;
564
565 while (q - p > 0) {
566 uint8_t t = *p;
567 *p = *q;
568 *q = t;
569 ++p;
570 --q;
571 }
572}
573
574static inline VALUE
575extract_item_member(const uint8_t *ptr, const rb_memory_view_item_component_t *member, const size_t i)
576{
577 RUBY_ASSERT(ptr != NULL);
578 RUBY_ASSERT(member != NULL);
579 RUBY_ASSERT(i < member->repeat);
580
581#ifdef WORDS_BIGENDIAN
582 const bool native_endian_p = !member->little_endian_p;
583#else
584 const bool native_endian_p = member->little_endian_p;
585#endif
586
587 const uint8_t *p = ptr + member->offset + i * member->size;
588
589 if (member->format == 'c') {
590 return INT2FIX(*(char *)p);
591 }
592 else if (member->format == 'C') {
593 return INT2FIX(*(unsigned char *)p);
594 }
595
596 union {
597 short s;
598 unsigned short us;
599 int i;
600 unsigned int ui;
601 long l;
602 unsigned long ul;
603 LONG_LONG ll;
604 unsigned LONG_LONG ull;
605 int16_t i16;
606 uint16_t u16;
607 int32_t i32;
608 uint32_t u32;
609 int64_t i64;
610 uint64_t u64;
611 intptr_t iptr;
612 uintptr_t uptr;
613 float f;
614 double d;
615 } val;
616
617 if (!native_endian_p) {
618 MEMCPY(&val, p, uint8_t, member->size);
619 switch_endianness((uint8_t *)&val, member->size);
620 }
621 else {
622 MEMCPY(&val, p, uint8_t, member->size);
623 }
624
625 switch (member->format) {
626 case 's':
627 if (member->native_size_p) {
628 return INT2FIX(val.s);
629 }
630 else {
631 return INT2FIX(val.i16);
632 }
633
634 case 'S':
635 case 'n':
636 case 'v':
637 if (member->native_size_p) {
638 return UINT2NUM(val.us);
639 }
640 else {
641 return INT2FIX(val.u16);
642 }
643
644 case 'i':
645 return INT2NUM(val.i);
646
647 case 'I':
648 return UINT2NUM(val.ui);
649
650 case 'l':
651 if (member->native_size_p) {
652 return LONG2NUM(val.l);
653 }
654 else {
655 return LONG2NUM(val.i32);
656 }
657
658 case 'L':
659 case 'N':
660 case 'V':
661 if (member->native_size_p) {
662 return ULONG2NUM(val.ul);
663 }
664 else {
665 return ULONG2NUM(val.u32);
666 }
667
668 case 'f':
669 case 'e':
670 case 'g':
671 return DBL2NUM(val.f);
672
673 case 'q':
674 if (member->native_size_p) {
675 return LL2NUM(val.ll);
676 }
677 else {
678#if SIZEOF_INT64_T == SIZEOF_LONG
679 return LONG2NUM(val.i64);
680#else
681 return LL2NUM(val.i64);
682#endif
683 }
684
685 case 'Q':
686 if (member->native_size_p) {
687 return ULL2NUM(val.ull);
688 }
689 else {
690#if SIZEOF_UINT64_T == SIZEOF_LONG
691 return ULONG2NUM(val.u64);
692#else
693 return ULL2NUM(val.u64);
694#endif
695 }
696
697 case 'd':
698 case 'E':
699 case 'G':
700 return DBL2NUM(val.d);
701
702 case 'j':
703 return INTPTR2NUM(val.iptr);
704
705 case 'J':
706 return UINTPTR2NUM(val.uptr);
707
708 default:
710 }
711}
712
713/* Return a value of the extracted member. */
714VALUE
715rb_memory_view_extract_item_member(const void *ptr, const rb_memory_view_item_component_t *member, const size_t i)
716{
717 if (ptr == NULL) return Qnil;
718 if (member == NULL) return Qnil;
719 if (i >= member->repeat) return Qnil;
720
721 return extract_item_member(ptr, member, i);
722}
723
724/* Return a value that consists of item members.
725 * When an item is a single member, the return value is a single value.
726 * When an item consists of multiple members, an array will be returned. */
727VALUE
728rb_memory_view_extract_item_members(const void *ptr, const rb_memory_view_item_component_t *members, const size_t n_members)
729{
730 if (ptr == NULL) return Qnil;
731 if (members == NULL) return Qnil;
732 if (n_members == 0) return Qnil;
733
734 if (n_members == 1 && members[0].repeat == 1) {
735 return rb_memory_view_extract_item_member(ptr, members, 0);
736 }
737
738 size_t i, j;
739 VALUE item = rb_ary_new();
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);
743 rb_ary_push(item, v);
744 }
745 }
746
747 return item;
748}
749
750void
752{
753 if (view->item_desc.components == NULL) {
754 const char *err;
755 rb_memory_view_item_component_t **p_components =
757 ssize_t n = rb_memory_view_parse_item_format(view->format, p_components, &view->item_desc.length, &err);
758 if (n < 0) {
759 rb_raise(rb_eRuntimeError,
760 "Unable to parse item format at %"PRIdSIZE" in \"%s\"",
761 (err - view->format), view->format);
762 }
763 }
764}
765
766/* Return a value that consists of item members in the given memory view. */
767VALUE
768rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices)
769{
770 void *ptr = rb_memory_view_get_item_pointer(view, indices);
771
772 if (view->format == NULL) {
773 return INT2FIX(*(uint8_t *)ptr);
774 }
775
776 if (view->item_desc.components == NULL) {
778 }
779
781}
782
783static const rb_memory_view_entry_t *
784lookup_memory_view_entry(VALUE klass)
785{
786 VALUE entry_obj = rb_ivar_lookup(klass, id_memory_view, Qnil);
787 while (NIL_P(entry_obj)) {
788 klass = rb_class_superclass(klass);
789
790 if (klass == rb_cBasicObject || klass == rb_cObject)
791 return NULL;
792
793 entry_obj = rb_ivar_lookup(klass, id_memory_view, Qnil);
794 }
795
796 if (! rb_typeddata_is_kind_of(entry_obj, &memory_view_entry_data_type))
797 return NULL;
798
799 return (const rb_memory_view_entry_t *)RTYPEDDATA_DATA(entry_obj);
800}
801
802/* Examine whether the given object supports memory view. */
803bool
805{
806 VALUE klass = CLASS_OF(obj);
807 const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
808 if (entry)
809 return (* entry->available_p_func)(obj);
810 else
811 return false;
812}
813
814/* Obtain a memory view from obj, and substitute the information to view. */
815bool
817{
818 VALUE klass = CLASS_OF(obj);
819 const rb_memory_view_entry_t *entry = lookup_memory_view_entry(klass);
820 if (entry) {
821 if (!(*entry->available_p_func)(obj)) {
822 return false;
823 }
824
825 bool rv = (*entry->get_func)(obj, view, flags);
826 if (rv) {
827 view->_memory_view_entry = entry;
828 register_exported_object(view->obj);
829 }
830 return rv;
831 }
832 else
833 return false;
834}
835
836/* Release the memory view obtained from obj. */
837bool
839{
840 const rb_memory_view_entry_t *entry = view->_memory_view_entry;
841 if (entry) {
842 bool rv = true;
843 if (entry->release_func) {
844 rv = (*entry->release_func)(view->obj, view);
845 }
846 if (rv) {
847 unregister_exported_object(view->obj);
848 view->obj = Qnil;
850 }
851 return rv;
852 }
853 else
854 return false;
855}
856
857void
858Init_MemoryView(void)
859{
860 exported_object_table = rb_init_identtable();
861
862 // exported_object_table is referred through rb_memory_view_exported_object_registry
863 // in -test-/memory_view extension.
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;
869
870 id_memory_view = rb_intern_const("__memory_view__");
871}
#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:1425
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:1478
void rb_warning(const char *fmt,...)
Issues a warning.
Definition error.c:498
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
Definition object.c:2311
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:59
#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:2064
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:531
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:229
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.
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.
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:433