carray 3.0.2 → 3.0.4

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 (247) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +568 -0
  3. data/README.md +6 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +220 -0
  7. data/ext/ca_axis_group.c +201 -123
  8. data/ext/ca_binop_dispatch.c +46 -52
  9. data/ext/ca_binop_dispatch.h +1 -1
  10. data/ext/ca_categorical_iterator.c +249 -183
  11. data/ext/ca_compare.c +7 -12
  12. data/ext/ca_for_buffer.h +21 -68
  13. data/ext/ca_group_iter.c +38 -23
  14. data/ext/ca_kernel_iterator.c +1765 -2076
  15. data/ext/ca_kernel_iterator.h +584 -566
  16. data/ext/ca_monop_dispatch.c +86 -63
  17. data/ext/ca_monop_dispatch.h +5 -0
  18. data/ext/ca_obj_array.c +223 -56
  19. data/ext/ca_obj_bincmp.c +172 -138
  20. data/ext/ca_obj_binop.c +116 -230
  21. data/ext/ca_obj_bitarray.c +40 -26
  22. data/ext/ca_obj_bitfield.c +100 -68
  23. data/ext/ca_obj_block.c +19 -9
  24. data/ext/ca_obj_byte_swap.c +45 -46
  25. data/ext/ca_obj_const_string.c +269 -44
  26. data/ext/ca_obj_face.c +612 -89
  27. data/ext/ca_obj_face.h +111 -47
  28. data/ext/ca_obj_fake.c +36 -79
  29. data/ext/ca_obj_farray.c +7 -3
  30. data/ext/ca_obj_field.c +33 -8
  31. data/ext/ca_obj_fixlen_string.c +23 -24
  32. data/ext/ca_obj_grid.c +541 -210
  33. data/ext/ca_obj_meld.c +331 -111
  34. data/ext/ca_obj_moncmp.c +80 -65
  35. data/ext/ca_obj_monop.c +177 -149
  36. data/ext/ca_obj_object.c +664 -248
  37. data/ext/ca_obj_record.c +63 -23
  38. data/ext/ca_obj_reduce.c +18 -16
  39. data/ext/ca_obj_refer.c +140 -58
  40. data/ext/ca_obj_remap.c +85 -24
  41. data/ext/ca_obj_repeat.c +24 -9
  42. data/ext/ca_obj_roll.c +121 -16
  43. data/ext/ca_obj_select.c +61 -58
  44. data/ext/ca_obj_select_axis.c +73 -107
  45. data/ext/ca_obj_shift.c +32 -102
  46. data/ext/ca_obj_stack.c +372 -122
  47. data/ext/ca_obj_stride.c +348 -140
  48. data/ext/ca_obj_string.c +88 -22
  49. data/ext/ca_obj_tile.c +38 -21
  50. data/ext/ca_obj_time.c +45 -31
  51. data/ext/ca_obj_timedelta.c +43 -29
  52. data/ext/ca_obj_transpose.c +17 -6
  53. data/ext/ca_obj_triop.c +125 -141
  54. data/ext/ca_obj_window.c +330 -44
  55. data/ext/ca_op_ipower.c +24 -11
  56. data/ext/ca_op_powi.h +57 -30
  57. data/ext/ca_pow5_table.h +656 -0
  58. data/ext/ca_sort_kernels.h +70 -0
  59. data/ext/ca_sweep_engine.c +104 -81
  60. data/ext/ca_sweep_engine.h +7 -20
  61. data/ext/ca_triop_dispatch.c +4 -1
  62. data/ext/ca_triop_dispatch.h +5 -2
  63. data/ext/caframe_csv_reader.c +542 -0
  64. data/ext/carray.h +241 -92
  65. data/ext/carray_access.c +608 -276
  66. data/ext/carray_address_basis.c +494 -97
  67. data/ext/carray_attribute.c +129 -4
  68. data/ext/carray_bincount.c +39 -7
  69. data/ext/carray_broadcast.c +56 -19
  70. data/ext/carray_build_flags.h +1 -1
  71. data/ext/carray_call_cfunc.c +108 -28
  72. data/ext/carray_cast.c +1576 -223
  73. data/ext/carray_cast_func.rb +77 -24
  74. data/ext/carray_class.c +4 -3
  75. data/ext/carray_conversion.c +208 -75
  76. data/ext/carray_copy.c +82 -24
  77. data/ext/carray_core.c +646 -162
  78. data/ext/carray_count.c +171 -88
  79. data/ext/carray_element.c +94 -60
  80. data/ext/carray_factorize.c +670 -324
  81. data/ext/carray_generate.c +260 -83
  82. data/ext/carray_histogram.c +209 -106
  83. data/ext/carray_hold.c +14 -16
  84. data/ext/carray_index_classifier.c +422 -243
  85. data/ext/carray_internal.h +692 -3
  86. data/ext/carray_kernels_bincmp.c +7 -0
  87. data/ext/carray_kernels_binop.c +487 -120
  88. data/ext/carray_kernels_init.c +653 -50
  89. data/ext/carray_kernels_moncmp.c +15 -8
  90. data/ext/carray_kernels_monop.c +99 -43
  91. data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
  92. data/ext/carray_kernels_reduce_boolean.c +112 -36
  93. data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
  94. data/ext/carray_kernels_reduce_extreme.c +3032 -2892
  95. data/ext/carray_kernels_reduce_variance.c +439 -500
  96. data/ext/carray_kernels_scan.c +1097 -488
  97. data/ext/carray_kernels_search.c +3787 -2176
  98. data/ext/carray_kernels_sort.c +1200 -920
  99. data/ext/carray_kernels_triop.c +479 -7
  100. data/ext/carray_key_windows.c +216 -0
  101. data/ext/carray_lazy.c +967 -153
  102. data/ext/carray_loop.c +81 -65
  103. data/ext/carray_mask.c +910 -324
  104. data/ext/carray_math_kernel.h +1 -0
  105. data/ext/carray_median_percentile.c +299 -100
  106. data/ext/carray_memory_view.c +872 -758
  107. data/ext/carray_operator.c +1171 -364
  108. data/ext/carray_order.c +505 -133
  109. data/ext/carray_partition.c +112 -65
  110. data/ext/carray_random.c +327 -239
  111. data/ext/carray_round.c +273 -0
  112. data/ext/carray_scatter.c +346 -78
  113. data/ext/carray_segment.c +257 -0
  114. data/ext/carray_slab.c +249 -86
  115. data/ext/carray_sort.c +224 -227
  116. data/ext/carray_sort_kernel.c +8 -1
  117. data/ext/carray_struct.c +118 -39
  118. data/ext/carray_test.c +193 -69
  119. data/ext/carray_time_text.c +1182 -0
  120. data/ext/carray_undef.c +4 -0
  121. data/ext/carray_utils.c +579 -113
  122. data/ext/extconf.rb +27 -2
  123. data/ext/mk_call_cfunc.rb +19 -4
  124. data/ext/mk_pow5_table.rb +35 -0
  125. data/ext/mkkernel.rb +1202 -1014
  126. data/ext/ruby_carray.c +17 -5
  127. data/ext/version.h +4 -4
  128. data/ext/version.rb +4 -2
  129. data/lib/carray/attribute.rb +160 -17
  130. data/lib/carray/autoload_carray.rb +23 -27
  131. data/lib/carray/autoload_method_extension.rb +20 -5
  132. data/lib/carray/axis_group.rb +360 -63
  133. data/lib/carray/basics.rb +162 -28
  134. data/lib/carray/bincount_nd.rb +18 -2
  135. data/lib/carray/block_iterator.rb +231 -58
  136. data/lib/carray/boolean_reduce.rb +34 -42
  137. data/lib/carray/categorical.rb +242 -52
  138. data/lib/carray/categorical_iterator.rb +437 -827
  139. data/lib/carray/complex.rb +41 -34
  140. data/lib/carray/conditional.rb +150 -14
  141. data/lib/carray/const_string.rb +59 -4
  142. data/lib/carray/construct.rb +15 -11
  143. data/lib/carray/data_type_extension.rb +1 -0
  144. data/lib/carray/distance_limit.rb +77 -0
  145. data/lib/carray/fixlen_string.rb +7 -1
  146. data/lib/carray/frame/column_verbs.rb +70 -0
  147. data/lib/carray/frame/concat.rb +157 -6
  148. data/lib/carray/frame/convert.rb +82 -7
  149. data/lib/carray/frame/csv_parser.rb +688 -87
  150. data/lib/carray/frame/describe.rb +80 -0
  151. data/lib/carray/frame/frame.rb +369 -45
  152. data/lib/carray/frame/group.rb +631 -90
  153. data/lib/carray/frame/io.rb +506 -79
  154. data/lib/carray/frame/join.rb +411 -62
  155. data/lib/carray/frame/records.rb +116 -19
  156. data/lib/carray/frame/reshape.rb +455 -0
  157. data/lib/carray/frame/sort.rb +30 -18
  158. data/lib/carray/frame/verbs.rb +868 -61
  159. data/lib/carray/frame/windows.rb +117 -0
  160. data/lib/carray/frame.rb +4 -0
  161. data/lib/carray/fuse_source.rb +118 -2
  162. data/lib/carray/fusion.rb +257 -42
  163. data/lib/carray/histogram.rb +6 -1
  164. data/lib/carray/inspect.rb +54 -13
  165. data/lib/carray/iterator.rb +99 -9
  166. data/lib/carray/key_windows.rb +620 -0
  167. data/lib/carray/lazy.rb +216 -193
  168. data/lib/carray/mask_gap_fill.rb +112 -92
  169. data/lib/carray/math.rb +32 -19
  170. data/lib/carray/meld_reduce.rb +52 -21
  171. data/lib/carray/methods/align_addr.rb +8 -4
  172. data/lib/carray/methods/bin.rb +26 -18
  173. data/lib/carray/methods/bincount.rb +18 -2
  174. data/lib/carray/methods/bit_string.rb +15 -4
  175. data/lib/carray/methods/choose.rb +16 -3
  176. data/lib/carray/methods/composition.rb +18 -10
  177. data/lib/carray/methods/factorize.rb +2 -2
  178. data/lib/carray/methods/gather_nd.rb +25 -5
  179. data/lib/carray/methods/insert_block.rb +9 -14
  180. data/lib/carray/methods/is_between.rb +59 -0
  181. data/lib/carray/methods/is_in.rb +140 -10
  182. data/lib/carray/methods/join.rb +2 -7
  183. data/lib/carray/methods/lag.rb +49 -0
  184. data/lib/carray/methods/locate_addr.rb +182 -45
  185. data/lib/carray/methods/mode.rb +18 -1
  186. data/lib/carray/methods/nunique.rb +11 -6
  187. data/lib/carray/methods/repeat.rb +1 -20
  188. data/lib/carray/methods/resize.rb +21 -23
  189. data/lib/carray/methods/snap.rb +34 -12
  190. data/lib/carray/methods/unique.rb +11 -2
  191. data/lib/carray/methods/value_counts.rb +4 -11
  192. data/lib/carray/record_order.rb +208 -0
  193. data/lib/carray/rng.rb +3 -0
  194. data/lib/carray/runtime.rb +65 -17
  195. data/lib/carray/segment_iterator.rb +1221 -0
  196. data/lib/carray/serialize.rb +214 -30
  197. data/lib/carray/slab_iterator.rb +101 -27
  198. data/lib/carray/stack.rb +10 -4
  199. data/lib/carray/string.rb +105 -5
  200. data/lib/carray/string_operation_extension.rb +12 -4
  201. data/lib/carray/struct.rb +137 -58
  202. data/lib/carray/struct_builder.rb +124 -17
  203. data/lib/carray/time.rb +1222 -186
  204. data/lib/carray/window_iterator.rb +242 -81
  205. data/lib/carray.rb +9 -2
  206. data/yard-stubs/ca_obj_array.rb +4 -4
  207. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  208. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  209. data/yard-stubs/ca_obj_fake.rb +1 -1
  210. data/yard-stubs/ca_obj_field.rb +5 -1
  211. data/yard-stubs/ca_obj_grid.rb +21 -10
  212. data/yard-stubs/ca_obj_refer.rb +5 -4
  213. data/yard-stubs/ca_obj_roll.rb +2 -3
  214. data/yard-stubs/ca_obj_shift.rb +5 -1
  215. data/yard-stubs/ca_obj_stride.rb +10 -7
  216. data/yard-stubs/ca_obj_tile.rb +1 -1
  217. data/yard-stubs/ca_obj_transpose.rb +1 -1
  218. data/yard-stubs/ca_obj_window.rb +9 -5
  219. data/yard-stubs/carray_access.rb +11 -5
  220. data/yard-stubs/carray_attribute.rb +0 -5
  221. data/yard-stubs/carray_broadcast.rb +1 -1
  222. data/yard-stubs/carray_cast.rb +88 -43
  223. data/yard-stubs/carray_conversion.rb +15 -2
  224. data/yard-stubs/carray_copy.rb +12 -5
  225. data/yard-stubs/carray_core.rb +3 -3
  226. data/yard-stubs/carray_count.rb +9 -0
  227. data/yard-stubs/carray_element.rb +10 -4
  228. data/yard-stubs/carray_generate.rb +21 -1
  229. data/yard-stubs/carray_lazy.rb +5 -1
  230. data/yard-stubs/carray_loop.rb +4 -1
  231. data/yard-stubs/carray_mask.rb +60 -12
  232. data/yard-stubs/carray_math.rb +52 -14
  233. data/yard-stubs/carray_median_percentile.rb +15 -8
  234. data/yard-stubs/carray_memory_view.rb +40 -23
  235. data/yard-stubs/carray_order.rb +61 -29
  236. data/yard-stubs/carray_random.rb +13 -4
  237. data/yard-stubs/carray_scatter.rb +21 -9
  238. data/yard-stubs/carray_segment.rb +72 -0
  239. data/yard-stubs/carray_slab.rb +10 -2
  240. data/yard-stubs/carray_sort.rb +49 -8
  241. data/yard-stubs/carray_test.rb +15 -3
  242. data/yard-stubs/carray_utils.rb +0 -26
  243. metadata +19 -5
  244. data/ext/ca_for_each_element.h +0 -239
  245. data/ext/carray_kernels_map.c +0 -3467
  246. data/lib/carray/arrow_tensor.rb +0 -401
  247. data/lib/carray/table.rb +0 -108
@@ -51,7 +51,6 @@
51
51
  #include <string.h>
52
52
  #include <math.h>
53
53
 
54
- #define CA_SORT_INSERTION_THRESHOLD 16
55
54
 
56
55
  /* ---------------------------------------------------------------------------
57
56
  DEFINE_SORT_MERGE(TYPE, SUFFIX)
@@ -616,5 +615,13 @@ DEFINE_PARTITION_QUICK(uint64_t, u64);
616
615
  DEFINE_PARTITION_QUICK(float32_t, f32);
617
616
  DEFINE_PARTITION_QUICK(double, f64);
618
617
 
618
+ /* The pair mergesort above for object pairs, which have no `<`: every
619
+ comparison goes through cmp (see CA_SORT_MERGE_BY_CMP). */
620
+ void
621
+ ca_sort_merge_pair_object (ca_pair_object *a, ca_pair_object *aux,
622
+ ca_size_t n,
623
+ int (*cmp)(const void *, const void *))
624
+ CA_SORT_MERGE_BY_CMP(ca_pair_object)
625
+
619
626
  /* No Init function: this file exposes only C kernels (via ca_sort_kernels.h);
620
627
  it registers no Ruby methods. */
data/ext/carray_struct.c CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  C-native member access for CAStruct subclasses. The hooked
4
4
  methods (`[]`, `[]=`, `initialize`) are installed on a subclass by
5
- `CArray.__install_castruct_methods__(klass)` — called from the
5
+ `CArray.install_struct_methods(klass)` — called from the
6
6
  bottom of lib/carray/struct.rb once CAStruct is defined, so this
7
7
  ext can load before that file.
8
8
 
@@ -16,11 +16,13 @@
16
16
  (primitive / bitfield / endian-swap)
17
17
  DISPATCH_TABLE hit -> Proc invoked via rb_funcall("call")
18
18
  (nested struct / CArray template / fixlen)
19
- miss -> send(name) / send(name=)
19
+ a Symbol, or a name that is not a member, raises: a member is
20
+ s["name"] or s[position], and its accessor s.name
20
21
 
21
22
  ---------------------------------------------------------------------------- */
22
23
 
23
24
  #include "carray.h"
25
+ #include "carray_internal.h" /* ca_obj2integer_fitting */
24
26
  #include <string.h>
25
27
 
26
28
  /* ---------------------------------------------------------------------
@@ -54,7 +56,7 @@ ca_struct_read_primitive_fast (const char *p, int type_code)
54
56
  case CA_BOOLEAN: {
55
57
  int8_t v;
56
58
  memcpy(&v, p, 1);
57
- return v ? INT2FIX(1) : INT2FIX(0);
59
+ return v ? Qtrue : Qfalse;
58
60
  }
59
61
  case CA_INT8: {
60
62
  int8_t v;
@@ -116,47 +118,55 @@ ca_struct_write_primitive_fast (char *p, int type_code, VALUE val)
116
118
  {
117
119
  switch (type_code) {
118
120
  case CA_BOOLEAN: {
119
- int8_t v = RTEST(val) && val != INT2FIX(0) ? 1 : 0;
121
+ /* The values a boolean array takes: true / false / 1 / 0. */
122
+ int8_t v;
123
+ if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
124
+ else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
125
+ else {
126
+ rb_raise(rb_eCADataTypeError,
127
+ "can't cast object '%"PRIsVALUE"' to <boolean>",
128
+ rb_inspect(val));
129
+ }
120
130
  memcpy(p, &v, 1);
121
131
  return;
122
132
  }
123
133
  case CA_INT8: {
124
- int8_t v = (int8_t) NUM2INT(val);
134
+ int8_t v = (int8_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT8));
125
135
  memcpy(p, &v, 1);
126
136
  return;
127
137
  }
128
138
  case CA_UINT8: {
129
- uint8_t v = (uint8_t) NUM2UINT(val);
139
+ uint8_t v = (uint8_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT8));
130
140
  memcpy(p, &v, 1);
131
141
  return;
132
142
  }
133
143
  case CA_INT16: {
134
- int16_t v = (int16_t) NUM2INT(val);
144
+ int16_t v = (int16_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT16));
135
145
  memcpy(p, &v, 2);
136
146
  return;
137
147
  }
138
148
  case CA_UINT16: {
139
- uint16_t v = (uint16_t) NUM2UINT(val);
149
+ uint16_t v = (uint16_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT16));
140
150
  memcpy(p, &v, 2);
141
151
  return;
142
152
  }
143
153
  case CA_INT32: {
144
- int32_t v = (int32_t) NUM2LONG(val);
154
+ int32_t v = (int32_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT32));
145
155
  memcpy(p, &v, 4);
146
156
  return;
147
157
  }
148
158
  case CA_UINT32: {
149
- uint32_t v = (uint32_t) NUM2ULONG(val);
159
+ uint32_t v = (uint32_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT32));
150
160
  memcpy(p, &v, 4);
151
161
  return;
152
162
  }
153
163
  case CA_INT64: {
154
- int64_t v = (int64_t) NUM2LL(val);
164
+ int64_t v = (int64_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT64));
155
165
  memcpy(p, &v, 8);
156
166
  return;
157
167
  }
158
168
  case CA_UINT64: {
159
- uint64_t v = (uint64_t) NUM2ULL(val);
169
+ uint64_t v = (uint64_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT64));
160
170
  memcpy(p, &v, 8);
161
171
  return;
162
172
  }
@@ -201,8 +211,10 @@ ca_struct_read_bitfield_fast (const char *p, int view_bytes,
201
211
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
202
212
  : ((uint64_t)1 << bits) - 1;
203
213
  uint64_t val = (word >> bit_in_word) & mask;
204
- /* Returned as an unsigned Integer; bit-fields don't carry a sign
214
+ /* A 1-bit field is a boolean, as a record array's column of it is.
215
+ Wider fields are unsigned Integers; bit-fields don't carry a sign
205
216
  bit in carray's CABitfield convention either. */
217
+ if (bits == 1) return val ? Qtrue : Qfalse;
206
218
  if (bits <= 31) return INT2FIX((long) val);
207
219
  if (bits <= 32) return ULONG2NUM((unsigned long) val);
208
220
  return ULL2NUM(val);
@@ -212,7 +224,19 @@ static void
212
224
  ca_struct_write_bitfield_fast (char *p, int view_bytes,
213
225
  int bit_in_word, int bits, VALUE val)
214
226
  {
215
- uint64_t v = NUM2ULL(val);
227
+ uint64_t v;
228
+ if ( bits == 1 ) {
229
+ if ( val == Qtrue || val == INT2FIX(1) ) v = 1;
230
+ else if ( val == Qfalse || val == INT2FIX(0) ) v = 0;
231
+ else {
232
+ rb_raise(rb_eCADataTypeError,
233
+ "can't cast object '%"PRIsVALUE"' to <boolean>",
234
+ rb_inspect(val));
235
+ }
236
+ }
237
+ else {
238
+ v = NUM2ULL(val);
239
+ }
216
240
  uint64_t mask = (bits >= 64) ? ~(uint64_t)0
217
241
  : ((uint64_t)1 << bits) - 1;
218
242
  v &= mask;
@@ -310,32 +334,32 @@ ca_struct_write_endian_swapped_fast (char *p, int type_code, VALUE val)
310
334
  {
311
335
  switch (type_code) {
312
336
  case CA_INT16: {
313
- uint16_t v = (uint16_t) NUM2INT(val);
337
+ uint16_t v = (uint16_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT16));
314
338
  v = CA_BSWAP16(v); memcpy(p, &v, 2);
315
339
  return;
316
340
  }
317
341
  case CA_UINT16: {
318
- uint16_t v = (uint16_t) NUM2UINT(val);
342
+ uint16_t v = (uint16_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT16));
319
343
  v = CA_BSWAP16(v); memcpy(p, &v, 2);
320
344
  return;
321
345
  }
322
346
  case CA_INT32: {
323
- uint32_t v = (uint32_t) NUM2LONG(val);
347
+ uint32_t v = (uint32_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT32));
324
348
  v = CA_BSWAP32(v); memcpy(p, &v, 4);
325
349
  return;
326
350
  }
327
351
  case CA_UINT32: {
328
- uint32_t v = (uint32_t) NUM2ULONG(val);
352
+ uint32_t v = (uint32_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT32));
329
353
  v = CA_BSWAP32(v); memcpy(p, &v, 4);
330
354
  return;
331
355
  }
332
356
  case CA_INT64: {
333
- uint64_t v = (uint64_t) NUM2LL(val);
357
+ uint64_t v = (uint64_t) NUM2LL(ca_obj2integer_fitting(val, CA_INT64));
334
358
  v = CA_BSWAP64(v); memcpy(p, &v, 8);
335
359
  return;
336
360
  }
337
361
  case CA_UINT64: {
338
- uint64_t v = NUM2ULL(val);
362
+ uint64_t v = (uint64_t) rb_num2ull(ca_obj2integer_fitting(val, CA_UINT64));
339
363
  v = CA_BSWAP64(v); memcpy(p, &v, 8);
340
364
  return;
341
365
  }
@@ -384,6 +408,45 @@ ca_struct_resolve_name (VALUE klass, VALUE name)
384
408
  return rb_funcall(name, rb_intern("to_s"), 0);
385
409
  }
386
410
 
411
+ /* The member an element's `[]` / `[]=` names: a String naming a member,
412
+ or an Integer position. A Symbol is not a member name here -- the
413
+ member is s["name"] or the accessor s.name -- and a name that is not a
414
+ member raises rather than calling a method of that name. */
415
+ static VALUE
416
+ ca_struct_member_name (VALUE klass, VALUE name)
417
+ {
418
+ VALUE name_s;
419
+ if ( SYMBOL_P(name) ) {
420
+ VALUE str = rb_sym2str(name);
421
+ if ( ! NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
422
+ str)) ) {
423
+ rb_raise(rb_eTypeError,
424
+ "a struct member is named by a String (s[\"%"PRIsVALUE"\"]) or "
425
+ "read by its accessor (s.%"PRIsVALUE"), not by a Symbol",
426
+ str, str);
427
+ }
428
+ rb_raise(rb_eTypeError,
429
+ "a struct member is named by a String, not by a Symbol "
430
+ "(%"PRIsVALUE" has no member %"PRIsVALUE" either)",
431
+ klass, rb_inspect(str));
432
+ }
433
+ if ( ! RB_TYPE_P(name, T_STRING) && ! RB_TYPE_P(name, T_FIXNUM) ) {
434
+ rb_raise(rb_eTypeError,
435
+ "a struct member is named by a String or an Integer position "
436
+ "(got %s)", rb_obj_classname(name));
437
+ }
438
+ name_s = ca_struct_resolve_name(klass, name);
439
+ if ( NIL_P(name_s)
440
+ || NIL_P(rb_hash_aref(rb_const_get(klass, rb_intern("MEMBER_TABLE")),
441
+ name_s)) ) {
442
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no member %"PRIsVALUE
443
+ " (members: %"PRIsVALUE")",
444
+ klass, rb_inspect(name),
445
+ rb_inspect(rb_const_get(klass, rb_intern("MEMBERS"))));
446
+ }
447
+ return name_s;
448
+ }
449
+
387
450
  /* ---------------------------------------------------------------------
388
451
  Shared write-dispatch helper reused by `[]=` and `initialize`.
389
452
  Returns 1 if the member was written, 0 if neither table contained
@@ -446,7 +509,7 @@ static VALUE
446
509
  rb_ca_struct_aref (VALUE self, VALUE name)
447
510
  {
448
511
  VALUE klass = rb_obj_class(self);
449
- VALUE name_s = ca_struct_resolve_name(klass, name);
512
+ VALUE name_s = ca_struct_member_name(klass, name);
450
513
 
451
514
  /* Fast path. FAST_PRIMITIVES may be absent on subclasses built
452
515
  outside Builder — fall through to the dispatch table in that
@@ -454,12 +517,14 @@ rb_ca_struct_aref (VALUE self, VALUE name)
454
517
  if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
455
518
  VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
456
519
  VALUE entry = rb_hash_aref(fast_table, name_s);
457
- if (!NIL_P(entry)) {
520
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
521
+ CArray *ca;
522
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
523
+ /* The fast path reads ca->ptr, which only an entity holds. A nested
524
+ element's @data is a view on its parent's bytes and goes through the
525
+ dispatch procs below, which reach them through field views. */
526
+ if (!NIL_P(entry) && ca_is_entity(ca)) {
458
527
  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
528
  switch (kind) {
464
529
  case FAST_KIND_PRIMITIVE: {
465
530
  /* [kind, offset, type_code] */
@@ -502,8 +567,9 @@ rb_ca_struct_aref (VALUE self, VALUE name)
502
567
  return rb_funcall(reader, rb_intern("call"), 1, data_val);
503
568
  }
504
569
 
505
- /* Unknown name: fall back to send(name) for computed members. */
506
- return rb_funcall(self, rb_to_id(name), 0);
570
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no reader for member %"PRIsVALUE,
571
+ klass, name_s);
572
+ return Qnil;
507
573
  }
508
574
 
509
575
  /* ---------------------------------------------------------------------
@@ -514,18 +580,31 @@ static VALUE
514
580
  rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
515
581
  {
516
582
  VALUE klass = rb_obj_class(self);
517
- VALUE name_s = ca_struct_resolve_name(klass, name);
583
+ VALUE name_s;
584
+
585
+ /* The fast path writes into the record's bytes directly. */
586
+ rb_check_frozen(self);
587
+ name_s = ca_struct_member_name(klass, name);
588
+
589
+ /* An element is one record of values: it has no mask to hold UNDEF. A
590
+ record is masked in its record array (a[i] = UNDEF). */
591
+ if ( val == CA_UNDEF ) {
592
+ rb_raise(rb_eTypeError,
593
+ "a struct element has no masked state; mask the record in its "
594
+ "record array instead");
595
+ }
518
596
 
519
597
  /* Fast path (see rb_ca_struct_aref for the absent-FAST_PRIMITIVES
520
598
  rationale and entry-shape contract). */
521
599
  if (rb_const_defined(klass, rb_intern("FAST_PRIMITIVES"))) {
522
600
  VALUE fast_table = rb_const_get(klass, rb_intern("FAST_PRIMITIVES"));
523
601
  VALUE entry = rb_hash_aref(fast_table, name_s);
524
- if (!NIL_P(entry)) {
602
+ VALUE data_val = rb_ivar_get(self, rb_intern("@data"));
603
+ CArray *ca;
604
+ TypedData_Get_Struct(data_val, CArray, &carray_data_type, ca);
605
+ /* Only an entity @data is written through ca->ptr (see the reader). */
606
+ if (!NIL_P(entry) && ca_is_entity(ca)) {
525
607
  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
608
  switch (kind) {
530
609
  case FAST_KIND_PRIMITIVE: {
531
610
  ca_size_t offset = NUM2SIZE(rb_ary_entry(entry, 1));
@@ -565,9 +644,9 @@ rb_ca_struct_aset (VALUE self, VALUE name, VALUE val)
565
644
  return val;
566
645
  }
567
646
 
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);
647
+ rb_raise(rb_eArgError, "%"PRIsVALUE" has no writer for member %"PRIsVALUE,
648
+ klass, name_s);
649
+ return Qnil;
571
650
  }
572
651
 
573
652
  /* ---------------------------------------------------------------------
@@ -689,7 +768,7 @@ void
689
768
  Init_carray_struct (void)
690
769
  {
691
770
  /* 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);
771
+ `CArray.install_struct_methods(klass)`. */
772
+ rb_define_private_method(rb_singleton_class(rb_cCArray), "install_struct_methods",
773
+ rb_carray_install_castruct_methods, 1);
695
774
  }