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
data/lib/carray/struct.rb CHANGED
@@ -1,13 +1,3 @@
1
- # ----------------------------------------------------------------------------
2
- #
3
- # carray/struct.rb
4
- #
5
- # This file is part of Ruby/CArray extension library.
6
- #
7
- # Copyright (C) 2005-2020 Hiroki Motoyoshi
8
- #
9
- # ----------------------------------------------------------------------------
10
- #
11
1
  # The data class for fixed length carray are required to satisfy only
12
2
  # five conditions.
13
3
  #
@@ -27,7 +17,7 @@
27
17
  # :size => 1024 # user defined size (with padding)
28
18
  # }
29
19
  #
30
- # CA.struct(option) { |s|
20
+ # CArray.struct(option) { |s|
31
21
  #
32
22
  # # numeric types
33
23
  #
@@ -49,11 +39,11 @@
49
39
  #
50
40
  # # struct type
51
41
  # struct(:st1, :st2) { uint8 :a, :b, :c }
52
- # struct :st3, :st4, :type => CA.struct { uint8 :a, :b, :c }
42
+ # struct :st3, :st4, :type => CArray.struct { uint8 :a, :b, :c }
53
43
  #
54
44
  # # union type
55
45
  # union(:un1, :un2) { uint8 :a; int16 :b; float32 :c }
56
- # union :un3, :un4, :type => CA.union { uint8 :a, :b, :c }
46
+ # union :un3, :un4, :type => CArray.union { uint8 :a, :b, :c }
57
47
  #
58
48
  # # anonymous
59
49
  #
@@ -70,13 +60,112 @@
70
60
  # member union{ int8 :a; int16 :b; float :c }, :st5, :st6
71
61
  #
72
62
  # }
73
- #
63
+
64
+ class CAStruct
65
+ # CAStruct::Builder is the DSL backend behind CArray.struct / CArray.union.
66
+ # It is only touched at struct-definition time (= the body of a
67
+ # `CArray.struct { ... }` block); programs that only consume pre-defined
68
+ # struct classes never reference it. Lazy-load on first lookup.
69
+ autoload :Builder, "carray/struct_builder"
70
+
71
+ # Generic base for all CAStruct-related errors. Callers can rescue
72
+ # this class to catch any struct-side failure regardless of which
73
+ # specific sub-error fired.
74
+ class Error < StandardError; end
75
+
76
+ # Raised by CAStruct::Builder when the struct/union definition is
77
+ # malformed (duplicate member name, empty typed-method call, etc.).
78
+ class DefinitionError < Error; end
79
+
80
+ # Raised by CAStruct#decode / #encode when the input cannot be
81
+ # interpreted as the struct's binary representation.
82
+ class DecodeError < Error; end
83
+
84
+ # Reflection record for a single struct member. Frozen by default
85
+ # so callers can treat it as a value object. Lifted from
86
+ # `MEMBER_TABLE`'s `[offset, type, opts]` tuple to give first-class
87
+ # access to member metadata without exposing the opaque hash shape.
88
+ #
89
+ # Two flavours:
90
+ #
91
+ # - Regular byte-typed member: `name` / `offset` / `type` / `bytes`
92
+ # describe the placement. For typed primitives, `type` is the
93
+ # Symbol (`:int32` etc.); for nested CAStruct subclass members,
94
+ # `type` is the class; for CArray-template members, `type` is the
95
+ # template CArray.
96
+ # - Bit member: `bitfield?` returns true; `bits` and
97
+ # `bit_offset` hold the bit-level placement. `offset` is the
98
+ # byte containing the LSB of the field (for raw-record indexing),
99
+ # `bytes` is nil.
100
+ class Field
101
+ attr_reader :name, :offset, :type, :bytes, :bits, :bit_offset, :endian
102
+
103
+ def initialize (name:, offset:, type:, bytes: nil,
104
+ bits: nil, bit_offset: nil, endian: nil)
105
+ @name = name
106
+ @offset = offset
107
+ @type = type
108
+ @bytes = bytes
109
+ @bits = bits
110
+ @bit_offset = bit_offset
111
+ @endian = endian
112
+ freeze
113
+ end
114
+
115
+ def bitfield?
116
+ @type == :bitfield
117
+ end
118
+
119
+ # @return [String]
120
+ def inspect
121
+ if bitfield?
122
+ format("#<%s %p bitfield bits=%d bit_offset=%d>",
123
+ self.class, @name, @bits, @bit_offset)
124
+ else
125
+ endian_part = @endian ? format(" endian=%p", @endian) : ""
126
+ format("#<%s %p type=%p offset=%d bytes=%s%s>",
127
+ self.class, @name, @type, @offset, @bytes.inspect,
128
+ endian_part)
129
+ end
130
+ end
131
+
132
+ alias to_s inspect
133
+
134
+ # Field-wise comparison of the member's declaration (name, offset, type,
135
+ # width, bit placement, endianness). Aliased as `eql?`.
136
+ # @return [Boolean]
137
+ def == (other)
138
+ other.is_a?(Field) &&
139
+ @name == other.name &&
140
+ @offset == other.offset &&
141
+ @type == other.type &&
142
+ @bytes == other.bytes &&
143
+ @bits == other.bits &&
144
+ @bit_offset == other.bit_offset &&
145
+ @endian == other.endian
146
+ end
147
+
148
+ alias eql? ==
149
+
150
+ # @return [Integer] a hash consistent with #==.
151
+ def hash
152
+ [@name, @offset, @type, @bytes, @bits, @bit_offset, @endian].hash
153
+ end
154
+ end
155
+
156
+ end
74
157
 
75
158
  class CArray
76
159
 
160
+ # @overload st
161
+ # Returns a Ruby `Struct` view exposing every {CAStruct} member
162
+ # of `self` as a `Struct` attribute holding the corresponding
163
+ # member column. Cached per receiver.
164
+ # @return [Struct]
165
+ # @raise [CAStruct::Error] when `self` has no `data_class`.
77
166
  def st
78
167
  unless has_data_class?
79
- raise "should have data_class"
168
+ raise CAStruct::Error, "carray does not have a data_class"
80
169
  end
81
170
  unless @struct
82
171
  struct_class = Struct.new(nil, *data_class::MEMBERS)
@@ -94,37 +183,128 @@ class CAStruct
94
183
 
95
184
  class << self
96
185
 
186
+ # @return [String] the struct's name, or `"AnonStruct"` when it was
187
+ # defined anonymously.
97
188
  def inspect
98
189
  return name.nil? ? "AnonStruct" : name
99
190
  end
100
191
 
192
+ # @overload [](*values)
193
+ # Returns a new struct record whose members are set from
194
+ # `values` in declaration order. Missing values leave the
195
+ # corresponding member at its default; extra values raise.
196
+ # @param values [Array<Object>]
197
+ # @return [CAStruct]
198
+ # @raise [ArgumentError] when too many values are given.
101
199
  def [] (*argv)
200
+ if argv.size > self::MEMBERS.size
201
+ raise ArgumentError,
202
+ format("too many arguments for %s.[] (<%i> for <%i>)",
203
+ inspect, argv.size, self::MEMBERS.size)
204
+ end
102
205
  obj = new()
103
206
  members.each do |name|
207
+ break if argv.empty?
104
208
  obj[name] = argv.shift
105
209
  end
106
210
  return obj
107
211
  end
108
212
 
213
+ # @overload members
214
+ # Returns the member name list in declaration order.
215
+ # @return [Array<String>]
109
216
  def members
110
- return self::MEMBERS.clone
111
- end
112
-
217
+ return self::MEMBERS
218
+ end
219
+
220
+ # @overload fields
221
+ # Returns the struct's members as {CAStruct::Field} objects
222
+ # in declaration order. Cached on the class.
223
+ # @return [Array<CAStruct::Field>]
224
+ def fields
225
+ @__fields__ ||= self::MEMBERS.map { |name|
226
+ offset, type, opts = *self::MEMBER_TABLE[name]
227
+ opts ||= {}
228
+ if type == :bitfield
229
+ CAStruct::Field.new(name: name,
230
+ offset: offset,
231
+ type: :bitfield,
232
+ bits: opts[:bits],
233
+ bit_offset: opts[:bit_offset])
234
+ else
235
+ CAStruct::Field.new(name: name,
236
+ offset: offset,
237
+ type: type,
238
+ bytes: opts[:bytes],
239
+ endian: opts[:endian])
240
+ end
241
+ }.freeze
242
+ end
243
+
244
+ # @overload field_info(name)
245
+ # Returns the {CAStruct::Field} for `name`, or `nil` when the
246
+ # member is unknown.
247
+ # @param name [Symbol, String]
248
+ # @return [CAStruct::Field, nil]
249
+ def field_info (name)
250
+ key = name.to_s
251
+ fields.find { |f| f.name == key }
252
+ end
253
+
254
+ # @overload offset_of(name)
255
+ # Returns the offset of member `name`: bytes for regular
256
+ # members, bits for bit members. `nil` when the member is
257
+ # unknown.
258
+ # @param name [Symbol, String]
259
+ # @return [Integer, nil]
260
+ def offset_of (name)
261
+ f = field_info(name)
262
+ return nil unless f
263
+ f.bitfield? ? f.bit_offset : f.offset
264
+ end
265
+
266
+ # @overload decode(data)
267
+ # Returns a new struct record built from the binary `data`
268
+ # (String or CArray).
269
+ # @param data [String, CArray]
270
+ # @return [CAStruct]
113
271
  def decode (data) ### required element as data class
114
272
  return new.decode(data)
115
273
  end
116
-
274
+
275
+ # @overload size
276
+ # Returns the byte size of the struct's layout.
277
+ # @return [Integer]
117
278
  def size
118
279
  return self::DATA_SIZE
119
280
  end
120
281
 
121
282
  end
122
283
 
284
+ # @overload initialize(*values)
285
+ # Allocates a new struct record. Members are set from the
286
+ # trailing positional `values` in declaration order, or from a
287
+ # single Hash argument keyed by member name.
288
+ # @param values [Array<Object>, Array<Hash>]
289
+ # @raise [ArgumentError] on unknown Hash keys or an excess of
290
+ # positional values.
123
291
  def initialize (*argv)
124
292
  @data = CScalar.new(self.class)
125
293
  mems = members
126
294
  if argv.size == 1 and argv.first.is_a?(Hash)
127
- argv.first.each do |k,v|
295
+ # Validate keys up-front so the caller gets one clear error
296
+ # listing all unknown keys, rather than a NoMethodError from the
297
+ # first one and silent partial-init for the rest.
298
+ known = self.class::MEMBER_TABLE
299
+ unknown = argv.first.keys.reject { |k| known.key?(k.to_s) }
300
+ unless unknown.empty?
301
+ raise ArgumentError,
302
+ format("unknown member(s) for %s: %s (known: %s)",
303
+ self.class.inspect,
304
+ unknown.map(&:inspect).join(", "),
305
+ mems.map(&:inspect).join(", "))
306
+ end
307
+ argv.first.each do |k, v|
128
308
  self[k] = v
129
309
  end
130
310
  elsif argv.size <= mems.size
@@ -132,9 +312,9 @@ class CAStruct
132
312
  self[mems[i]] = v
133
313
  end
134
314
  else
135
- raise ArgumentError,
136
- format("too many arguments for %s.new (<%i> for <%i>)",
137
- self.class.inspect, argv.size, members.size)
315
+ raise ArgumentError,
316
+ format("too many arguments for %s.new (<%i> for <%i>)",
317
+ self.class.inspect, argv.size, mems.size)
138
318
  end
139
319
  end
140
320
 
@@ -146,72 +326,104 @@ class CAStruct
146
326
 
147
327
  public
148
328
 
329
+ # Member access (`record[:name]` / `record["name"]` / `record[i]`).
330
+ # Step 3 (3.0): the per-type `case` was replaced by a class-level
331
+ # DISPATCH_TABLE built once at struct-definition time. Each known
332
+ # member has a frozen `[reader_proc, writer_proc]` pair that closes
333
+ # over its offset / type / opts, so the per-call work is one Hash
334
+ # lookup + one Proc#call. Unknown names fall back to `send(name)`
335
+ # so subclasses can define computed members via plain Ruby methods.
336
+ # @overload [](name)
337
+ # Returns the value of member `name` (Integer indexes the
338
+ # member list). Unknown names fall through to `send(name)`.
339
+ # @param name [Symbol, String, Integer]
340
+ # @return [Object]
149
341
  def [] (name)
150
342
  if name.kind_of?(Integer)
151
343
  name = members[name]
152
344
  end
153
- offset, type, opts = *self.class::MEMBER_TABLE[name.to_s]
154
- case type
155
- when nil
156
- return send(name)
157
- when Class
158
- return type.decode(@data.field(offset, type))
159
- when CArray
160
- return @data.field(offset,type)[0,false]
345
+ pair = self.class::DISPATCH_TABLE[name.to_s]
346
+ if pair
347
+ pair[0].call(@data)
161
348
  else
162
- return @data.field(offset,type,opts)[0]
349
+ send(name)
163
350
  end
164
351
  end
165
352
 
353
+ # @overload []=(name, val)
354
+ # Sets member `name` to `val`. Unknown names fall through to
355
+ # `send("#{name}=", val)`.
356
+ # @param name [Symbol, String, Integer]
357
+ # @param val [Object]
358
+ # @return [Object] `val`.
166
359
  def []= (name, val)
167
360
  if name.kind_of?(Integer)
168
361
  name = members[name]
169
362
  end
170
- offset, type, opts = *self.class::MEMBER_TABLE[name.to_s]
171
- case type
172
- when nil
173
- send(name.to_s + "=", val)
174
- when Class
175
- @data.field(offset, type)[0] = val
176
- when CArray
177
- @data.field(offset, type)[0,false] = val
363
+ pair = self.class::DISPATCH_TABLE[name.to_s]
364
+ if pair
365
+ pair[1].call(@data, val)
178
366
  else
179
- @data.field(offset,type,opts)[0] = val
367
+ send(name.to_s + "=", val)
180
368
  end
181
369
  end
182
370
 
371
+ # @overload each { |value| ... }
372
+ # Yields each member value in declaration order.
373
+ # @yieldparam value [Object]
374
+ # @return [self]
183
375
  def each
184
376
  members.each do |name|
185
377
  yield(self[name])
186
378
  end
187
379
  end
188
380
 
381
+ # @overload each_pair { |name, value| ... }
382
+ # Yields each member as a `(name, value)` pair in declaration
383
+ # order.
384
+ # @yieldparam name [Symbol]
385
+ # @yieldparam value [Object]
386
+ # @return [self]
189
387
  def each_pair
190
388
  members.each do |name|
191
389
  yield(name.intern, self[name])
192
390
  end
193
391
  end
194
392
 
393
+ # @overload length
394
+ # Returns the number of members.
395
+ # @return [Integer]
195
396
  def length
196
397
  return self.class::MEMBERS.length
197
398
  end
198
-
399
+
199
400
  alias size length
200
401
 
402
+ # @overload members
403
+ # Returns the member name list in declaration order.
404
+ # @return [Array<String>]
201
405
  def members
202
- return self.class::MEMBERS.clone
406
+ return self.class::MEMBERS
203
407
  end
204
408
 
409
+ # @overload values
410
+ # Returns the member values in declaration order.
411
+ # @return [Array<Object>]
205
412
  def values
206
413
  return members.map{|name| self[name] }
207
414
  end
208
415
 
209
416
  alias to_a values
210
417
 
418
+ # @overload values_at(*names)
419
+ # Returns the values of the named members in the given order.
420
+ # @param names [Array<Symbol, String, Integer>]
421
+ # @return [Array<Object>]
211
422
  def values_at (*names)
212
423
  return names.map{|name| self[name] }
213
424
  end
214
425
 
426
+ # @return [String] the class name followed by every member and its value.
215
427
  def inspect
216
428
  table = {}
217
429
  members.each do |key|
@@ -220,6 +432,9 @@ class CAStruct
220
432
  return ["<", self.class.inspect, " ", table.inspect[1..-2], ">"].join
221
433
  end
222
434
 
435
+ # Value comparison against another record of the same class. Unlike
436
+ # `eql?`, which compares the raw bytes, this compares the decoded members.
437
+ # @return [Boolean]
223
438
  def == (other)
224
439
  case other
225
440
  when self.class
@@ -229,333 +444,115 @@ class CAStruct
229
444
  end
230
445
  end
231
446
 
447
+ # Byte-level identity. Two CAStruct instances are eql? iff they are
448
+ # of exactly the same class and their binary representations match.
449
+ # Lets struct records work as Hash keys / Set members.
450
+ def eql? (other)
451
+ other.is_a?(self.class) && encode == other.encode
452
+ end
453
+
454
+ # @return [Integer] a hash consistent with #eql? (byte-level identity), so
455
+ # records work as Hash keys and Set members.
456
+ def hash
457
+ encode.hash
458
+ end
459
+
460
+ # @overload decode(data)
461
+ # Loads `data`'s binary representation into `self`. A String
462
+ # is loaded directly; a CArray is copied through
463
+ # `dump_binary` (no aliasing).
464
+ # @param data [String, CArray]
465
+ # @return [self]
466
+ # @raise [CAStruct::DecodeError] on an unsupported input.
232
467
  def decode (data)
233
468
  case data
234
469
  when String
235
470
  @data.load_binary(data)
236
471
  when CArray
237
- @data = data
472
+ @data.load_binary(data.dump_binary)
238
473
  else
239
- raise "unkown data to decode"
474
+ raise CAStruct::DecodeError,
475
+ format("unknown data to decode: %s", data.class)
240
476
  end
241
477
  return self
242
478
  end
243
479
 
480
+ # @overload encode
481
+ # Returns the binary representation of `self` matching this
482
+ # struct's layout.
483
+ # @return [String]
244
484
  def encode ### required element as data class
245
485
  return @data.dump_binary
246
486
  end
247
487
 
248
- alias to_s encode
249
-
488
+ # @overload swap_bytes!
489
+ # Byte-swaps `self` in place (endian flip).
490
+ # @return [self]
250
491
  def swap_bytes!
251
- @data.swap_bytes!
492
+ @data[] = @data.swap_bytes
252
493
  return self
253
494
  end
254
495
 
496
+ # @overload swap_bytes
497
+ # Returns a fresh byte-swapped copy of `self`.
498
+ # @return [CAStruct]
255
499
  def swap_bytes
256
500
  return self.class.decode(@data.swap_bytes.dump_binary)
257
501
  end
258
502
 
503
+ # @overload to_ptr
504
+ # Returns a `Fiddle::Pointer` to the backing storage.
505
+ # @return [Fiddle::Pointer]
259
506
  def to_ptr
260
507
  return @data.to_ptr
261
508
  end
262
509
 
263
510
  end
264
511
 
512
+ # Struct whose members all start at offset 0, so they are alternative
513
+ # readings of the same bytes. Otherwise behaves as a {CAStruct}.
265
514
  class CAUnion < CAStruct
266
515
  class << self
516
+ # @return [String] the union's name, or `"AnonUnion"` when it was
517
+ # defined anonymously.
267
518
  def inspect
268
519
  return name.nil? ? "AnonUnion" : name
269
520
  end
270
521
  end
271
522
  end
272
523
 
273
- module CA
524
+ class CArray
274
525
 
526
+ # @overload struct(opt = {}, &block)
527
+ # Returns a new {CAStruct} subclass defined by the block via
528
+ # {CAStruct::Builder}. Options control alignment, packing, and
529
+ # endianness.
530
+ # @param opt [Hash]
531
+ # @yield DSL calls in {CAStruct::Builder}.
532
+ # @return [Class] anonymous {CAStruct} subclass.
275
533
  def self.struct (opt={}, &block)
276
534
  return CAStruct::Builder.new(:struct, opt).define(&block)
277
535
  end
278
536
 
537
+ # @overload union(opt = {}, &block)
538
+ # Returns a new {CAUnion} subclass defined by the block. Same
539
+ # options and DSL as {.struct} but every member occupies the
540
+ # same offset.
541
+ # @param opt [Hash]
542
+ # @yield DSL calls in {CAStruct::Builder}.
543
+ # @return [Class] anonymous {CAUnion} subclass.
279
544
  def self.union (opt={}, &block)
280
545
  return CAStruct::Builder.new(:union, opt).define(&block)
281
546
  end
282
547
 
283
548
  end
284
549
 
285
- # ---------------------------------------------------------------------
286
- #
287
- # Struct Builder Class
288
- #
289
- # ---------------------------------------------------------------------
290
-
291
- class CAStruct::Builder # :nodoc:
292
-
293
- class Member # :nodoc:
294
-
295
- def initialize (name, type, opt={})
296
- @name, @type, @opt = name, type, opt
297
- if @type.kind_of?(CArray)
298
- @type = @type.to_ca
299
- @byte_length = @type.bytes * @type.elements
300
- @bytes = 0
301
- else
302
- data_type, @bytes = CArray.guess_type_and_bytes(@type, @opt[:bytes])
303
- if data_type == CA_OBJECT
304
- raise RuntimeError, "CA_OBJECT type can't be a member of struct or union"
305
- end
306
- @byte_length = @bytes
307
- end
308
- @offset = @opt[:offset]
309
- end
310
-
311
- attr_reader :name, :type, :offset, :bytes, :byte_length
312
-
313
- end
314
-
315
- ALIGN_TABLE = {
316
- CA_FIXLEN => CA_ALIGN_FIXLEN,
317
- CA_BOOLEAN => CA_ALIGN_INT8,
318
- CA_INT8 => CA_ALIGN_INT8,
319
- CA_UINT8 => CA_ALIGN_INT8,
320
- CA_INT16 => CA_ALIGN_INT16,
321
- CA_UINT16 => CA_ALIGN_INT16,
322
- CA_INT32 => CA_ALIGN_INT32,
323
- CA_UINT32 => CA_ALIGN_INT32,
324
- CA_INT64 => CA_ALIGN_INT64,
325
- CA_UINT64 => CA_ALIGN_INT64,
326
- CA_FLOAT32 => CA_ALIGN_FLOAT32,
327
- CA_FLOAT64 => CA_ALIGN_FLOAT64,
328
- CA_CMPLX64 => CA_ALIGN_FLOAT32,
329
- CA_CMPLX128 => CA_ALIGN_FLOAT64,
330
- }
331
-
332
- CARRAY_METHODS = CArray.instance_methods.map{|m| m.intern }
333
-
334
- def initialize (type, opt = {})
335
- if not opt[:pack].nil? and not opt[:pack].is_a?(Integer)
336
- raise "invalid alignment given '#{align}'"
337
- end
338
- @type = type ### :struct or :union
339
- @align = opt[:pack] ### nil for alignment, int for pack(n)
340
- @members = [] ### array of CArray::Struct::Builder::Member
341
- @offset = 0 ### offset of each member and size of struct
342
- @align_max = 1 ### maximum of alignment among members
343
- @size = opt[:size] ### user defined struct size
344
- end
345
-
346
- def define (&block)
347
- # ---
348
- case block.arity
349
- when 1
350
- block.call(self) ### struct/union definition block
351
- when -1, 0
352
- instance_exec(&block) ### struct/union definition block
353
- else
354
- raise "invalid # of block parameters"
355
- end
356
- # ---
357
- case @type
358
- when :struct
359
- klass = Class.new(CAStruct)
360
- when :union
361
- klass = Class.new(CAUnion)
362
- end
363
- # ---
364
- if @align.nil?
365
- @offset = alignment(@offset, :align_max)
366
- end
367
- if @size
368
- if @size < @offset
369
- raise RuntimeError, "struct size exceeds the fixlen size"
370
- end
371
- @offset = @size
372
- end
373
- klass.const_set(:DATA_SIZE, @offset) ### required element as data class
374
- # ---
375
- table = Hash.new
376
- names = []
377
- @members.each do |mem|
378
- name = mem.name
379
- type = mem.type
380
- offset = mem.offset
381
- bytes = mem.bytes
382
- if bytes
383
- table[name] = [offset, type, {:bytes=>bytes}]
384
- else
385
- table[name] = [offset, type]
386
- end
387
- names.push(name)
388
- end
389
- klass.const_set(:MEMBER_TABLE, table) ### required element as data class
390
- klass.const_set(:MEMBERS, names)
391
- # ---
392
- klass.module_eval {
393
- names.each do |name|
394
- define_method(name) {
395
- return self[name]
396
- }
397
- define_method("#{name}=") { |val|
398
- return self[name] = val
399
- }
400
- end
401
- }
402
- # ---
403
- return klass
404
- end
405
-
406
- private
407
-
408
- def alignment (addr, data_type, opt={})
409
- case data_type
410
- when Integer
411
- align = ALIGN_TABLE[data_type]
412
- when CArray, Class
413
- align = CA_ALIGN_VOIDP
414
- when :align_max
415
- align = @align_max
416
- else
417
- data_type, bytes = CArray.guess_type_and_bytes(data_type, opt[:bytes])
418
- return alignment(addr, data_type, :bytes=>bytes)
419
- end
420
- if align > @align_max
421
- @align_max = align
422
- end
423
- if ( d = addr % align ) != 0
424
- addr += (align - d)
425
- end
426
- return addr
427
- end
428
-
429
- def pack (addr, align, opt={})
430
- if ( addr % align ) != 0
431
- raise "invalid offset for packing"
432
- end
433
- return addr
434
- end
435
-
436
- public
437
-
438
- def member (data_type, id = nil, opt = {})
439
- opt = opt.clone
440
- if id
441
- id = id.to_s
442
- else
443
- id = "#{@members.size}"
444
- end
445
- case @type
446
- when :struct ### struct
447
- case @align
448
- when nil ### -- aligned
449
- @offset = alignment(@offset, data_type, opt)
450
- opt[:offset] = @offset
451
- else ### -- packed
452
- if opt[:offset] ### ---- explicit offset
453
- @offset = pack(opt[:offset], @align, opt)
454
- else
455
- opt[:offset] = @offset ### ---- auto offset
456
- end
457
- end
458
- mem = Member.new(id, data_type, opt)
459
- @members.push(mem)
460
- @offset += mem.byte_length
461
- when :union ### union
462
- alignment(0, data_type, opt)
463
- opt[:offset] = 0
464
- mem = Member.new(id, data_type, opt)
465
- @members.push(mem)
466
- if mem.byte_length > @offset
467
- @offset = mem.byte_length
468
- end
469
- end
470
- end
471
-
472
- # method for nested struct
473
- def struct (*args, &block)
474
- opt = args.last.is_a?(Hash) ? args.pop : {}
475
- if block
476
- opt = {:pack => @align}.update(opt)
477
- st = self.class.new(:struct, opt).define(&block)
478
- elsif opt[:type] and opt[:type] <= CAStruct
479
- st = opt[:type]
480
- else
481
- raise "type is not given for struct member of struct"
482
- end
483
- args.each do |arg|
484
- member(st, arg)
485
- end
486
- return st
487
- end
488
-
489
- # method for nested union
490
- def union (*args, &block)
491
- opt = args.last.is_a?(Hash) ? args.pop : {}
492
- if block
493
- opt = {:pack => @align}.update(opt)
494
- st = self.class.new(:union, opt).define(&block)
495
- elsif opt[:type] and opt[:type] <= CAStruct
496
- st = opt[:type]
497
- else
498
- raise "type is not given for union member of struct"
499
- end
500
- args.each do |arg|
501
- member(st, arg)
502
- end
503
- return st
504
- end
505
-
506
- def array (*args)
507
- opt = args.last.is_a?(Hash) ? args.pop : {}
508
- if not opt[:type] or not opt[:type].kind_of?(CArray)
509
- raise "type is not given for array member of struct"
510
- end
511
- args.each do |arg|
512
- member(opt[:type], arg)
513
- end
514
- end
515
-
516
- [
517
- "fixlen",
518
- "boolean",
519
- "int8",
520
- "uint8",
521
- "int16",
522
- "uint16",
523
- "int32",
524
- "uint32",
525
- "int64",
526
- "uint64",
527
- "float32",
528
- "float64",
529
- "float128",
530
- "cmplx64",
531
- "cmplx128",
532
- "cmplx256",
533
- ].each do |typename|
534
- class_eval %{
535
- def #{typename} (*args)
536
- opt = args.last.is_a?(Hash) ? args.pop : {}
537
- args.each do |arg|
538
- member(:#{typename}, arg, opt)
539
- end
540
- end
541
- }
542
- end
543
-
544
- alias char_p fixlen
545
- alias byte uint8
546
- alias short int16
547
- alias int int32
548
- alias float float32
549
- alias double float64
550
- alias complex cmplx64
551
- alias dcomplex cmplx128
552
-
553
- end
554
-
555
-
556
550
 
557
551
  class CAStruct
558
552
 
553
+ # Renders the struct's layout as the definition source that would rebuild
554
+ # it — member names, types, offsets and any nested struct.
555
+ # @return [String]
559
556
  def self.spec
560
557
  output = ""
561
558
  table = self::MEMBER_TABLE
@@ -572,9 +569,9 @@ class CAStruct
572
569
  output << sprintf("%s = ", self.name)
573
570
  end
574
571
  if self < CAUnion
575
- output << sprintf("CA.union(:size=>%i) {\n", self::DATA_SIZE)
572
+ output << sprintf("CArray.union(:size=>%i) {\n", self::DATA_SIZE)
576
573
  else
577
- output << sprintf("CA.struct(:size=>%i) {\n", self::DATA_SIZE)
574
+ output << sprintf("CArray.struct(:size=>%i) {\n", self::DATA_SIZE)
578
575
  end
579
576
  members.each do |member|
580
577
  offset, type, option = *table[member]
@@ -623,10 +620,25 @@ class CAStruct
623
620
  end
624
621
  end
625
622
 
623
+ # @return [String] the layout of this record's class; see {CAStruct.spec}.
626
624
  def spec
627
625
  return self.class.spec
628
626
  end
629
-
627
+
628
+ end
629
+
630
+ # Hand the C extension a reference to CAStruct so it can override
631
+ # `[]` / `[]=` with the fast primitive-aware C path defined in
632
+ # ext/carray_struct.c.
633
+ #
634
+ # CAREFUL: this must run AFTER CAStruct's Ruby [] / []= are defined
635
+ # above (so the C versions can replace them) and AFTER FAST_PRIMITIVES /
636
+ # DISPATCH_TABLE keys are agreed upon (so the per-class struct definition
637
+ # in Builder sets them up before any instance is created). Subclasses
638
+ # created by Builder later don't need re-installation -- they inherit the
639
+ # C methods from CAStruct.
640
+ if CArray.respond_to?(:__install_castruct_methods__)
641
+ CArray.__install_castruct_methods__(CAStruct)
630
642
  end
631
643
 
632
644