carray 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +5 -25
  3. data/CHANGELOG.md +16 -0
  4. data/LICENSE +1 -1
  5. data/NEWS.md +3 -0
  6. data/README.md +128 -44
  7. data/carray.gemspec +22 -24
  8. data/ext/ca_array_pool.c +91 -0
  9. data/ext/ca_axis_descriptor.h +186 -0
  10. data/ext/ca_axis_dispatch.c +924 -0
  11. data/ext/ca_axis_group.c +1208 -0
  12. data/ext/ca_bincmp_dispatch.c +76 -0
  13. data/ext/ca_bincmp_dispatch.h +85 -0
  14. data/ext/ca_binop_dispatch.c +125 -0
  15. data/ext/ca_binop_dispatch.h +159 -0
  16. data/ext/ca_categorical_iterator.c +1375 -0
  17. data/ext/ca_compare.c +94 -0
  18. data/ext/ca_compare.h +26 -0
  19. data/ext/ca_composite_dispatch.c +414 -0
  20. data/ext/ca_composite_dispatch.h +116 -0
  21. data/ext/ca_for_buffer.h +96 -0
  22. data/ext/ca_for_each_element.h +241 -0
  23. data/ext/ca_group_iter.c +304 -0
  24. data/ext/ca_iter_substrate.h +325 -0
  25. data/ext/ca_kernel_iterator.c +4321 -0
  26. data/ext/ca_kernel_iterator.h +2603 -0
  27. data/ext/ca_moncmp_dispatch.c +37 -0
  28. data/ext/ca_moncmp_dispatch.h +62 -0
  29. data/ext/ca_monop_dispatch.c +200 -0
  30. data/ext/ca_monop_dispatch.h +235 -0
  31. data/ext/ca_obj_array.c +355 -359
  32. data/ext/ca_obj_bincmp.c +809 -0
  33. data/ext/ca_obj_binop.c +892 -0
  34. data/ext/ca_obj_bitarray.c +369 -164
  35. data/ext/ca_obj_bitfield.c +294 -234
  36. data/ext/ca_obj_block.c +189 -711
  37. data/ext/ca_obj_byte_swap.c +766 -0
  38. data/ext/ca_obj_const_string.c +965 -0
  39. data/ext/ca_obj_face.c +670 -0
  40. data/ext/ca_obj_face.h +247 -0
  41. data/ext/ca_obj_fake.c +228 -100
  42. data/ext/ca_obj_farray.c +54 -441
  43. data/ext/ca_obj_field.c +82 -529
  44. data/ext/ca_obj_fixlen_string.c +306 -0
  45. data/ext/ca_obj_grid.c +858 -440
  46. data/ext/ca_obj_meld.c +1034 -0
  47. data/ext/ca_obj_moncmp.c +569 -0
  48. data/ext/ca_obj_monop.c +1111 -0
  49. data/ext/ca_obj_object.c +774 -298
  50. data/ext/ca_obj_record.c +468 -0
  51. data/ext/ca_obj_reduce.c +97 -82
  52. data/ext/ca_obj_refer.c +569 -459
  53. data/ext/ca_obj_remap.c +475 -0
  54. data/ext/ca_obj_repeat.c +92 -477
  55. data/ext/ca_obj_roll.c +616 -0
  56. data/ext/ca_obj_select.c +344 -296
  57. data/ext/ca_obj_select_axis.c +1296 -0
  58. data/ext/ca_obj_shift.c +230 -792
  59. data/ext/ca_obj_source.c +78 -0
  60. data/ext/ca_obj_stack.c +1173 -0
  61. data/ext/ca_obj_stride.c +2501 -0
  62. data/ext/ca_obj_string.c +268 -0
  63. data/ext/ca_obj_tile.c +614 -0
  64. data/ext/ca_obj_time.c +546 -0
  65. data/ext/ca_obj_timedelta.c +435 -0
  66. data/ext/ca_obj_transpose.c +62 -516
  67. data/ext/ca_obj_triop.c +746 -0
  68. data/ext/ca_obj_unbound_repeat.c +208 -241
  69. data/ext/ca_obj_window.c +1131 -563
  70. data/ext/ca_op_byte_swap.c +175 -0
  71. data/ext/ca_op_ipower.c +319 -0
  72. data/ext/ca_op_powi.h +88 -0
  73. data/ext/ca_sort_kernels.h +132 -0
  74. data/ext/ca_sweep_engine.c +430 -0
  75. data/ext/ca_sweep_engine.h +157 -0
  76. data/ext/ca_transform_common.c +228 -0
  77. data/ext/ca_triop_dispatch.c +55 -0
  78. data/ext/ca_triop_dispatch.h +62 -0
  79. data/ext/carray.h +795 -402
  80. data/ext/carray_access.c +831 -711
  81. data/ext/carray_attribute.c +98 -330
  82. data/ext/carray_bincount.c +255 -0
  83. data/ext/carray_broadcast.c +283 -0
  84. data/ext/carray_call_cfunc.c +1360 -828
  85. data/ext/carray_call_cfunc.h +160 -0
  86. data/ext/carray_cast.c +1212 -301
  87. data/ext/carray_cast_func.rb +81 -40
  88. data/ext/carray_class.c +53 -63
  89. data/ext/carray_config.h +28 -0
  90. data/ext/carray_conversion.c +350 -346
  91. data/ext/carray_copy.c +156 -268
  92. data/ext/carray_core.c +1342 -199
  93. data/ext/carray_count.c +312 -0
  94. data/ext/carray_data_type.c +43 -19
  95. data/ext/carray_element.c +585 -213
  96. data/ext/carray_factorize.c +2542 -0
  97. data/ext/carray_generate.c +230 -559
  98. data/ext/carray_histogram.c +490 -0
  99. data/ext/carray_hold.c +228 -0
  100. data/ext/carray_index_classifier.c +1035 -0
  101. data/ext/carray_index_classifier.h +27 -0
  102. data/ext/carray_internal.h +120 -0
  103. data/ext/carray_kernels_bincmp.c +4445 -0
  104. data/ext/carray_kernels_binop.c +10979 -0
  105. data/ext/carray_kernels_init.c +36 -0
  106. data/ext/carray_kernels_map.c +3466 -0
  107. data/ext/carray_kernels_moncmp.c +2096 -0
  108. data/ext/carray_kernels_monop.c +18312 -0
  109. data/ext/carray_kernels_reduce_aggregate.c +25836 -0
  110. data/ext/carray_kernels_reduce_boolean.c +329 -0
  111. data/ext/carray_kernels_reduce_cumulative.c +14592 -0
  112. data/ext/carray_kernels_reduce_extreme.c +16947 -0
  113. data/ext/carray_kernels_reduce_variance.c +3909 -0
  114. data/ext/carray_kernels_scan.c +3692 -0
  115. data/ext/carray_kernels_search.c +32137 -0
  116. data/ext/carray_kernels_sort.c +10625 -0
  117. data/ext/carray_kernels_triop.c +1391 -0
  118. data/ext/carray_lazy.c +567 -0
  119. data/ext/carray_loop.c +88 -200
  120. data/ext/carray_mask.c +848 -154
  121. data/ext/carray_math_kernel.h +120 -0
  122. data/ext/carray_mathfunc.c +10 -241
  123. data/ext/carray_median_percentile.c +1257 -0
  124. data/ext/carray_memory_view.c +1625 -0
  125. data/ext/carray_operator.c +1526 -318
  126. data/ext/carray_order.c +664 -1394
  127. data/ext/carray_partition.c +416 -0
  128. data/ext/carray_random.c +518 -0
  129. data/ext/carray_scatter.c +357 -0
  130. data/ext/carray_slab.c +1219 -0
  131. data/ext/carray_slab.h +84 -0
  132. data/ext/carray_sort.c +829 -0
  133. data/ext/carray_sort_kernel.c +620 -0
  134. data/ext/carray_struct.c +695 -0
  135. data/ext/carray_test.c +343 -229
  136. data/ext/carray_undef.c +34 -17
  137. data/ext/carray_utils.c +175 -74
  138. data/ext/extconf.rb +216 -55
  139. data/ext/mk_call_cfunc.rb +480 -0
  140. data/ext/mkkernel.rb +8842 -0
  141. data/ext/ruby_carray.c +202 -101
  142. data/ext/version.h +4 -14
  143. data/ext/version.rb +5 -13
  144. data/lib/carray/arrow_tensor.rb +401 -0
  145. data/lib/carray/attribute.rb +166 -0
  146. data/lib/carray/autoload_carray.rb +220 -0
  147. data/lib/carray/autoload_method_extension.rb +44 -0
  148. data/lib/carray/axis_group.rb +711 -0
  149. data/lib/carray/basics.rb +481 -0
  150. data/lib/carray/bincount_nd.rb +358 -0
  151. data/lib/carray/block_iterator.rb +604 -0
  152. data/lib/carray/boolean_reduce.rb +109 -0
  153. data/lib/carray/categorical.rb +561 -0
  154. data/lib/carray/categorical_iterator.rb +1062 -0
  155. data/lib/carray/complex.rb +150 -0
  156. data/lib/carray/conditional.rb +216 -0
  157. data/lib/carray/const_string.rb +228 -0
  158. data/lib/carray/construct.rb +139 -328
  159. data/lib/carray/core_extensions.rb +240 -0
  160. data/lib/carray/data_type_extension.rb +233 -0
  161. data/lib/carray/fixlen_string.rb +95 -0
  162. data/lib/carray/frame/concat.rb +132 -0
  163. data/lib/carray/frame/convert.rb +95 -0
  164. data/lib/carray/frame/csv_parser.rb +211 -0
  165. data/lib/carray/frame/frame.rb +649 -0
  166. data/lib/carray/frame/group.rb +186 -0
  167. data/lib/carray/frame/io.rb +164 -0
  168. data/lib/carray/frame/join.rb +248 -0
  169. data/lib/carray/frame/records.rb +99 -0
  170. data/lib/carray/frame/sort.rb +113 -0
  171. data/lib/carray/frame/verbs.rb +299 -0
  172. data/lib/carray/frame.rb +16 -0
  173. data/lib/carray/histogram.rb +512 -0
  174. data/lib/carray/inspect.rb +37 -20
  175. data/lib/carray/iterator.rb +57 -349
  176. data/lib/carray/lazy.rb +889 -0
  177. data/lib/carray/mask_gap_fill.rb +200 -0
  178. data/lib/carray/math.rb +78 -342
  179. data/lib/carray/meld_reduce.rb +289 -0
  180. data/lib/carray/methods/align_addr.rb +116 -0
  181. data/lib/carray/methods/bin.rb +128 -0
  182. data/lib/carray/methods/bincount.rb +87 -0
  183. data/lib/carray/methods/bit_string.rb +92 -0
  184. data/lib/carray/methods/broadcast.rb +63 -0
  185. data/lib/carray/methods/choose.rb +39 -0
  186. data/lib/carray/methods/composition.rb +280 -0
  187. data/lib/carray/methods/gather_nd.rb +206 -0
  188. data/lib/carray/methods/index.rb +39 -0
  189. data/lib/carray/methods/insert_block.rb +99 -0
  190. data/lib/carray/methods/is_in.rb +141 -0
  191. data/lib/carray/methods/join.rb +90 -0
  192. data/lib/carray/methods/locate_addr.rb +47 -0
  193. data/lib/carray/methods/mask_duplicates.rb +41 -0
  194. data/lib/carray/methods/meshgrid.rb +91 -0
  195. data/lib/carray/methods/mode.rb +126 -0
  196. data/lib/carray/methods/nunique.rb +46 -0
  197. data/lib/carray/methods/resize.rb +56 -0
  198. data/lib/carray/methods/snap.rb +156 -0
  199. data/lib/carray/methods/string_format.rb +57 -0
  200. data/lib/carray/methods/unique.rb +47 -0
  201. data/lib/carray/methods/value_counts.rb +71 -0
  202. data/lib/carray/mkmf.rb +124 -101
  203. data/lib/carray/runtime.rb +108 -0
  204. data/lib/carray/serialize.rb +478 -167
  205. data/lib/carray/slab_iterator.rb +292 -0
  206. data/lib/carray/stack.rb +291 -0
  207. data/lib/carray/string.rb +56 -180
  208. data/lib/carray/string_operation_extension.rb +289 -0
  209. data/lib/carray/struct.rb +335 -323
  210. data/lib/carray/struct_builder.rb +697 -0
  211. data/lib/carray/table.rb +41 -2
  212. data/lib/carray/time.rb +2255 -38
  213. data/lib/carray/window_iterator.rb +655 -0
  214. data/lib/carray.rb +55 -57
  215. metadata +163 -130
  216. data/Rakefile +0 -51
  217. data/TODO.md +0 -18
  218. data/ext/ca_iter_block.c +0 -257
  219. data/ext/ca_iter_dimension.c +0 -299
  220. data/ext/ca_iter_window.c +0 -214
  221. data/ext/ca_obj_mapping.c +0 -644
  222. data/ext/carray_iterator.c +0 -641
  223. data/ext/carray_math.rb +0 -850
  224. data/ext/carray_numeric.c +0 -259
  225. data/ext/carray_sort_addr.c +0 -254
  226. data/ext/carray_stat.c +0 -2100
  227. data/ext/carray_stat_proc.rb +0 -1999
  228. data/ext/mkmath.rb +0 -741
  229. data/ext/ruby_ccomplex.c +0 -509
  230. data/ext/ruby_float_func.c +0 -86
  231. data/lib/carray/array.rb +0 -8
  232. data/lib/carray/autoload/autoload_base.rb +0 -19
  233. data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
  234. data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
  235. data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
  236. data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
  237. data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
  238. data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
  239. data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
  240. data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
  241. data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
  242. data/lib/carray/autoload/autoload_gem_random.rb +0 -8
  243. data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
  244. data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
  245. data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
  246. data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
  247. data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
  248. data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
  249. data/lib/carray/autoload/autoload_object_link.rb +0 -1
  250. data/lib/carray/autoload/autoload_object_pack.rb +0 -2
  251. data/lib/carray/autoload.rb +0 -141
  252. data/lib/carray/basic.rb +0 -191
  253. data/lib/carray/broadcast.rb +0 -101
  254. data/lib/carray/compose.rb +0 -315
  255. data/lib/carray/convert.rb +0 -115
  256. data/lib/carray/info.rb +0 -110
  257. data/lib/carray/io/imagemagick.rb +0 -235
  258. data/lib/carray/mask.rb +0 -102
  259. data/lib/carray/math/histogram.rb +0 -177
  260. data/lib/carray/math/recurrence.rb +0 -93
  261. data/lib/carray/object/ca_obj_iterator.rb +0 -50
  262. data/lib/carray/object/ca_obj_link.rb +0 -50
  263. data/lib/carray/object/ca_obj_pack.rb +0 -99
  264. data/lib/carray/obsolete.rb +0 -256
  265. data/lib/carray/ordering.rb +0 -181
  266. data/lib/carray/testing.rb +0 -51
  267. data/lib/carray/transform.rb +0 -109
  268. data/misc/Methods.ja.md +0 -182
  269. data/misc/NOTE +0 -51
  270. data/spec/Classes/CABitfield_spec.rb +0 -58
  271. data/spec/Classes/CABlockIterator_spec.rb +0 -114
  272. data/spec/Classes/CABlock_spec.rb +0 -205
  273. data/spec/Classes/CAField_spec.rb +0 -39
  274. data/spec/Classes/CAGrid_spec.rb +0 -75
  275. data/spec/Classes/CAMap_spec.rb +0 -0
  276. data/spec/Classes/CAMapping_spec.rb +0 -105
  277. data/spec/Classes/CAObject_attribute_spec.rb +0 -33
  278. data/spec/Classes/CAObject_spec.rb +0 -33
  279. data/spec/Classes/CARefer_spec.rb +0 -93
  280. data/spec/Classes/CARepeat_spec.rb +0 -65
  281. data/spec/Classes/CASelect_spec.rb +0 -22
  282. data/spec/Classes/CAShift_spec.rb +0 -16
  283. data/spec/Classes/CAStruct_spec.rb +0 -71
  284. data/spec/Classes/CATranspose_spec.rb +0 -60
  285. data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
  286. data/spec/Classes/CAWindow_spec.rb +0 -54
  287. data/spec/Classes/CAWrap_spec.rb +0 -8
  288. data/spec/Classes/CArray_spec.rb +0 -184
  289. data/spec/Classes/CScalar_spec.rb +0 -55
  290. data/spec/Classes/ex1.rb +0 -46
  291. data/spec/Features/feature_130_spec.rb +0 -19
  292. data/spec/Features/feature_attributes_spec.rb +0 -280
  293. data/spec/Features/feature_boolean_spec.rb +0 -98
  294. data/spec/Features/feature_broadcast.rb +0 -116
  295. data/spec/Features/feature_cast_function.rb +0 -19
  296. data/spec/Features/feature_cast_spec.rb +0 -33
  297. data/spec/Features/feature_class_spec.rb +0 -84
  298. data/spec/Features/feature_complex_spec.rb +0 -42
  299. data/spec/Features/feature_composite_spec.rb +0 -124
  300. data/spec/Features/feature_convert_spec.rb +0 -46
  301. data/spec/Features/feature_copy_spec.rb +0 -123
  302. data/spec/Features/feature_creation_spec.rb +0 -84
  303. data/spec/Features/feature_element_spec.rb +0 -144
  304. data/spec/Features/feature_extream_spec.rb +0 -54
  305. data/spec/Features/feature_generate_spec.rb +0 -74
  306. data/spec/Features/feature_index_spec.rb +0 -69
  307. data/spec/Features/feature_mask_spec.rb +0 -580
  308. data/spec/Features/feature_math_spec.rb +0 -97
  309. data/spec/Features/feature_order_spec.rb +0 -146
  310. data/spec/Features/feature_ref_store_spec.rb +0 -209
  311. data/spec/Features/feature_serialization_spec.rb +0 -125
  312. data/spec/Features/feature_stat_spec.rb +0 -397
  313. data/spec/Features/feature_virtual_spec.rb +0 -48
  314. data/spec/Features/method_eq_spec.rb +0 -81
  315. data/spec/Features/method_is_nan_spec.rb +0 -12
  316. data/spec/Features/method_map_spec.rb +0 -54
  317. data/spec/Features/method_max_with.rb +0 -20
  318. data/spec/Features/method_min_with.rb +0 -19
  319. data/spec/Features/method_ne_spec.rb +0 -18
  320. data/spec/Features/method_project_spec.rb +0 -188
  321. data/spec/Features/method_ref_spec.rb +0 -27
  322. data/spec/Features/method_round_spec.rb +0 -11
  323. data/spec/Features/method_s_linspace_spec.rb +0 -48
  324. data/spec/Features/method_s_span_spec.rb +0 -14
  325. data/spec/Features/method_seq_spec.rb +0 -47
  326. data/spec/Features/method_sort_with.rb +0 -43
  327. data/spec/Features/method_sorted_with.rb +0 -29
  328. data/spec/Features/method_span_spec.rb +0 -42
  329. data/spec/Features/method_wrap_readonly_spec.rb +0 -43
  330. data/spec/UnitTest/test_CAVirtual.rb +0 -214
  331. data/spec/spec_all.rb +0 -10
  332. data/utils/ca_ase.rb +0 -21
  333. data/utils/ca_methods.rb +0 -15
  334. data/utils/cast_checker.rb +0 -30
  335. data/utils/convert_test.rb +0 -73
  336. data/utils/extract_yard.rb +0 -22
  337. data/utils/guess_shape.rb +0 -76
  338. data/utils/monkey_patch_methods.rb +0 -62
  339. data/utils/remove_resource_fork.sh +0 -5
@@ -0,0 +1,695 @@
1
+ /* ---------------------------------------------------------------------------
2
+
3
+ C-native member access for CAStruct subclasses. The hooked
4
+ methods (`[]`, `[]=`, `initialize`) are installed on a subclass by
5
+ `CArray.__install_castruct_methods__(klass)` — called from the
6
+ bottom of lib/carray/struct.rb once CAStruct is defined, so this
7
+ ext can load before that file.
8
+
9
+ Sibling of lib/carray/struct.rb (Ruby-side CAStruct::Builder that
10
+ populates the FAST_PRIMITIVES / DISPATCH_TABLE constants read
11
+ from here).
12
+
13
+ Dispatch order for both `[]` and `[]=`:
14
+
15
+ FAST_PRIMITIVES hit -> direct memcpy against @data.ptr + offset
16
+ (primitive / bitfield / endian-swap)
17
+ DISPATCH_TABLE hit -> Proc invoked via rb_funcall("call")
18
+ (nested struct / CArray template / fixlen)
19
+ miss -> send(name) / send(name=)
20
+
21
+ ---------------------------------------------------------------------------- */
22
+
23
+ #include "carray.h"
24
+ #include <string.h>
25
+
26
+ /* ---------------------------------------------------------------------
27
+ FAST_PRIMITIVES entry kind tags.
28
+ *
29
+ * CAREFUL: these constants are kept in sync with the Ruby-side
30
+ * CAStruct::Builder::FAST_KIND_* — an off-by-one drift silently
31
+ * mis-dispatches every access.
32
+ --------------------------------------------------------------------- */
33
+
34
+ #define FAST_KIND_PRIMITIVE 0
35
+ #define FAST_KIND_BITFIELD 1
36
+ #define FAST_KIND_ENDIAN 2
37
+
38
+ /* 16/32/64-bit byte swap via compiler builtins. Clang and GCC both
39
+ expose __builtin_bswap*, and the project's CFLAGS already require
40
+ a recent toolchain. */
41
+ #define CA_BSWAP16(x) ((uint16_t) __builtin_bswap16((uint16_t)(x)))
42
+ #define CA_BSWAP32(x) ((uint32_t) __builtin_bswap32((uint32_t)(x)))
43
+ #define CA_BSWAP64(x) ((uint64_t) __builtin_bswap64((uint64_t)(x)))
44
+
45
+ /* ---------------------------------------------------------------------
46
+ Type-coded primitive read / write. Type codes are the same
47
+ CA_INT8 / CA_UINT8 / ... constants used everywhere else in carray.
48
+ --------------------------------------------------------------------- */
49
+
50
+ static VALUE
51
+ ca_struct_read_primitive_fast (const char *p, int type_code)
52
+ {
53
+ switch (type_code) {
54
+ case CA_BOOLEAN: {
55
+ int8_t v;
56
+ memcpy(&v, p, 1);
57
+ return v ? INT2FIX(1) : INT2FIX(0);
58
+ }
59
+ case CA_INT8: {
60
+ int8_t v;
61
+ memcpy(&v, p, 1);
62
+ return INT2FIX(v);
63
+ }
64
+ case CA_UINT8: {
65
+ uint8_t v;
66
+ memcpy(&v, p, 1);
67
+ return INT2FIX(v);
68
+ }
69
+ case CA_INT16: {
70
+ int16_t v;
71
+ memcpy(&v, p, 2);
72
+ return INT2FIX(v);
73
+ }
74
+ case CA_UINT16: {
75
+ uint16_t v;
76
+ memcpy(&v, p, 2);
77
+ return INT2FIX(v);
78
+ }
79
+ case CA_INT32: {
80
+ int32_t v;
81
+ memcpy(&v, p, 4);
82
+ return LONG2NUM(v);
83
+ }
84
+ case CA_UINT32: {
85
+ uint32_t v;
86
+ memcpy(&v, p, 4);
87
+ return ULONG2NUM(v);
88
+ }
89
+ case CA_INT64: {
90
+ int64_t v;
91
+ memcpy(&v, p, 8);
92
+ return LL2NUM(v);
93
+ }
94
+ case CA_UINT64: {
95
+ uint64_t v;
96
+ memcpy(&v, p, 8);
97
+ return ULL2NUM(v);
98
+ }
99
+ case CA_FLOAT32: {
100
+ float v;
101
+ memcpy(&v, p, 4);
102
+ return DBL2NUM((double) v);
103
+ }
104
+ case CA_FLOAT64: {
105
+ double v;
106
+ memcpy(&v, p, 8);
107
+ return DBL2NUM(v);
108
+ }
109
+ default:
110
+ return Qundef; /* should not happen; FAST_PRIMITIVES filters */
111
+ }
112
+ }
113
+
114
+ static void
115
+ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
116
+ {
117
+ switch (type_code) {
118
+ case CA_BOOLEAN: {
119
+ int8_t v = RTEST(val) && val != INT2FIX(0) ? 1 : 0;
120
+ memcpy(p, &v, 1);
121
+ return;
122
+ }
123
+ case CA_INT8: {
124
+ int8_t v = (int8_t) NUM2INT(val);
125
+ memcpy(p, &v, 1);
126
+ return;
127
+ }
128
+ case CA_UINT8: {
129
+ uint8_t v = (uint8_t) NUM2UINT(val);
130
+ memcpy(p, &v, 1);
131
+ return;
132
+ }
133
+ case CA_INT16: {
134
+ int16_t v = (int16_t) NUM2INT(val);
135
+ memcpy(p, &v, 2);
136
+ return;
137
+ }
138
+ case CA_UINT16: {
139
+ uint16_t v = (uint16_t) NUM2UINT(val);
140
+ memcpy(p, &v, 2);
141
+ return;
142
+ }
143
+ case CA_INT32: {
144
+ int32_t v = (int32_t) NUM2LONG(val);
145
+ memcpy(p, &v, 4);
146
+ return;
147
+ }
148
+ case CA_UINT32: {
149
+ uint32_t v = (uint32_t) NUM2ULONG(val);
150
+ memcpy(p, &v, 4);
151
+ return;
152
+ }
153
+ case CA_INT64: {
154
+ int64_t v = (int64_t) NUM2LL(val);
155
+ memcpy(p, &v, 8);
156
+ return;
157
+ }
158
+ case CA_UINT64: {
159
+ uint64_t v = (uint64_t) NUM2ULL(val);
160
+ memcpy(p, &v, 8);
161
+ return;
162
+ }
163
+ case CA_FLOAT32: {
164
+ float v = (float) NUM2DBL(val);
165
+ memcpy(p, &v, 4);
166
+ return;
167
+ }
168
+ case CA_FLOAT64: {
169
+ double v = NUM2DBL(val);
170
+ memcpy(p, &v, 8);
171
+ return;
172
+ }
173
+ default:
174
+ rb_raise(rb_eRuntimeError,
175
+ "[BUG] ca_struct_write_primitive_fast: unsupported type %d",
176
+ type_code);
177
+ }
178
+ }
179
+
180
+ /* ---------------------------------------------------------------------
181
+ Bit-field read / write. Mirrors CABitfield: load the spanning
182
+ power-of-2 word (1/2/4/8 bytes) at start_byte, shift right by
183
+ bit_in_word, mask off `bits` bits. Writes use load-modify-store
184
+ to keep neighbouring bits untouched.
185
+ --------------------------------------------------------------------- */
186
+
187
+ static VALUE
188
+ ca_struct_read_bitfield_fast (const char *p, int view_bytes,
189
+ int bit_in_word, int bits)
190
+ {
191
+ uint64_t word = 0;
192
+ switch (view_bytes) {
193
+ case 1: { uint8_t v; memcpy(&v, p, 1); word = v; break; }
194
+ case 2: { uint16_t v; memcpy(&v, p, 2); word = v; break; }
195
+ case 4: { uint32_t v; memcpy(&v, p, 4); word = v; break; }
196
+ case 8: { uint64_t v; memcpy(&v, p, 8); word = v; break; }
197
+ default:
198
+ rb_raise(rb_eRuntimeError,
199
+ "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
200
+ }
201
+ uint64_t mask = (bits >= 64) ? ~(uint64_t)0
202
+ : ((uint64_t)1 << bits) - 1;
203
+ uint64_t val = (word >> bit_in_word) & mask;
204
+ /* Returned as an unsigned Integer; bit-fields don't carry a sign
205
+ bit in carray's CABitfield convention either. */
206
+ if (bits <= 31) return INT2FIX((long) val);
207
+ if (bits <= 32) return ULONG2NUM((unsigned long) val);
208
+ return ULL2NUM(val);
209
+ }
210
+
211
+ static void
212
+ ca_struct_write_bitfield_fast (char *p, int view_bytes,
213
+ int bit_in_word, int bits, VALUE val)
214
+ {
215
+ uint64_t v = NUM2ULL(val);
216
+ uint64_t mask = (bits >= 64) ? ~(uint64_t)0
217
+ : ((uint64_t)1 << bits) - 1;
218
+ v &= mask;
219
+ uint64_t shifted_mask = mask << bit_in_word;
220
+ uint64_t shifted_val = v << bit_in_word;
221
+ switch (view_bytes) {
222
+ case 1: {
223
+ uint8_t word;
224
+ memcpy(&word, p, 1);
225
+ word = (uint8_t)((word & ~(uint8_t)shifted_mask) | (uint8_t)shifted_val);
226
+ memcpy(p, &word, 1);
227
+ return;
228
+ }
229
+ case 2: {
230
+ uint16_t word;
231
+ memcpy(&word, p, 2);
232
+ word = (uint16_t)((word & ~(uint16_t)shifted_mask) | (uint16_t)shifted_val);
233
+ memcpy(p, &word, 2);
234
+ return;
235
+ }
236
+ case 4: {
237
+ uint32_t word;
238
+ memcpy(&word, p, 4);
239
+ word = (uint32_t)((word & ~(uint32_t)shifted_mask) | (uint32_t)shifted_val);
240
+ memcpy(p, &word, 4);
241
+ return;
242
+ }
243
+ case 8: {
244
+ uint64_t word;
245
+ memcpy(&word, p, 8);
246
+ word = (word & ~shifted_mask) | shifted_val;
247
+ memcpy(p, &word, 8);
248
+ return;
249
+ }
250
+ default:
251
+ rb_raise(rb_eRuntimeError,
252
+ "[BUG] bitfield view_bytes %d not in {1,2,4,8}", view_bytes);
253
+ }
254
+ }
255
+
256
+ /* ---------------------------------------------------------------------
257
+ Endian-swapped primitive read / write. Only entries whose target
258
+ endian differs from the host land here -- the Ruby Builder folds
259
+ host-matching cases into the plain primitive fast path. No view
260
+ allocation; one __builtin_bswap per access.
261
+ --------------------------------------------------------------------- */
262
+
263
+ static VALUE
264
+ ca_struct_read_endian_swapped_fast (const char *p, int type_code)
265
+ {
266
+ switch (type_code) {
267
+ case CA_INT16: {
268
+ uint16_t v; memcpy(&v, p, 2); v = CA_BSWAP16(v);
269
+ return INT2FIX((int16_t) v);
270
+ }
271
+ case CA_UINT16: {
272
+ uint16_t v; memcpy(&v, p, 2); v = CA_BSWAP16(v);
273
+ return INT2FIX(v);
274
+ }
275
+ case CA_INT32: {
276
+ uint32_t v; memcpy(&v, p, 4); v = CA_BSWAP32(v);
277
+ return LONG2NUM((int32_t) v);
278
+ }
279
+ case CA_UINT32: {
280
+ uint32_t v; memcpy(&v, p, 4); v = CA_BSWAP32(v);
281
+ return ULONG2NUM(v);
282
+ }
283
+ case CA_INT64: {
284
+ uint64_t v; memcpy(&v, p, 8); v = CA_BSWAP64(v);
285
+ return LL2NUM((int64_t) v);
286
+ }
287
+ case CA_UINT64: {
288
+ uint64_t v; memcpy(&v, p, 8); v = CA_BSWAP64(v);
289
+ return ULL2NUM(v);
290
+ }
291
+ case CA_FLOAT32: {
292
+ uint32_t bits; memcpy(&bits, p, 4); bits = CA_BSWAP32(bits);
293
+ float v; memcpy(&v, &bits, 4);
294
+ return DBL2NUM((double) v);
295
+ }
296
+ case CA_FLOAT64: {
297
+ uint64_t bits; memcpy(&bits, p, 8); bits = CA_BSWAP64(bits);
298
+ double v; memcpy(&v, &bits, 8);
299
+ return DBL2NUM(v);
300
+ }
301
+ default:
302
+ rb_raise(rb_eRuntimeError,
303
+ "[BUG] endian-swapped type %d unsupported in fast path",
304
+ type_code);
305
+ }
306
+ }
307
+
308
+ static void
309
+ ca_struct_write_endian_swapped_fast (char *p, int type_code, VALUE val)
310
+ {
311
+ switch (type_code) {
312
+ case CA_INT16: {
313
+ uint16_t v = (uint16_t) NUM2INT(val);
314
+ v = CA_BSWAP16(v); memcpy(p, &v, 2);
315
+ return;
316
+ }
317
+ case CA_UINT16: {
318
+ uint16_t v = (uint16_t) NUM2UINT(val);
319
+ v = CA_BSWAP16(v); memcpy(p, &v, 2);
320
+ return;
321
+ }
322
+ case CA_INT32: {
323
+ uint32_t v = (uint32_t) NUM2LONG(val);
324
+ v = CA_BSWAP32(v); memcpy(p, &v, 4);
325
+ return;
326
+ }
327
+ case CA_UINT32: {
328
+ uint32_t v = (uint32_t) NUM2ULONG(val);
329
+ v = CA_BSWAP32(v); memcpy(p, &v, 4);
330
+ return;
331
+ }
332
+ case CA_INT64: {
333
+ uint64_t v = (uint64_t) NUM2LL(val);
334
+ v = CA_BSWAP64(v); memcpy(p, &v, 8);
335
+ return;
336
+ }
337
+ case CA_UINT64: {
338
+ uint64_t v = NUM2ULL(val);
339
+ v = CA_BSWAP64(v); memcpy(p, &v, 8);
340
+ return;
341
+ }
342
+ case CA_FLOAT32: {
343
+ float vf = (float) NUM2DBL(val);
344
+ uint32_t bits; memcpy(&bits, &vf, 4);
345
+ bits = CA_BSWAP32(bits); memcpy(p, &bits, 4);
346
+ return;
347
+ }
348
+ case CA_FLOAT64: {
349
+ double vd = NUM2DBL(val);
350
+ uint64_t bits; memcpy(&bits, &vd, 8);
351
+ bits = CA_BSWAP64(bits); memcpy(p, &bits, 8);
352
+ return;
353
+ }
354
+ default:
355
+ rb_raise(rb_eRuntimeError,
356
+ "[BUG] endian-swapped type %d unsupported in fast path",
357
+ type_code);
358
+ }
359
+ }
360
+
361
+ /* ---------------------------------------------------------------------
362
+ Resolve a member name to a Ruby String key matching MEMBER_TABLE /
363
+ DISPATCH_TABLE / FAST_PRIMITIVES. Integer indices route through
364
+ MEMBERS, mirroring the Ruby implementation. Returns Qnil if the
365
+ integer is out of range (handled at the call site).
366
+ --------------------------------------------------------------------- */
367
+
368
+ static VALUE
369
+ ca_struct_resolve_name (VALUE klass, VALUE name)
370
+ {
371
+ if (RB_TYPE_P(name, T_FIXNUM)) {
372
+ VALUE members = rb_const_get(klass, rb_intern("MEMBERS"));
373
+ name = rb_ary_entry(members, FIX2LONG(name));
374
+ if (NIL_P(name)) return Qnil;
375
+ }
376
+ if (RB_TYPE_P(name, T_STRING)) {
377
+ return name;
378
+ }
379
+ if (RB_TYPE_P(name, T_SYMBOL)) {
380
+ /* rb_sym2str returns the symbol's underlying (frozen, interned)
381
+ String — no allocation, unlike `name.to_s` which copies. */
382
+ return rb_sym2str(name);
383
+ }
384
+ return rb_funcall(name, rb_intern("to_s"), 0);
385
+ }
386
+
387
+ /* ---------------------------------------------------------------------
388
+ Shared write-dispatch helper reused by `[]=` and `initialize`.
389
+ Returns 1 if the member was written, 0 if neither table contained
390
+ the name (caller decides whether to send / raise).
391
+ --------------------------------------------------------------------- */
392
+
393
+ static int
394
+ ca_struct_dispatch_write (VALUE klass, VALUE name_s, VALUE val, VALUE data_val)
395
+ {
396
+ if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
397
+ VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
398
+ VALUE entry = rb_hash_aref(fast_table, name_s);
399
+ if (!NIL_P(entry)) {
400
+ int kind = NUM2INT(rb_ary_entry(entry, 0));
401
+ CArray *ca;
402
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
403
+ switch (kind) {
404
+ case FAST_KIND_PRIMITIVE: {
405
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
406
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
407
+ ca_struct_write_primitive_fast(ca->ptr + offset, type_code, val);
408
+ return 1;
409
+ }
410
+ case FAST_KIND_BITFIELD: {
411
+ ca_size_t start_byte = NUM2SIZE(rb_ary_entry(entry, 1));
412
+ int view_bytes = NUM2INT (rb_ary_entry(entry, 2));
413
+ int bit_in_word = NUM2INT (rb_ary_entry(entry, 3));
414
+ int bits = NUM2INT (rb_ary_entry(entry, 4));
415
+ ca_struct_write_bitfield_fast(ca->ptr + start_byte,
416
+ view_bytes, bit_in_word, bits, val);
417
+ return 1;
418
+ }
419
+ case FAST_KIND_ENDIAN: {
420
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
421
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
422
+ ca_struct_write_endian_swapped_fast(ca->ptr + offset, type_code, val);
423
+ return 1;
424
+ }
425
+ default:
426
+ rb_raise(rb_eRuntimeError,
427
+ "[BUG] FAST_PRIMITIVES unknown kind %d", kind);
428
+ }
429
+ }
430
+ }
431
+ VALUE dispatch_table = rb_const_get(klass, rb_intern("DISPATCH_TABLE"));
432
+ VALUE pair = rb_hash_aref(dispatch_table, name_s);
433
+ if (!NIL_P(pair)) {
434
+ VALUE writer = rb_ary_entry(pair, 1);
435
+ rb_funcall(writer, rb_intern("call"), 2, data_val, val);
436
+ return 1;
437
+ }
438
+ return 0;
439
+ }
440
+
441
+ /* ---------------------------------------------------------------------
442
+ CAStruct#[] (C-native).
443
+ --------------------------------------------------------------------- */
444
+
445
+ static VALUE
446
+ rb_ca_struct_aref (VALUE self, VALUE name)
447
+ {
448
+ VALUE klass = rb_obj_class(self);
449
+ VALUE name_s = ca_struct_resolve_name(klass, name);
450
+
451
+ /* Fast path. FAST_PRIMITIVES may be absent on subclasses built
452
+ outside Builder — fall through to the dispatch table in that
453
+ case. Entry shape is `[kind, ...args]`; args depend on kind. */
454
+ if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
455
+ VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
456
+ VALUE entry = rb_hash_aref(fast_table, name_s);
457
+ if (!NIL_P(entry)) {
458
+ int kind = NUM2INT(rb_ary_entry(entry, 0));
459
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
460
+ CArray *ca;
461
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
462
+ /* @data is a CScalar entity, so ca->ptr is always valid. */
463
+ switch (kind) {
464
+ case FAST_KIND_PRIMITIVE: {
465
+ /* [kind, offset, type_code] */
466
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
467
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
468
+ return ca_struct_read_primitive_fast(ca->ptr + offset, type_code);
469
+ }
470
+ case FAST_KIND_BITFIELD: {
471
+ /* [kind, start_byte, view_bytes, bit_in_word, bits] */
472
+ ca_size_t start_byte = NUM2SIZE(rb_ary_entry(entry, 1));
473
+ int view_bytes = NUM2INT (rb_ary_entry(entry, 2));
474
+ int bit_in_word = NUM2INT (rb_ary_entry(entry, 3));
475
+ int bits = NUM2INT (rb_ary_entry(entry, 4));
476
+ return ca_struct_read_bitfield_fast(ca->ptr + start_byte,
477
+ view_bytes, bit_in_word, bits);
478
+ }
479
+ case FAST_KIND_ENDIAN: {
480
+ /* [kind, offset, type_code] — only the swap-needed branch;
481
+ same-endian members are folded into FAST_KIND_PRIMITIVE
482
+ at build time. */
483
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
484
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
485
+ return ca_struct_read_endian_swapped_fast(ca->ptr + offset,
486
+ type_code);
487
+ }
488
+ default:
489
+ rb_raise(rb_eRuntimeError,
490
+ "[BUG] FAST_PRIMITIVES unknown kind %d", kind);
491
+ }
492
+ }
493
+ }
494
+
495
+ /* Dispatch-table path (nested struct / CArray template / fixlen /
496
+ anything Builder didn't fold into FAST_PRIMITIVES) */
497
+ VALUE dispatch_table = rb_const_get(klass, rb_intern("DISPATCH_TABLE"));
498
+ VALUE pair = rb_hash_aref(dispatch_table, name_s);
499
+ if (!NIL_P(pair)) {
500
+ VALUE reader = rb_ary_entry(pair, 0);
501
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
502
+ return rb_funcall(reader, rb_intern("call"), 1, data_val);
503
+ }
504
+
505
+ /* Unknown name: fall back to send(name) for computed members. */
506
+ return rb_funcall(self, rb_to_id(name), 0);
507
+ }
508
+
509
+ /* ---------------------------------------------------------------------
510
+ CAStruct#[]= (C-native).
511
+ --------------------------------------------------------------------- */
512
+
513
+ static VALUE
514
+ rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
515
+ {
516
+ VALUE klass = rb_obj_class(self);
517
+ VALUE name_s = ca_struct_resolve_name(klass, name);
518
+
519
+ /* Fast path (see rb_ca_struct_aref for the absent-FAST_PRIMITIVES
520
+ rationale and entry-shape contract). */
521
+ if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
522
+ VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
523
+ VALUE entry = rb_hash_aref(fast_table, name_s);
524
+ if (!NIL_P(entry)) {
525
+ int kind = NUM2INT(rb_ary_entry(entry, 0));
526
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
527
+ CArray *ca;
528
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
529
+ switch (kind) {
530
+ case FAST_KIND_PRIMITIVE: {
531
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
532
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
533
+ ca_struct_write_primitive_fast(ca->ptr + offset, type_code, val);
534
+ return val;
535
+ }
536
+ case FAST_KIND_BITFIELD: {
537
+ ca_size_t start_byte = NUM2SIZE(rb_ary_entry(entry, 1));
538
+ int view_bytes = NUM2INT (rb_ary_entry(entry, 2));
539
+ int bit_in_word = NUM2INT (rb_ary_entry(entry, 3));
540
+ int bits = NUM2INT (rb_ary_entry(entry, 4));
541
+ ca_struct_write_bitfield_fast(ca->ptr + start_byte,
542
+ view_bytes, bit_in_word, bits, val);
543
+ return val;
544
+ }
545
+ case FAST_KIND_ENDIAN: {
546
+ ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
547
+ int type_code = NUM2INT (rb_ary_entry(entry, 2));
548
+ ca_struct_write_endian_swapped_fast(ca->ptr + offset, type_code, val);
549
+ return val;
550
+ }
551
+ default:
552
+ rb_raise(rb_eRuntimeError,
553
+ "[BUG] FAST_PRIMITIVES unknown kind %d", kind);
554
+ }
555
+ }
556
+ }
557
+
558
+ /* Dispatch-table path */
559
+ VALUE dispatch_table = rb_const_get(klass, rb_intern("DISPATCH_TABLE"));
560
+ VALUE pair = rb_hash_aref(dispatch_table, name_s);
561
+ if (!NIL_P(pair)) {
562
+ VALUE writer = rb_ary_entry(pair, 1);
563
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
564
+ rb_funcall(writer, rb_intern("call"), 2, data_val, val);
565
+ return val;
566
+ }
567
+
568
+ /* Unknown name: fall back to send("name=", val). */
569
+ VALUE setter_name = rb_str_plus(rb_obj_as_string(name), rb_str_new_cstr("="));
570
+ return rb_funcall(self, rb_intern_str(setter_name), 1, val);
571
+ }
572
+
573
+ /* ---------------------------------------------------------------------
574
+ C-native CAStruct#initialize.
575
+
576
+ Always creates @data = CScalar.new(klass) as an entity. Hash init
577
+ walks the Hash once, dispatching each key through
578
+ ca_struct_dispatch_write and accumulating unknown keys into a
579
+ single ArgumentError. Positional init dispatches argv[i] under
580
+ MEMBERS[i].
581
+ --------------------------------------------------------------------- */
582
+
583
+ typedef struct {
584
+ VALUE klass;
585
+ VALUE data_val;
586
+ VALUE unknown_keys; /* lazily-allocated Array; Qnil until needed */
587
+ } ca_struct_hash_init_state_t;
588
+
589
+ /* rb_hash_foreach callback: dispatch one key/val pair via the
590
+ shared write helper, accumulating unknown keys for a single
591
+ ArgumentError after the iteration. */
592
+ static int
593
+ ca_struct_hash_init_iter (VALUE key, VALUE val, VALUE state_va)
594
+ {
595
+ ca_struct_hash_init_state_t *st =
596
+ (ca_struct_hash_init_state_t *) state_va;
597
+ VALUE name_s = ca_struct_resolve_name(st->klass, key);
598
+ if (!ca_struct_dispatch_write(st->klass, name_s, val, st->data_val)) {
599
+ if (NIL_P(st->unknown_keys)) st->unknown_keys = rb_ary_new();
600
+ rb_ary_push(st->unknown_keys, key);
601
+ }
602
+ return ST_CONTINUE;
603
+ }
604
+
605
+ static VALUE
606
+ rb_ca_struct_initialize (int argc, VALUE *argv, VALUE self)
607
+ {
608
+ VALUE klass = rb_obj_class(self);
609
+
610
+ /* @data is a CScalar entity sized to klass::DATA_SIZE. The
611
+ FAST_PRIMITIVES / dispatcher paths access ca->ptr directly,
612
+ so it must be an entity (a view would not carry a stable ptr
613
+ across the fast path). */
614
+ VALUE data_val = rb_funcall(rb_cCScalar, rb_intern("new"), 1, klass);
615
+ rb_ivar_set(self, rb_intern("@data"), data_val);
616
+
617
+ if (argc == 0) return Qnil;
618
+
619
+ /* Hash init */
620
+ if (argc == 1 && TYPE(argv[0]) == T_HASH) {
621
+ VALUE hash = argv[0];
622
+ ca_struct_hash_init_state_t state = {
623
+ .klass = klass,
624
+ .data_val = data_val,
625
+ .unknown_keys = Qnil,
626
+ };
627
+ /* CAREFUL: pass ca_struct_hash_init_iter without a cast. Its
628
+ signature already matches rb_hash_foreach's callback shape,
629
+ and the old (int (*)(ANYARGS)) cast collapses to
630
+ (int (*)(void)) under C23 and is rejected by gcc-15 as an
631
+ incompatible pointer type. */
632
+ rb_hash_foreach(hash,
633
+ ca_struct_hash_init_iter,
634
+ (VALUE) &state);
635
+ if (!NIL_P(state.unknown_keys)) {
636
+ VALUE members = rb_const_get(klass, rb_intern("MEMBERS"));
637
+ VALUE unknown_inspect = rb_inspect(state.unknown_keys);
638
+ VALUE known_inspect = rb_inspect(members);
639
+ rb_raise(rb_eArgError,
640
+ "unknown member(s) for %s: %s (known: %s)",
641
+ rb_class2name(klass),
642
+ StringValueCStr(unknown_inspect),
643
+ StringValueCStr(known_inspect));
644
+ }
645
+ return Qnil;
646
+ }
647
+
648
+ /* Positional init */
649
+ VALUE members = rb_const_get(klass, rb_intern("MEMBERS"));
650
+ long mems_size = RARRAY_LEN(members);
651
+ if (argc > mems_size) {
652
+ rb_raise(rb_eArgError,
653
+ "too many arguments for %s.new (<%d> for <%ld>)",
654
+ rb_class2name(klass), argc, mems_size);
655
+ }
656
+ for (int i = 0; i < argc; i++) {
657
+ VALUE name_s = rb_ary_entry(members, i);
658
+ if (!ca_struct_dispatch_write(klass, name_s, argv[i], data_val)) {
659
+ /* Unreachable for Builder-constructed subclasses: MEMBERS
660
+ entries are seeded into both FAST_PRIMITIVES and
661
+ DISPATCH_TABLE. Fall back to send for hand-built
662
+ subclasses that only define writers. */
663
+ VALUE setter_name = rb_str_plus(rb_obj_as_string(name_s),
664
+ rb_str_new_cstr("="));
665
+ rb_funcall(self, rb_intern_str(setter_name), 1, argv[i]);
666
+ }
667
+ }
668
+ return Qnil;
669
+ }
670
+
671
+ /* ---------------------------------------------------------------------
672
+ Install the C `[]` / `[]=` / `initialize` on the given class.
673
+ Called from lib/carray/struct.rb once CAStruct is defined.
674
+ --------------------------------------------------------------------- */
675
+
676
+ static VALUE
677
+ rb_carray_install_castruct_methods (VALUE self, VALUE klass)
678
+ {
679
+ if (!RB_TYPE_P(klass, T_CLASS)) {
680
+ rb_raise(rb_eTypeError, "expected a Class");
681
+ }
682
+ rb_define_method(klass, "[]", rb_ca_struct_aref, 1);
683
+ rb_define_method(klass, "[]=", rb_ca_struct_aset, 2);
684
+ rb_define_method(klass, "initialize", rb_ca_struct_initialize, -1);
685
+ return klass;
686
+ }
687
+
688
+ void
689
+ Init_carray_struct (void)
690
+ {
691
+ /* Singleton on rb_cCArray so lib/carray/struct.rb can call it as
692
+ `CArray.__install_castruct_methods__(klass)`. */
693
+ rb_define_singleton_method(rb_cCArray, "__install_castruct_methods__",
694
+ rb_carray_install_castruct_methods, 1);
695
+ }