Ruby  3.4.0dev (2024-11-05 revision 348a53415339076afc4a02fcd09f3ae36e9c4c61)
memory_view.c (348a53415339076afc4a02fcd09f3ae36e9c4c61)
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 
34 static st_table *exported_object_table = NULL;
35 VALUE rb_memory_view_exported_object_registry = Qundef;
36 
37 static int
38 exported_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 
44 static void
45 exported_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 
51 static void
52 exported_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 
61 const 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 
71 static int
72 exported_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 
85 static int
86 exported_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 
99 static void
100 register_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 
107 static void
108 unregister_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 
118 static ID id_memory_view;
119 
120 static 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 */
131 bool
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. */
148 bool
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. */
164 bool
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. */
180 void
181 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)
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 */
199 bool
200 rb_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
220 static const char native_types[] = "sSiIlLqQjJ";
221 #else
222 static const char native_types[] = "sSiIlLjJ";
223 #endif
224 static const char endianness_types[] = "sSiIlLqQjJ";
225 
226 typedef enum {
227  ENDIANNESS_NATIVE,
228  ENDIANNESS_LITTLE,
229  ENDIANNESS_BIG
230 } endianness_t;
231 
232 static ssize_t
233 get_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 
376 static inline ssize_t
377 calculate_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 
388 ssize_t
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) {
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. */
505 ssize_t
506 rb_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. */
512 void *
513 rb_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 
555 static void
556 switch_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 
573 static inline VALUE
574 extract_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:
709  }
710 }
711 
712 /* Return a value of the extracted member. */
713 VALUE
714 rb_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
727 rb_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 
749 void
751 {
752  if (view->item_desc.components == NULL) {
753  const char *err;
754  rb_memory_view_item_component_t **p_components =
756  ssize_t n = rb_memory_view_parse_item_format(view->format, p_components, &view->item_desc.length, &err);
757  if (n < 0) {
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
767 rb_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) {
777  }
778 
780 }
781 
782 static const rb_memory_view_entry_t *
783 lookup_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. */
802 bool
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. */
814 bool
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. */
836 bool
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 
856 void
857 Init_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.
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 }
#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 xfree
Old name of ruby_xfree.
Definition: xmalloc.h:58
#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:203
#define ALLOC_N
Old name of RB_ALLOC_N.
Definition: memory.h:394
#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
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition: error.c:3627
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:1353
VALUE rb_eRuntimeError
RuntimeError exception.
Definition: error.c:1401
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:1454
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1404
void rb_warning(const char *fmt,...)
Issues a warning.
Definition: error.c:496
VALUE rb_class_superclass(VALUE klass)
Queries the parent of the given class.
Definition: object.c:2147
VALUE rb_cBasicObject
BasicObject class.
Definition: object.c:64
void rb_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:2094
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition: array.c:747
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
Definition: array.c:1384
#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
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:1859
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition: symbol.h:276
char * ptr
Pointer to the underlying memory region, of at least capa bytes.
Definition: io.h:2
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'->11` etc.
Definition: util.c:81
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition: sprintf.c:1217
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition: memory.h:367
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.
Definition: memory_view.c:513
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
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
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
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
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
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
bool rb_memory_view_available_p(VALUE obj)
Return true if obj supports to export a MemoryView.
Definition: memory_view.c:803
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
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
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
VALUE rb_memory_view_get_item(rb_memory_view_t *view, const ssize_t *indices)
Definition: memory_view.c:767
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
void rb_memory_view_prepare_item_desc(rb_memory_view_t *view)
Fill the item_desc member of view.
Definition: memory_view.c:750
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
Definition: cxxanyargs.hpp:432
#define RTYPEDDATA_DATA(v)
Convenient getter macro.
Definition: rtypeddata.h:102
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition: rtypeddata.h:449
This is the struct that holds necessary info for a struct.
Definition: rtypeddata.h:200
Operations applied to a specific kind of a memory view.
Definition: memory_view.h:166
rb_memory_view_get_func_t get_func
Exports a memory view from a Ruby object.
Definition: memory_view.h:170
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_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
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.
Definition: memory_view.h:125
ssize_t item_size
The number of bytes in each element.
Definition: memory_view.h:116
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
ssize_t ndim
The number of dimension.
Definition: memory_view.h:132
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
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.
Definition: memory_view.h:147
const ssize_t * shape
ndim size array indicating the number of elements in each dimension.
Definition: memory_view.h:137
void * private_data
The private data for managing this exported memory.
Definition: memory_view.h:150
size_t length
The number of components in an item.
Definition: memory_view.h:128
const char * format
A string to describe the format of an element, or NULL for unsigned bytes.
Definition: memory_view.h:111
ssize_t byte_size
The number of bytes in data.
Definition: memory_view.h:87
struct rb_memory_view_t::@54 item_desc
Description of each components.
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