carray 3.0.6 → 3.0.7

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +110 -0
  3. data/README.md +24 -3
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_group.c +6 -6
  6. data/ext/ca_binop_dispatch.c +8 -0
  7. data/ext/ca_binop_dispatch.h +1 -0
  8. data/ext/ca_categorical_iterator.c +7 -7
  9. data/ext/ca_kernel_iterator.h +6 -4
  10. data/ext/ca_obj_array.c +4 -4
  11. data/ext/ca_obj_bincmp.c +19 -3
  12. data/ext/ca_obj_binop.c +36 -11
  13. data/ext/ca_obj_bitarray.c +2 -4
  14. data/ext/ca_obj_bitfield.c +86 -4
  15. data/ext/ca_obj_byte_swap.c +2 -4
  16. data/ext/ca_obj_const_string.c +422 -257
  17. data/ext/ca_obj_face.c +71 -33
  18. data/ext/ca_obj_fake.c +112 -13
  19. data/ext/ca_obj_farray.c +7 -1
  20. data/ext/ca_obj_fixlen_string.c +51 -17
  21. data/ext/ca_obj_grid.c +12 -7
  22. data/ext/ca_obj_meld.c +5 -5
  23. data/ext/ca_obj_monop.c +65 -39
  24. data/ext/ca_obj_object.c +9 -9
  25. data/ext/ca_obj_reduce.c +2 -4
  26. data/ext/ca_obj_refer.c +104 -10
  27. data/ext/ca_obj_repeat.c +1 -1
  28. data/ext/ca_obj_roll.c +2 -4
  29. data/ext/ca_obj_select.c +8 -6
  30. data/ext/ca_obj_select_axis.c +8 -6
  31. data/ext/ca_obj_stack.c +5 -5
  32. data/ext/ca_obj_stride.c +46 -8
  33. data/ext/ca_obj_string.c +2 -2
  34. data/ext/ca_obj_tile.c +2 -4
  35. data/ext/ca_obj_triop.c +15 -12
  36. data/ext/ca_obj_window.c +22 -15
  37. data/ext/ca_op_ipower.c +63 -15
  38. data/ext/ca_op_powi.h +6 -29
  39. data/ext/ca_sort_kernels.h +3 -0
  40. data/ext/ca_sweep_engine.c +90 -23
  41. data/ext/ca_sweep_engine.h +5 -6
  42. data/ext/caframe_csv_reader.c +1 -1
  43. data/ext/carray.h +3 -1
  44. data/ext/carray_access.c +98 -39
  45. data/ext/carray_address_basis.c +98 -24
  46. data/ext/carray_broadcast.c +2 -2
  47. data/ext/carray_call_cfunc.c +5 -8
  48. data/ext/carray_cast.c +311 -37
  49. data/ext/carray_cast_func.rb +37 -5
  50. data/ext/carray_conversion.c +56 -2
  51. data/ext/carray_copy.c +24 -0
  52. data/ext/carray_core.c +69 -6
  53. data/ext/carray_count.c +28 -9
  54. data/ext/carray_element.c +9 -3
  55. data/ext/carray_factorize.c +11 -11
  56. data/ext/carray_histogram.c +2 -2
  57. data/ext/carray_index_classifier.c +7 -5
  58. data/ext/carray_internal.h +94 -0
  59. data/ext/carray_kernels_binop.c +55 -55
  60. data/ext/carray_kernels_init.c +14 -14
  61. data/ext/carray_kernels_monop.c +90 -140
  62. data/ext/carray_lazy.c +16 -1
  63. data/ext/carray_loop.c +9 -2
  64. data/ext/carray_mask.c +118 -11
  65. data/ext/carray_median_percentile.c +13 -8
  66. data/ext/carray_memory_view.c +41 -22
  67. data/ext/carray_operator.c +72 -11
  68. data/ext/carray_order.c +52 -9
  69. data/ext/carray_partition.c +1 -1
  70. data/ext/carray_random.c +1 -1
  71. data/ext/carray_round.c +24 -1
  72. data/ext/carray_scatter.c +4 -0
  73. data/ext/carray_segment.c +1 -1
  74. data/ext/carray_slab.c +13 -4
  75. data/ext/carray_sort.c +6 -3
  76. data/ext/carray_sort_kernel.c +24 -0
  77. data/ext/carray_struct.c +21 -6
  78. data/ext/carray_test.c +85 -39
  79. data/ext/carray_time_text.c +6 -0
  80. data/ext/carray_utils.c +124 -54
  81. data/ext/mk_call_cfunc.rb +6 -11
  82. data/ext/mkkernel.rb +37 -27
  83. data/ext/version.h +4 -4
  84. data/lib/carray/axis_group.rb +9 -6
  85. data/lib/carray/basics.rb +26 -8
  86. data/lib/carray/bincount_nd.rb +8 -2
  87. data/lib/carray/block_iterator.rb +48 -21
  88. data/lib/carray/categorical.rb +21 -8
  89. data/lib/carray/categorical_iterator.rb +2 -1
  90. data/lib/carray/conditional.rb +52 -4
  91. data/lib/carray/const_string.rb +74 -0
  92. data/lib/carray/construct.rb +2 -2
  93. data/lib/carray/core_extensions.rb +19 -68
  94. data/lib/carray/data_type_extension.rb +1 -0
  95. data/lib/carray/fixlen_string.rb +83 -1
  96. data/lib/carray/frame/column_verbs.rb +61 -6
  97. data/lib/carray/frame/concat.rb +50 -6
  98. data/lib/carray/frame/csv_parser.rb +13 -4
  99. data/lib/carray/frame/frame.rb +38 -6
  100. data/lib/carray/frame/group.rb +67 -40
  101. data/lib/carray/frame/io.rb +10 -5
  102. data/lib/carray/frame/join.rb +20 -6
  103. data/lib/carray/frame/records.rb +6 -4
  104. data/lib/carray/frame/reshape.rb +25 -5
  105. data/lib/carray/frame/verbs.rb +78 -25
  106. data/lib/carray/frame/windows.rb +30 -6
  107. data/lib/carray/fuse_source.rb +4 -2
  108. data/lib/carray/iterator.rb +8 -0
  109. data/lib/carray/key_windows.rb +10 -4
  110. data/lib/carray/lazy.rb +12 -17
  111. data/lib/carray/methods/bin.rb +3 -0
  112. data/lib/carray/methods/composition.rb +1 -1
  113. data/lib/carray/methods/factorize.rb +2 -0
  114. data/lib/carray/methods/gather_nd.rb +4 -1
  115. data/lib/carray/methods/is_in.rb +23 -3
  116. data/lib/carray/methods/locate_addr.rb +63 -12
  117. data/lib/carray/methods/repeat.rb +11 -1
  118. data/lib/carray/record_order.rb +35 -6
  119. data/lib/carray/runtime.rb +10 -0
  120. data/lib/carray/segment_iterator.rb +19 -16
  121. data/lib/carray/serialize.rb +22 -0
  122. data/lib/carray/slab_iterator.rb +1 -1
  123. data/lib/carray/stack.rb +3 -3
  124. data/lib/carray/string.rb +6 -6
  125. data/lib/carray/string_operation_extension.rb +25 -4
  126. data/lib/carray/struct_builder.rb +4 -1
  127. data/lib/carray/time.rb +190 -46
  128. data/lib/carray/window_iterator.rb +48 -12
  129. data/yard-stubs/ca_obj_bitfield.rb +4 -1
  130. data/yard-stubs/ca_obj_refer.rb +3 -0
  131. data/yard-stubs/ca_obj_window.rb +2 -1
  132. data/yard-stubs/carray_access.rb +4 -2
  133. data/yard-stubs/carray_cast.rb +6 -0
  134. data/yard-stubs/carray_count.rb +6 -3
  135. data/yard-stubs/carray_math.rb +5 -0
  136. data/yard-stubs/carray_order.rb +6 -0
  137. data/yard-stubs/carray_scatter.rb +2 -1
  138. data/yard-stubs/carray_test.rb +18 -5
  139. metadata +2 -2
@@ -36,6 +36,28 @@ ca_sweep_shape_str (CArray *ca)
36
36
  return s;
37
37
  }
38
38
 
39
+ /* fsync is the kernel author's string: one '0' (input) or '1' (output) per
40
+ * operand. Any other character would read as neither, and that operand
41
+ * would be neither written back nor given a mask. */
42
+ static void
43
+ ca_sweep_check_fsync (ca_sweep_state_t *st, const char *fallback_label)
44
+ {
45
+ int k_op;
46
+ if ((int) strlen(st->fsync) != st->n_ops) {
47
+ rb_raise(rb_eRuntimeError,
48
+ "[BUG] invalid length of fsync arg in %s (expected %d)",
49
+ st->src_label ? st->src_label : fallback_label, st->n_ops);
50
+ }
51
+ for (k_op = 0; k_op < st->n_ops; k_op++) {
52
+ if (st->fsync[k_op] != '0' && st->fsync[k_op] != '1') {
53
+ rb_raise(rb_eRuntimeError,
54
+ "[BUG] invalid character '%c' in fsync arg \"%s\" in %s "
55
+ "(expected '0' or '1')", st->fsync[k_op], st->fsync,
56
+ st->src_label ? st->src_label : fallback_label);
57
+ }
58
+ }
59
+ }
60
+
39
61
  void
40
62
  ca_sweep_refuse_shapes (CArray *a, CArray *b)
41
63
  {
@@ -60,8 +82,10 @@ ca_sweep_same_shape (CArray *a, CArray *b)
60
82
 
61
83
  /* Pair the operands: a scalar walks with stride 0, every other operand
62
84
  * must have the shape of the first non-scalar one, which is returned (NULL
63
- * when all operands are scalars). Sets stride[] and n_kernel. Allocates
64
- * nothing, so it runs before any operand is acquired. */
85
+ * when all operands are scalars). A scalar OUTPUT would have every cell of
86
+ * the walk written over one cell, so it is refused beside an array. Sets
87
+ * stride[] and n_kernel. Allocates nothing, so it runs before any operand
88
+ * is acquired. */
65
89
  static CArray *
66
90
  ca_sweep_pair_operands (ca_sweep_state_t *st)
67
91
  {
@@ -82,6 +106,14 @@ ca_sweep_pair_operands (ca_sweep_state_t *st)
82
106
  ca_sweep_refuse_shapes(donor, ca);
83
107
  }
84
108
  }
109
+ for (k_op = 0; donor && k_op < st->n_ops; k_op++) {
110
+ if (st->fsync[k_op] == '1' && ca_is_scalar(st->cx[k_op])) {
111
+ rb_raise(rb_eArgError,
112
+ "%s: a scalar output has one cell, and an array of %lld cells "
113
+ "was given as an operand; a scalar output pairs only with "
114
+ "scalars", ca_calling_method_name(), (long long) st->n_kernel);
115
+ }
116
+ }
85
117
  return donor;
86
118
  }
87
119
 
@@ -117,6 +149,29 @@ ca_sweep_any_input_mask (ca_sweep_state_t *st)
117
149
  return 0;
118
150
  }
119
151
 
152
+ /* The whole-buffer scratch for INPUT ca. Object cells read from a lazy
153
+ * operand or a CAObject exist nowhere else until the kernel has used them,
154
+ * and the kernel may call Ruby, so the scratch is held for GC as long as it
155
+ * lives. It is recorded in owned_buf before the hold, which can raise. */
156
+ static char *
157
+ ca_sweep_scratch_alloc (ca_sweep_state_t *st, int k_op)
158
+ {
159
+ CArray *ca = st->cx[k_op];
160
+ st->owned_buf[k_op] = xmalloc(ca->elements * ca->bytes);
161
+ if (ca->data_type == CA_OBJECT) {
162
+ ca_object_cells_clear(st->owned_buf[k_op], ca->elements);
163
+ ca_gc_hold_buffer(st->owned_buf[k_op], ca->elements);
164
+ }
165
+ return st->owned_buf[k_op];
166
+ }
167
+
168
+ static void
169
+ ca_sweep_scratch_free (void *buf)
170
+ {
171
+ ca_gc_release_buffer(buf);
172
+ xfree(buf);
173
+ }
174
+
120
175
  /* Give back everything acquire has taken so far, without syncing: detach
121
176
  * attached operands, free owned scratch (xmalloc on the whole-buffer path,
122
177
  * arena on the chunked one) and the mask buffers. Called when a read
@@ -132,7 +187,7 @@ ca_sweep_abort (ca_sweep_state_t *st, int chunked)
132
187
  ca_detach(st->cx[k_op]);
133
188
  } else if (st->owned_buf[k_op]) {
134
189
  if (chunked) ca_lazy_arena_release(st->owned_buf[k_op]);
135
- else xfree(st->owned_buf[k_op]);
190
+ else ca_sweep_scratch_free(st->owned_buf[k_op]);
136
191
  }
137
192
  st->owned_buf[k_op] = NULL;
138
193
  }
@@ -147,6 +202,21 @@ ca_sweep_abort (ca_sweep_state_t *st, int chunked)
147
202
  }
148
203
  }
149
204
 
205
+ /* The outputs' masks are overwritten by the OR of the input masks. When no
206
+ * input is masked that is all clear, and there is no m0 to carry it: clear
207
+ * them here, before an output is attached, so a view gathers the cleared
208
+ * mask. An output without a mask does not get one. */
209
+ static void
210
+ ca_sweep_clear_output_masks (ca_sweep_state_t *st)
211
+ {
212
+ int k_op;
213
+ for (k_op = 0; k_op < st->n_ops; k_op++) {
214
+ if (st->fsync[k_op] == '1' && ca_has_mask(st->cx[k_op])) {
215
+ ca_clear_mask(st->cx[k_op]);
216
+ }
217
+ }
218
+ }
219
+
150
220
  typedef struct {
151
221
  ca_sweep_state_t *st;
152
222
  int any_input_mask;
@@ -163,6 +233,8 @@ ca_sweep_acquire_body (VALUE arg)
163
233
  ca_sweep_state_t *st = ctx->st;
164
234
  int k_op;
165
235
 
236
+ if (!ctx->any_input_mask) ca_sweep_clear_output_masks(st);
237
+
166
238
  /* Per-operand acquire:
167
239
  * OUTPUT (fsync == '1') -> ca_attach + base = ca->ptr
168
240
  * INPUT alias -> ca_attach + base = ca->ptr
@@ -178,9 +250,7 @@ ca_sweep_acquire_body (VALUE arg)
178
250
  st->attached[k_op] = 1;
179
251
  st->base[k_op] = (char *) ca->ptr;
180
252
  } else {
181
- ca_size_t bytes_total = ca->elements * ca->bytes;
182
- st->owned_buf[k_op] = xmalloc(bytes_total);
183
- st->base[k_op] = st->owned_buf[k_op];
253
+ st->base[k_op] = ca_sweep_scratch_alloc(st, k_op);
184
254
  ca_xfer_all(ca, st->base[k_op], CA_XFER_GET);
185
255
  }
186
256
  }
@@ -209,7 +279,7 @@ ca_sweep_acquire_body (VALUE arg)
209
279
  }
210
280
 
211
281
  /* Overwrite each OUTPUT mask with the iter mask (creating the
212
- OUTPUT mask if it did not exist). No-op when m0 is NULL. */
282
+ OUTPUT mask if it did not exist). */
213
283
  if (st->m0) {
214
284
  for (k_op = 0; k_op < st->n_ops; k_op++) {
215
285
  CArray *ca = st->cx[k_op];
@@ -228,12 +298,7 @@ ca_sweep_acquire (ca_sweep_state_t *st)
228
298
  int k_op;
229
299
  int tag = 0;
230
300
 
231
- if ((int) strlen(st->fsync) != st->n_ops) {
232
- rb_raise(rb_eRuntimeError,
233
- "[BUG] invalid length of fsync arg in %s (expected %d)",
234
- st->src_label ? st->src_label : "ca_sweep_acquire",
235
- st->n_ops);
236
- }
301
+ ca_sweep_check_fsync(st, "ca_sweep_acquire");
237
302
 
238
303
  for (k_op = 0; k_op < st->n_ops; k_op++) {
239
304
  st->base[k_op] = NULL;
@@ -343,7 +408,7 @@ ca_sweep_release (ca_sweep_state_t *st)
343
408
  if (st->attached[k_op]) {
344
409
  ca_detach(st->cx[k_op]);
345
410
  } else if (st->owned_buf[k_op]) {
346
- xfree(st->owned_buf[k_op]);
411
+ ca_sweep_scratch_free(st->owned_buf[k_op]);
347
412
  st->owned_buf[k_op] = NULL;
348
413
  }
349
414
  }
@@ -363,6 +428,8 @@ ca_sweep_acquire_chunked_body (VALUE arg)
363
428
  ca_sweep_state_t *st = ctx->st;
364
429
  int k_op;
365
430
 
431
+ if (!ctx->any_input_mask) ca_sweep_clear_output_masks(st);
432
+
366
433
  /* per-operand acquire:
367
434
  * OUTPUT (fsync == '1') : ca_attach + base_orig = ca->ptr (legitimate)
368
435
  * INPUT alias : ca_attach + base_orig = ca->ptr (zero-copy)
@@ -378,9 +445,14 @@ ca_sweep_acquire_chunked_body (VALUE arg)
378
445
  st->attached[k_op] = 1;
379
446
  st->base_orig[k_op] = (char *) ca->ptr;
380
447
  } else {
381
- /* non-alias non-scalar INPUT: arena scratch sized for chunk_n_max */
382
- ca_size_t scratch_bytes = st->chunk_n_max * ca->bytes;
383
- st->owned_buf[k_op] = (char *) ca_lazy_arena_acquire(scratch_bytes);
448
+ /* non-alias non-scalar INPUT: arena scratch sized for chunk_n_max;
449
+ * the arena marks an object scratch while it is in use */
450
+ if (ca->data_type == CA_OBJECT) {
451
+ st->owned_buf[k_op] = (char *) ca_lazy_arena_acquire_object(st->chunk_n_max);
452
+ } else {
453
+ ca_size_t scratch_bytes = st->chunk_n_max * ca->bytes;
454
+ st->owned_buf[k_op] = (char *) ca_lazy_arena_acquire(scratch_bytes);
455
+ }
384
456
  /* base_orig stays NULL -- per-chunk gather lands the data in
385
457
  * owned_buf[k_op]; base[k_op] will be set to owned_buf[k_op] at
386
458
  * each chunk boundary. */
@@ -424,12 +496,7 @@ ca_sweep_acquire_chunked (ca_sweep_state_t *st)
424
496
  int tag = 0;
425
497
  CArray *shape_donor = NULL;
426
498
 
427
- if ((int) strlen(st->fsync) != st->n_ops) {
428
- rb_raise(rb_eRuntimeError,
429
- "[BUG] invalid length of fsync arg in %s (expected %d)",
430
- st->src_label ? st->src_label : "ca_sweep_acquire_chunked",
431
- st->n_ops);
432
- }
499
+ ca_sweep_check_fsync(st, "ca_sweep_acquire_chunked");
433
500
 
434
501
  for (k_op = 0; k_op < st->n_ops; k_op++) {
435
502
  st->base[k_op] = NULL;
@@ -2,10 +2,8 @@
2
2
  *
3
3
  * ca_sweep_engine.h -- sweep (xfer_all) author surface engine, shared helper
4
4
  *
5
- * Established in PROPOSAL_L0_AUTHOR_SURFACE L0.1 (2026-06-11). Extracts
6
- * the xfer_all-aware lifecycle template previously duplicated 7-times in
7
- * ext/carray_call_cfunc.c (= one body per arity 1..7) into a single shared
8
- * helper, also used by the chunked slab family (ca_call_cslab_*).
5
+ * The xfer_all-aware lifecycle shared by ext/carray_call_cfunc.c (one body
6
+ * per arity 1..7) and the chunked slab family (ca_call_cslab_*).
9
7
  *
10
8
  * Lifecycle template:
11
9
  * (1) per-operand acquire
@@ -14,7 +12,8 @@
14
12
  * INPUT (fsync=='0') non-alias -> xmalloc + ca_xfer_all(GET)
15
13
  * (2) operand pairing (scalar collapse; arrays must agree in shape)
16
14
  * (3) mask OR across INPUT operands -> m0 (xmalloc, NULL if none)
17
- * (4) mask propagate to OUTPUT operands (overwrite output->mask)
15
+ * (4) mask propagate to OUTPUT operands (overwrite output->mask; cleared
16
+ * when no INPUT is masked)
18
17
  * (5) caller runs the per-cell loop using base[] + stride[]
19
18
  * (6) per-operand release (reverse order):
20
19
  * sync OUTPUTs, detach attached or xfree owned_buf, xfree m0
@@ -64,7 +63,7 @@ typedef struct ca_sweep_state {
64
63
  ca_size_t n_kernel;
65
64
  const char *src_label;
66
65
 
67
- /* === chunked path fields (L0.1 chunk, PROPOSAL_L0_AUTHOR_SURFACE) === */
66
+ /* === chunked path fields === */
68
67
  /* Used by ca_sweep_acquire_chunked / ca_sweep_next_chunk / ca_sweep_release_chunked
69
68
  * (= the chunked slab family, ca_call_cslab_*). Unused by the whole-materialise path
70
69
  * (ca_sweep_acquire / ca_sweep_release) — cfunc and other whole-buffer callers
@@ -498,7 +498,7 @@ ca_csv_build_const_string_columns (VALUE text, ca_csv_walk_t *w)
498
498
  CArray *pe;
499
499
  int64_t *range;
500
500
  boolean8_t *m = NULL;
501
- VALUE col = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
501
+ volatile VALUE col = rb_carray_new(CA_FIXLEN, 1, dim, 2 * sizeof(int64_t), NULL);
502
502
  rb_ary_push(cols, col);
503
503
  TypedData_Get_Struct(col, CArray, &carray_data_type, pe);
504
504
  range = (int64_t *) pe->ptr;
data/ext/carray.h CHANGED
@@ -1201,7 +1201,9 @@ double OBJ2DBL (VALUE v);
1201
1201
  /* Mask-aware object->numeric parsers: return 1 with *out set on success, or
1202
1202
  0 (no value; caller masks the cell) on parse failure. Used by the
1203
1203
  object->int/float cast loop so an unparseable cell becomes UNDEF instead
1204
- of a silent 0.0 (float) or a raise (int). See ext/carray_cast.c. */
1204
+ of a silent 0.0 (float) or a raise (int). An Integer wider than the C
1205
+ type gives its low bytes (two's complement), as a narrowing C cast does;
1206
+ these parsers do not raise. See ext/carray_cast.c. */
1205
1207
  int ca_obj2dbl_ok (VALUE v, double *out);
1206
1208
  int ca_obj2cmplx_ok (VALUE v, double complex *out);
1207
1209
  int rb_obj2long_ok (VALUE v, long *out);
data/ext/carray_access.c CHANGED
@@ -39,15 +39,9 @@ rb_ca_store_index (VALUE self, ca_size_t *idx, VALUE rval)
39
39
  ca_create_mask(ca);
40
40
  ca_store_index(ca->mask, idx, &one);
41
41
  }
42
- else { /* unset mask and set value of the element at the index 'idx' */
42
+ else { /* set value of the element at the index 'idx' and unset its mask */
43
43
 
44
- /* unset mask */
45
- ca_update_mask(ca);
46
- if ( ca->mask ) {
47
- ca_store_index(ca->mask, idx, &zero);
48
- }
49
-
50
- /* store value */
44
+ /* A value that can not be stored leaves the cell masked. */
51
45
  {
52
46
  volatile VALUE holder;
53
47
  char *v = ALLOCV_N(char, holder, ca->bytes);
@@ -55,6 +49,11 @@ rb_ca_store_index (VALUE self, ca_size_t *idx, VALUE rval)
55
49
  ca_store_index(ca, idx, v);
56
50
  ALLOCV_END(holder);
57
51
  }
52
+
53
+ ca_update_mask(ca);
54
+ if ( ca->mask ) {
55
+ ca_store_index(ca->mask, idx, &zero);
56
+ }
58
57
  }
59
58
 
60
59
  return rval;
@@ -123,13 +122,9 @@ rb_ca_store_addr (VALUE self, ca_size_t addr, VALUE rval)
123
122
  ca_create_mask(ca);
124
123
  ca_store_addr(ca->mask, addr, &one);
125
124
  }
126
- else { /* set value at the element */
127
- ca_update_mask(ca);
128
- if ( ca->mask ) {
129
- ca_store_addr(ca->mask, addr, &zero); /* unset mask */
130
- }
125
+ else { /* set value at the element and unset its mask */
131
126
 
132
- /* store value */
127
+ /* A value that can not be stored leaves the cell masked. */
133
128
  {
134
129
  volatile VALUE holder;
135
130
  char *v = ALLOCV_N(char, holder, ca->bytes);
@@ -137,6 +132,11 @@ rb_ca_store_addr (VALUE self, ca_size_t addr, VALUE rval)
137
132
  ca_store_addr(ca, addr, v);
138
133
  ALLOCV_END(holder);
139
134
  }
135
+
136
+ ca_update_mask(ca);
137
+ if ( ca->mask ) {
138
+ ca_store_addr(ca->mask, addr, &zero);
139
+ }
140
140
  }
141
141
 
142
142
  return rval;
@@ -426,14 +426,38 @@ rb_ca_ref_address (VALUE self, CAIndexInfo *info)
426
426
  return rb_ca_fetch_addr(self, addr);
427
427
  }
428
428
 
429
+ /* What a store to one cell takes from an array: its one cell. A cell of an
430
+ object array holds any value, the array itself included, so only a
431
+ CScalar is opened there. */
432
+ static VALUE
433
+ ca_cell_of_store_value (VALUE self, VALUE rval)
434
+ {
435
+ CArray *ca, *cv;
436
+ if ( rb_obj_is_cscalar(rval) ) {
437
+ return rb_ca_fetch_addr(rval, 0);
438
+ }
439
+ if ( ! rb_obj_is_carray(rval) ) {
440
+ return rval;
441
+ }
442
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
443
+ if ( ca->data_type == CA_OBJECT ) {
444
+ return rval;
445
+ }
446
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
447
+ if ( cv->elements != 1 ) {
448
+ rb_raise(rb_eArgError,
449
+ "%s: shape mismatch writing to one cell (<- %" PRIi64 " cells)",
450
+ ca_calling_method_name(), (int64_t) cv->elements);
451
+ }
452
+ return rb_ca_fetch_addr(rval, 0);
453
+ }
454
+
429
455
  static VALUE
430
456
  rb_ca_store_address (VALUE self, CAIndexInfo *info, volatile VALUE rval)
431
457
  {
432
458
  ca_size_t addr;
433
459
  addr = info->index[0].scalar;
434
- if ( rb_obj_is_cscalar(rval) ) {
435
- rval = rb_ca_fetch_addr(rval, 0);
436
- }
460
+ rval = ca_cell_of_store_value(self, rval);
437
461
  rb_ca_store_addr(self, addr, rval);
438
462
  return rval;
439
463
  }
@@ -461,9 +485,7 @@ rb_ca_store_point (VALUE self, CAIndexInfo *info, volatile VALUE val)
461
485
  for (i=0; i<ca->ndim; i++) {
462
486
  idx[i] = info->index[i].scalar;
463
487
  }
464
- if ( rb_obj_is_cscalar(val) ) {
465
- val = rb_ca_fetch_addr(val, 0);
466
- }
488
+ val = ca_cell_of_store_value(self, val);
467
489
  rb_ca_store_index(self, idx, val);
468
490
  return val;
469
491
  }
@@ -522,8 +544,8 @@ ca_store_all_cast_body (VALUE arg)
522
544
  memset(ca->mask->ptr, 0, ca->elements);
523
545
  }
524
546
  if ( ca->mask ) {
525
- ca_cast_block_with_mask(ca->elements, c->cv, c->scratch, ca, ca->ptr,
526
- (boolean8_t*)ca->mask->ptr);
547
+ ca_cast_block_skip_masked(ca->elements, c->cv, c->scratch, ca, ca->ptr,
548
+ (boolean8_t*)ca->mask->ptr);
527
549
  }
528
550
  else {
529
551
  ca_cast_block(ca->elements, c->cv, c->scratch, ca, ca->ptr);
@@ -567,9 +589,9 @@ ca_store_all_list_body (VALUE arg)
567
589
  }
568
590
  m++;
569
591
  }
570
- ca_cast_block_with_mask(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list),
571
- cast_target, ca->ptr,
572
- (boolean8_t*)ca->mask->ptr);
592
+ ca_cast_block_skip_masked(ca->elements, c->ico, (VALUE *)RARRAY_CONST_PTR(list),
593
+ cast_target, ca->ptr,
594
+ (boolean8_t*)ca->mask->ptr);
573
595
  }
574
596
  else {
575
597
  /* Every cell gets a value, so none stays masked. */
@@ -617,8 +639,8 @@ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
617
639
  rb_ca_data_class(rval), rb_ca_data_class(self));
618
640
  }
619
641
  rb_raise(rb_eTypeError,
620
- "can not store a %s array into a record array of %"PRIsVALUE,
621
- rb_obj_classname(rval), rb_ca_data_class(self));
642
+ "can not store a %"PRIsVALUE" array into a record array of %"PRIsVALUE,
643
+ rb_obj_class(rval), rb_ca_data_class(self));
622
644
  }
623
645
  if ( ca_is_face(ca) ) {
624
646
  if ( rb_obj_class(self) == rb_obj_class(rval)
@@ -637,9 +659,9 @@ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
637
659
  return rval;
638
660
  }
639
661
  rb_raise(rb_eTypeError,
640
- "can not store a %s array into %s array "
662
+ "can not store a %"PRIsVALUE" array into %s array "
641
663
  "(to_type(:object) gives its values)",
642
- rb_obj_classname(rval), ca_type_name[ca->data_type]);
664
+ rb_obj_class(rval), ca_type_name[ca->data_type]);
643
665
  }
644
666
  return rb_funcall(rval, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
645
667
  }
@@ -647,6 +669,20 @@ ca_face_source_for_store (VALUE self, CArray *ca, VALUE rval)
647
669
  /* A store into no cells writes nothing, but the value is checked as it
648
670
  would be for any cells: the same statement does not raise or pass
649
671
  depending on how many cells the index picks. */
672
+ /* Writing complex cells into real ones would drop the imaginary parts
673
+ without a word, where a Complex scalar is refused. */
674
+ void
675
+ ca_refuse_complex_into_real (CArray *ca, CArray *cv)
676
+ {
677
+ if ( ca_is_complex_type(cv) && ! ca_is_complex_type(ca)
678
+ && ca->data_type != CA_OBJECT ) {
679
+ rb_raise(rb_eCADataTypeError,
680
+ "%s: cannot store %s cells in %s (#real and #to_type take the part wanted)",
681
+ ca_calling_method_name(), ca_type_name[cv->data_type],
682
+ ca_type_name[ca->data_type]);
683
+ }
684
+ }
685
+
650
686
  static void
651
687
  ca_store_all_check_empty (VALUE self, VALUE rval)
652
688
  {
@@ -661,7 +697,10 @@ ca_store_all_check_empty (VALUE self, VALUE rval)
661
697
  rval = rb_funcall(rval, id_to_ca, 0);
662
698
  }
663
699
  if ( rb_obj_is_carray(rval) ) {
700
+ CArray *cv;
664
701
  ca_broadcast_to_destination(self, (volatile VALUE *) &rval);
702
+ TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
703
+ ca_refuse_complex_into_real(ca, cv);
665
704
  }
666
705
  else if ( TYPE(rval) == T_ARRAY ) {
667
706
  if ( NIL_P(rb_ary_flatten_for_elements(rval, ca->elements, ca)) ) {
@@ -733,6 +772,7 @@ rb_ca_store_all (VALUE self, VALUE rval)
733
772
  /* The destination owns the shape; see ca_broadcast_to_destination. */
734
773
  ca_broadcast_to_destination(self, &rval);
735
774
  TypedData_Get_Struct(rval, CArray, &carray_data_type, cv);
775
+ ca_refuse_complex_into_real(ca, cv);
736
776
 
737
777
  /* Fixlen cells of another width are fitted cell by cell, padded with
738
778
  NUL or cut to the destination's width, as a String or an Array of
@@ -1053,7 +1093,7 @@ extern VALUE rb_ca_remap_new (VALUE cary, VALUE rmapper);
1053
1093
  reads as masked (a CAGrid hole); the same-shape fast path is left to a
1054
1094
  mapper with no masked cell. */
1055
1095
  static VALUE
1056
- rb_ca_fancy_index_chain (VALUE self, VALUE rmapper)
1096
+ rb_ca_fancy_index_chain (VALUE self, volatile VALUE rmapper)
1057
1097
  {
1058
1098
  CArray *ca, *cm;
1059
1099
  volatile VALUE flat_self, flat_mapper, gridded, result;
@@ -1144,7 +1184,7 @@ ca_argv_is_method_call (int argc, VALUE *argv)
1144
1184
  {
1145
1185
  const char *name;
1146
1186
  if ( argc < 1 || ! SYMBOL_P(argv[0]) ) return 0;
1147
- name = rb_id2name(SYM2ID(argv[0]));
1187
+ name = rb_id2name(SYM2ID(argv[0])); /* name: read at once by strlen */
1148
1188
  return ( name != NULL && strlen(name) > 1 );
1149
1189
  }
1150
1190
 
@@ -1660,14 +1700,14 @@ ca_face_convert_store_rval (VALUE self, CArray *ca, VALUE val)
1660
1700
  const char *method = ca_face_storage_method(self);
1661
1701
  if ( method ) {
1662
1702
  rb_raise(rb_eTypeError,
1663
- "%s cannot store a bare %s array as raw storage "
1703
+ "%"PRIsVALUE" cannot store a bare %s array as raw storage "
1664
1704
  "(write the %s storage, which #%s gives, directly)",
1665
- rb_obj_classname(self), ca_type_name[cval->data_type],
1705
+ rb_obj_class(self), ca_type_name[cval->data_type],
1666
1706
  ca_type_name[storage->data_type], method);
1667
1707
  }
1668
1708
  rb_raise(rb_eTypeError,
1669
- "%s cannot store a bare %s array as raw storage",
1670
- rb_obj_classname(self), ca_type_name[cval->data_type]);
1709
+ "%"PRIsVALUE" cannot store a bare %s array as raw storage",
1710
+ rb_obj_class(self), ca_type_name[cval->data_type]);
1671
1711
  }
1672
1712
  }
1673
1713
  return val;
@@ -1888,7 +1928,7 @@ rb_ca_store2 (VALUE self, int n, VALUE *rindex, VALUE rval)
1888
1928
  }
1889
1929
 
1890
1930
  static VALUE
1891
- rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1931
+ rb_ca_s_scan_index (VALUE self, VALUE rdim, volatile VALUE ridx)
1892
1932
  {
1893
1933
  volatile VALUE rtype, rindex;
1894
1934
  CAIndexInfo info;
@@ -1899,11 +1939,13 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1899
1939
 
1900
1940
  Check_Type(rdim, T_ARRAY);
1901
1941
  Check_Type(ridx, T_ARRAY);
1942
+ /* Converting an entry can run Ruby that changes the Array: read a copy. */
1943
+ ridx = rb_ary_dup(ridx);
1902
1944
 
1903
1945
  ndim = (int) RARRAY_LEN(rdim);
1904
1946
  CA_CHECK_RANK_GIVEN(ndim);
1905
1947
  for (i=0; i<ndim; i++) {
1906
- dim[i] = NUM2SIZE(rb_ary_entry(rdim, i));
1948
+ dim[i] = ca_integer_arg(rb_ary_entry(rdim, i), "a dimension", NULL);
1907
1949
  }
1908
1950
  CA_CHECK_DIM_GIVEN(ndim, dim);
1909
1951
  elements = ca_shape_elements(ndim, dim, 1);
@@ -1988,7 +2030,7 @@ rb_ca_s_scan_index (VALUE self, VALUE rdim, VALUE ridx)
1988
2030
  }
1989
2031
 
1990
2032
  static VALUE
1991
- rb_ca_normalize_index (VALUE self, VALUE ridx)
2033
+ rb_ca_normalize_index (VALUE self, volatile VALUE ridx)
1992
2034
  {
1993
2035
  volatile VALUE rindex;
1994
2036
  CArray *ca;
@@ -1997,6 +2039,8 @@ rb_ca_normalize_index (VALUE self, VALUE ridx)
1997
2039
 
1998
2040
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
1999
2041
  Check_Type(ridx, T_ARRAY);
2042
+ /* Converting an entry can run Ruby that changes the Array: read a copy. */
2043
+ ridx = rb_ary_dup(ridx);
2000
2044
 
2001
2045
  info.range_check = 1;
2002
2046
  rb_ca_scan_index(ca->ndim, ca->dim, ca->elements,
@@ -2080,7 +2124,7 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
2080
2124
  }
2081
2125
  ca_check_rank_fits(ndim, NULL);
2082
2126
  for (i = 0; i < ndim; i++) {
2083
- dim[i] = NUM2SIZE(rb_ary_entry(rshape, i));
2127
+ dim[i] = ca_integer_arg(rb_ary_entry(rshape, i), "a dimension", NULL);
2084
2128
  if ( dim[i] < 0 ) {
2085
2129
  rb_raise(rb_eArgError, "negative dim %" PRId64 " at axis %d",
2086
2130
  (ca_size_t) dim[i], i);
@@ -2091,12 +2135,21 @@ parse_shape_kwarg (VALUE rshape, int *out_ndim, ca_size_t *dim,
2091
2135
  *out_elements = elements;
2092
2136
  }
2093
2137
 
2138
+ /* A scalar key of index2addr / addr2index, by the rule of `[]`: a Float that
2139
+ is a whole number is that position, any other is no position at all. */
2140
+ static VALUE
2141
+ ca_scalar_position_key (VALUE v)
2142
+ {
2143
+ return RB_FLOAT_TYPE_P(v) ? SIZE2NUM(ca_index_key_value(v)) : v;
2144
+ }
2145
+
2094
2146
  static VALUE
2095
2147
  addr2index_do (int ndim, ca_size_t *dim, ca_size_t elements, VALUE raddr)
2096
2148
  {
2097
2149
  volatile VALUE out;
2098
2150
  int i;
2099
2151
 
2152
+ raddr = ca_scalar_position_key(raddr);
2100
2153
  /* Scalar path (Integer input): returns Ruby Array of N Integers.
2101
2154
  * Preserves legacy shape for callers like `i, j = ca.addr2index(k)`. */
2102
2155
  if ( rb_obj_is_kind_of(raddr, rb_cInteger) ) {
@@ -2253,12 +2306,18 @@ index2addr_do (int ndim, ca_size_t *dim, int argc, VALUE *argv)
2253
2306
  ca_size_t out_dim[CA_RANK_MAX];
2254
2307
  ca_size_t out_elements = 1;
2255
2308
  int shape_from = -1;
2309
+ VALUE keys[CA_RANK_MAX];
2256
2310
 
2257
2311
  if ( argc != ndim ) {
2258
2312
  rb_raise(rb_eArgError,
2259
2313
  "wrong number of indices (%d for %d)", argc, ndim);
2260
2314
  }
2261
2315
 
2316
+ for (i = 0; i < ndim; i++) {
2317
+ keys[i] = ca_scalar_position_key(argv[i]);
2318
+ }
2319
+ argv = keys;
2320
+
2262
2321
  for (i = 0; i < ndim; i++) {
2263
2322
  if ( ! rb_obj_is_kind_of(argv[i], rb_cInteger) ) {
2264
2323
  all_number = 0;