carray 3.0.4 → 3.0.5

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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +205 -0
  3. data/carray.gemspec +1 -1
  4. data/ext/ca_array_pool.c +25 -1
  5. data/ext/ca_axis_dispatch.c +13 -20
  6. data/ext/ca_categorical_iterator.c +27 -20
  7. data/ext/ca_compare.c +4 -4
  8. data/ext/ca_composite_dispatch.c +9 -9
  9. data/ext/ca_kernel_iterator.c +6 -6
  10. data/ext/ca_kernel_iterator.h +112 -83
  11. data/ext/ca_obj_array.c +69 -19
  12. data/ext/ca_obj_bincmp.c +1 -0
  13. data/ext/ca_obj_binop.c +1 -0
  14. data/ext/ca_obj_bitarray.c +5 -1
  15. data/ext/ca_obj_bitfield.c +58 -43
  16. data/ext/ca_obj_byte_swap.c +2 -1
  17. data/ext/ca_obj_const_string.c +15 -3
  18. data/ext/ca_obj_face.c +42 -1
  19. data/ext/ca_obj_field.c +13 -3
  20. data/ext/ca_obj_grid.c +52 -5
  21. data/ext/ca_obj_monop.c +46 -10
  22. data/ext/ca_obj_object.c +64 -12
  23. data/ext/ca_obj_record.c +3 -0
  24. data/ext/ca_obj_reduce.c +4 -0
  25. data/ext/ca_obj_refer.c +18 -9
  26. data/ext/ca_obj_roll.c +26 -6
  27. data/ext/ca_obj_select.c +34 -5
  28. data/ext/ca_obj_shift.c +31 -7
  29. data/ext/ca_obj_stack.c +1 -0
  30. data/ext/ca_obj_stride.c +28 -19
  31. data/ext/ca_obj_string.c +11 -2
  32. data/ext/ca_obj_tile.c +27 -7
  33. data/ext/ca_obj_transpose.c +8 -4
  34. data/ext/ca_obj_triop.c +3 -0
  35. data/ext/ca_obj_window.c +79 -50
  36. data/ext/ca_op_ipower.c +4 -4
  37. data/ext/ca_sort_kernels.h +6 -3
  38. data/ext/carray.h +33 -0
  39. data/ext/carray_access.c +16 -3
  40. data/ext/carray_attribute.c +24 -0
  41. data/ext/carray_cast.c +243 -82
  42. data/ext/carray_cast_func.rb +56 -26
  43. data/ext/carray_conversion.c +2 -2
  44. data/ext/carray_core.c +3 -0
  45. data/ext/carray_count.c +6 -0
  46. data/ext/carray_element.c +9 -4
  47. data/ext/carray_factorize.c +20 -20
  48. data/ext/carray_generate.c +8 -2
  49. data/ext/carray_histogram.c +1 -1
  50. data/ext/carray_index_classifier.c +4 -4
  51. data/ext/carray_internal.h +12 -0
  52. data/ext/carray_kernels_bincmp.c +168 -168
  53. data/ext/carray_kernels_binop.c +900 -900
  54. data/ext/carray_kernels_init.c +4 -4
  55. data/ext/carray_kernels_moncmp.c +96 -96
  56. data/ext/carray_kernels_monop.c +1984 -1984
  57. data/ext/carray_kernels_reduce_aggregate.c +1733 -1733
  58. data/ext/carray_kernels_reduce_cumulative.c +642 -642
  59. data/ext/carray_kernels_reduce_extreme.c +1150 -1150
  60. data/ext/carray_kernels_reduce_variance.c +258 -258
  61. data/ext/carray_kernels_scan.c +128 -128
  62. data/ext/carray_kernels_search.c +340 -340
  63. data/ext/carray_kernels_sort.c +295 -304
  64. data/ext/carray_kernels_triop.c +114 -114
  65. data/ext/carray_key_windows.c +4 -0
  66. data/ext/carray_mask.c +45 -22
  67. data/ext/carray_memory_view.c +2 -2
  68. data/ext/carray_operator.c +5 -5
  69. data/ext/carray_order.c +227 -8
  70. data/ext/carray_random.c +18 -18
  71. data/ext/carray_scatter.c +24 -7
  72. data/ext/carray_slab.c +32 -9
  73. data/ext/carray_sort.c +224 -16
  74. data/ext/carray_sort_kernel.c +5 -7
  75. data/ext/carray_struct.c +35 -43
  76. data/ext/carray_time_text.c +4 -2
  77. data/ext/carray_utils.c +63 -21
  78. data/ext/mkkernel.rb +210 -115
  79. data/ext/version.h +4 -4
  80. data/lib/carray/attribute.rb +17 -4
  81. data/lib/carray/basics.rb +27 -7
  82. data/lib/carray/categorical.rb +30 -16
  83. data/lib/carray/categorical_iterator.rb +6 -6
  84. data/lib/carray/const_string.rb +10 -0
  85. data/lib/carray/data_type_extension.rb +8 -1
  86. data/lib/carray/fixlen_string.rb +41 -2
  87. data/lib/carray/frame/column_verbs.rb +4 -3
  88. data/lib/carray/frame/concat.rb +17 -3
  89. data/lib/carray/frame/frame.rb +6 -7
  90. data/lib/carray/frame/group.rb +100 -13
  91. data/lib/carray/frame/io.rb +3 -1
  92. data/lib/carray/fuse_source.rb +31 -2
  93. data/lib/carray/lazy.rb +35 -0
  94. data/lib/carray/methods/is_in.rb +8 -1
  95. data/lib/carray/segment_iterator.rb +12 -8
  96. data/lib/carray/serialize.rb +19 -2
  97. data/lib/carray/stack.rb +15 -12
  98. data/lib/carray/string.rb +8 -0
  99. data/lib/carray/string_operation_extension.rb +13 -5
  100. data/lib/carray/struct_builder.rb +5 -3
  101. data/lib/carray/time.rb +107 -68
  102. data/yard-stubs/ca_obj_transpose.rb +5 -4
  103. data/yard-stubs/carray_attribute.rb +3 -1
  104. data/yard-stubs/carray_cast.rb +2 -0
  105. data/yard-stubs/carray_conversion.rb +5 -1
  106. data/yard-stubs/carray_element.rb +7 -6
  107. data/yard-stubs/carray_mask.rb +8 -3
  108. data/yard-stubs/carray_math.rb +14 -4
  109. data/yard-stubs/carray_order.rb +100 -4
  110. data/yard-stubs/carray_scatter.rb +7 -5
  111. metadata +1 -1
data/ext/ca_obj_monop.c CHANGED
@@ -370,12 +370,19 @@ apply_monop_in_place (uint16_t op_id, int8_t cur_dt,
370
370
  fn(slab_n, mask, (char *) data, 1, (char *) data, 1);
371
371
  }
372
372
 
373
- /* Minimal CArray-shaped stub used as ca_cast_block's data_type-only carrier.
374
- ca_cast_block only reads data_type to index into ca_cast_func_table. */
375
- typedef struct {
376
- int16_t obj_type;
377
- int8_t data_type;
378
- } ca_data_type_stub_t;
373
+ /* A zeroed CArray carrying only data_type and bytes, handed to
374
+ ca_cast_block in place of a real array. It is a whole CArray, not a
375
+ shorter struct cast to one: ca_cast_block reads data_type through a
376
+ CArray pointer, and a fixlen cast reads bytes as well. */
377
+ static CArray
378
+ ca_data_type_stub (int8_t data_type)
379
+ {
380
+ CArray stub;
381
+ memset(&stub, 0, sizeof(stub));
382
+ stub.data_type = data_type;
383
+ stub.bytes = ca_sizeof[data_type];
384
+ return stub;
385
+ }
379
386
 
380
387
  /* Pull leaf into the output buffer. Three paths:
381
388
 
@@ -428,8 +435,8 @@ pull_leaf_with_optional_cast (CArray *leaf, uint16_t innermost_op,
428
435
  if ( has_cast_at_leaf ) {
429
436
  /* Path 2: cast scratch → data with output layout in target data_type. */
430
437
  int8_t target_dt = (int8_t)(innermost_op - CA_MONOP_CAST_BASE);
431
- ca_data_type_stub_t src_stub = { 0, leaf->data_type };
432
- ca_data_type_stub_t dst_stub = { 0, target_dt };
438
+ CArray src_stub = ca_data_type_stub(leaf->data_type);
439
+ CArray dst_stub = ca_data_type_stub(target_dt);
433
440
  ca_cast_block(slab_n, &src_stub, scratch, &dst_stub, data);
434
441
  result_dt = target_dt;
435
442
  } else {
@@ -555,6 +562,30 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
555
562
  chain_apply_count = chain_len - 1;
556
563
  }
557
564
 
565
+ /* The chain runs in place, so the buffer must hold slab_n cells of the
566
+ widest type along it: the leaf, or a step that widens before a later
567
+ one narrows (cmplx128.lazy.abs pulls 16-byte cells and answers 8-byte
568
+ ones). When that is wider than the output, run in a scratch and copy
569
+ the answer into `data` at the end. */
570
+ void *out = data;
571
+ void *work = NULL;
572
+ {
573
+ ca_size_t max_bytes = leaf->bytes;
574
+ int8_t dt = leaf->data_type;
575
+ for ( i = chain_len - 1; i >= 0; i-- ) {
576
+ dt = ca_monop_is_cast(op_chain[i])
577
+ ? (int8_t)(op_chain[i] - CA_MONOP_CAST_BASE)
578
+ : ca_lazy_promote_monop(op_chain[i], dt);
579
+ if ( ca_sizeof[dt] > max_bytes ) max_bytes = ca_sizeof[dt];
580
+ }
581
+ if ( max_bytes > mo->bytes ) {
582
+ work = ( leaf->data_type == CA_OBJECT )
583
+ ? ca_lazy_arena_acquire_object((slab_n * max_bytes + sizeof(VALUE) - 1) / sizeof(VALUE))
584
+ : ca_lazy_arena_acquire(slab_n * max_bytes);
585
+ data = work;
586
+ }
587
+ }
588
+
558
589
  /* === 2. pull leaf into output buffer (with optional cast) === */
559
590
  cur_dt = pull_leaf_with_optional_cast(
560
591
  leaf,
@@ -576,8 +607,8 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
576
607
  ca_size_t src_bytes = ca_sizeof[cur_dt];
577
608
  ca_size_t total_bytes = slab_n * src_bytes;
578
609
  void *scratch;
579
- ca_data_type_stub_t src_stub = { 0, cur_dt };
580
- ca_data_type_stub_t dst_stub = { 0, target_dt };
610
+ CArray src_stub = ca_data_type_stub(cur_dt);
611
+ CArray dst_stub = ca_data_type_stub(target_dt);
581
612
 
582
613
  /* Copy current data → scratch (in src data_type), then cast scratch
583
614
  → data (in target data_type). Required because in-place cast
@@ -604,6 +635,10 @@ ca_monop_eval (CAMonOp *mo, const ca_lazy_req_t *req, void *data, int dir)
604
635
  if ( slab_mask ) {
605
636
  ca_lazy_arena_release(slab_mask);
606
637
  }
638
+ if ( work ) {
639
+ memcpy(out, work, slab_n * mo->bytes);
640
+ ca_lazy_arena_release(work);
641
+ }
607
642
  }
608
643
 
609
644
  static void
@@ -1116,6 +1151,7 @@ Init_ca_obj_monop (void)
1116
1151
  rb_define_const(rb_cCAMonOp, "OP_RAD_PI", INT2NUM(CA_MONOP_RAD_PI));
1117
1152
  rb_define_const(rb_cCAMonOp, "OP_SIGN", INT2NUM(CA_MONOP_SIGN));
1118
1153
  rb_define_const(rb_cCAMonOp, "OP_ARG_I", INT2NUM(CA_MONOP_ARG_I));
1154
+ rb_define_const(rb_cCAMonOp, "OP_BYTE_SWAP", INT2NUM(CA_MONOP_BYTE_SWAP));
1119
1155
  rb_define_const(rb_cCAMonOp, "CAST_BASE", INT2NUM(CA_MONOP_CAST_BASE));
1120
1156
 
1121
1157
  rb_define_alloc_func(rb_cCAMonOp, rb_ca_monop_s_allocate);
data/ext/ca_obj_object.c CHANGED
@@ -171,18 +171,66 @@ ca_object_empty_wrap (CArray *w)
171
171
  }
172
172
  }
173
173
 
174
+ /* Moves the cells of a wrap (or of its lent mask, `w`) into an array of
175
+ their own, held by `holder` under `name`, and points `w` at them. The
176
+ cells are those the hook left in the caller's buffer. */
177
+ static void
178
+ ca_object_keep_cells (VALUE holder, const char *name, CArray *w)
179
+ {
180
+ volatile VALUE cells;
181
+ CArray *c;
182
+ cells = rb_carray_new(w->data_type, w->ndim, w->dim, w->bytes, NULL);
183
+ c = (CArray *) DATA_PTR(cells);
184
+ memcpy(c->ptr, w->ptr, (size_t) w->elements * w->bytes);
185
+ rb_ivar_set(holder, rb_intern(name), cells);
186
+ w->ptr = c->ptr;
187
+ }
188
+
189
+ /* Takes the caller's buffer back from a wrap the hook was lent, together
190
+ with `lent_mask`, the wrap of the data's mask bits it was lent with (or
191
+ NULL). When the hook took a view of either, the view outlives the call,
192
+ so both keep their cells; otherwise both are left with none. A mask the
193
+ hook made on the wrap is the wrap's own, with cells of its own. */
194
+ static void
195
+ ca_object_take_back (VALUE wrap, CArray *lent_mask)
196
+ {
197
+ CArray *w = (CArray *) DATA_PTR(wrap);
198
+ CArray *m = ( lent_mask != NULL && w->mask == lent_mask ) ? lent_mask : NULL;
199
+ int viewed = ca_test_flag(w, CA_FLAG_LENT_VIEWED) ||
200
+ ( m != NULL && ca_test_flag(m, CA_FLAG_LENT_VIEWED) );
201
+ ca_unset_flag(w, CA_FLAG_LENT | CA_FLAG_LENT_VIEWED);
202
+ if ( lent_mask != NULL ) {
203
+ ca_unset_flag(lent_mask, CA_FLAG_LENT | CA_FLAG_LENT_VIEWED);
204
+ }
205
+ if ( viewed ) {
206
+ ca_object_keep_cells(wrap, "kept_cells", w);
207
+ if ( m != NULL ) {
208
+ ca_object_keep_cells(wrap, "kept_mask_cells", m);
209
+ }
210
+ }
211
+ else {
212
+ if ( m != NULL ) {
213
+ ca_object_empty_wrap(m);
214
+ }
215
+ ca_object_empty_wrap(w);
216
+ }
217
+ }
218
+
174
219
  /* Calls copy_data / sync_data with a wrap of a caller's buffer, shaped as
175
- the object, and takes the buffer back however the call leaves. The wrap
176
- shares the object's mask (the data's), and whatever mask it holds after
177
- the call becomes the data's: a hook that writes masked values masks the
178
- object's cells. The data points at the buffer for the duration too, for
179
- a hook that reads the object back while it runs. */
220
+ the object, and takes the buffer back however the call leaves. When the
221
+ data has a mask, the wrap's mask is a wrap of its bits: a hook that writes
222
+ masked values masks the object's cells, and the data keeps its mask
223
+ whatever the hook does with the wrap's. A mask the hook makes on a wrap
224
+ that had none is the wrap's own and goes with it. The data points at the
225
+ buffer for the duration too, for a hook that reads the object back while
226
+ it runs. */
180
227
 
181
228
  typedef struct {
182
229
  CAObject *ca;
183
230
  char *saved;
184
231
  ID mid;
185
232
  VALUE wrap;
233
+ CArray *mask; /* the wrap of the data's mask bits, or NULL */
186
234
  } ca_object_lend_t;
187
235
 
188
236
  static VALUE
@@ -196,11 +244,8 @@ static VALUE
196
244
  ca_object_lend_restore (VALUE varg)
197
245
  {
198
246
  ca_object_lend_t *l = (ca_object_lend_t *) varg;
199
- CArray *w = (CArray *) DATA_PTR(l->wrap);
200
- l->ca->data->mask = w->mask;
201
- w->mask = NULL;
202
- ca_object_empty_wrap(w);
203
247
  l->ca->data->ptr = l->saved;
248
+ ca_object_take_back(l->wrap, l->mask);
204
249
  return Qnil;
205
250
  }
206
251
 
@@ -213,7 +258,14 @@ ca_object_call_lending (CAObject *ca, void *ptr, const char *mid)
213
258
  w = ca_wrap_new(data->data_type, data->ndim, data->dim, data->bytes,
214
259
  NULL, (char *) ptr);
215
260
  l.wrap = ca_wrap_struct(w);
216
- w->mask = data->mask;
261
+ l.mask = NULL;
262
+ if ( data->mask != NULL ) {
263
+ l.mask = (CArray *) ca_wrap_new(CA_BOOLEAN, data->ndim, data->dim, 0,
264
+ NULL, data->mask->ptr);
265
+ ca_set_flag(l.mask, CA_FLAG_LENT);
266
+ w->mask = l.mask;
267
+ }
268
+ ca_set_flag(w, CA_FLAG_LENT);
217
269
  l.ca = ca;
218
270
  l.saved = data->ptr;
219
271
  l.mid = rb_intern(mid);
@@ -253,7 +305,7 @@ ca_object_hook_release (VALUE varg)
253
305
  ca_object_hook_t *h = (ca_object_hook_t *) varg;
254
306
  int i;
255
307
  for ( i = 0; i < h->ntrans; i++ ) {
256
- ca_object_empty_wrap((CArray *) DATA_PTR(h->trans[i]));
308
+ ca_object_take_back(h->trans[i], NULL);
257
309
  }
258
310
  return Qnil;
259
311
  }
@@ -742,7 +794,6 @@ ca_object_setup (CAObject *ca,
742
794
  int8_t data_type, int8_t ndim, ca_size_t *dim, ca_size_t bytes)
743
795
  {
744
796
  ca_size_t elements;
745
- int8_t i;
746
797
 
747
798
  /* check arguments */
748
799
 
@@ -1194,6 +1245,7 @@ ca_object_wrap_transient (int8_t data_type, ca_size_t bytes,
1194
1245
  }
1195
1246
 
1196
1247
  wrap = ca_wrap_new(data_type, ndim, dim, bytes, NULL, (char *) data);
1248
+ ca_set_flag(wrap, CA_FLAG_LENT);
1197
1249
  return ca_wrap_struct(wrap);
1198
1250
  }
1199
1251
 
data/ext/ca_obj_record.c CHANGED
@@ -309,6 +309,9 @@ rb_ca_record_s_wrap (int argc, VALUE *argv, VALUE klass)
309
309
  rb_scan_args(argc, argv, "2", &entity_val, &data_class);
310
310
  }
311
311
  rb_check_carray_object(entity_val);
312
+ /* A record array (or another Face) is wrapped from its storage, so the
313
+ new record is not a Face over a Face. */
314
+ entity_val = rb_ca_strip_face_value(entity_val);
312
315
  return ca_record_build(klass, data_class, entity_val);
313
316
  }
314
317
 
data/ext/ca_obj_reduce.c CHANGED
@@ -56,6 +56,10 @@ ca_reduce_setup (CAReduce *ca, CArray *parent, ca_size_t count, ca_size_t offset
56
56
  "[BUG] CAReduce can't inherit other than boolean array");
57
57
  }
58
58
 
59
+ if ( count <= 0 ) {
60
+ rb_raise(rb_eArgError, "CAReduce: count should be positive");
61
+ }
62
+
59
63
  /* ca == NULL asks for the checks above only, so that a constructor can
60
64
  run them before it allocates. */
61
65
  if ( ca == NULL ) {
data/ext/ca_obj_refer.c CHANGED
@@ -145,7 +145,9 @@ ca_refer_setup (CARefer *ca, CArray *parent,
145
145
  ca_stride_setup_view((CAStride *) ca, CA_OBJ_REFER, parent,
146
146
  data_type, bytes, ndim, dim, strides, base_offset);
147
147
 
148
- if (ca_is_scalar(parent)) {
148
+ /* A refer of a scalar stays one only while it has the one cell: read as
149
+ narrower elements it is an array of several. */
150
+ if (ca_is_scalar(parent) && ca->elements == 1) {
149
151
  ca_set_flag(ca, CA_FLAG_SCALAR);
150
152
  }
151
153
  return 0;
@@ -211,16 +213,20 @@ ca_refer_func_create_mask (void *ap)
211
213
  else if (ca->bytes < parent_bytes) {
212
214
  /* Divided reinterpret: one parent element splits into
213
215
  ratio = parent_bytes / bytes view elements. Build a
214
- CARepeat that broadcasts each parent mask bit across the
215
- ratio sub-positions, then refer-reshape it to the view's
216
- actual shape. */
216
+ repeat of two axes, (parent elements, ratio), that broadcasts
217
+ each parent mask bit across the ratio sub-positions, then
218
+ refer-reshape it to the view's actual shape. Two axes whatever
219
+ the parent's rank, so a parent at CA_RANK_MAX needs no extra
220
+ one. */
217
221
  ca_size_t ratio = parent_bytes / ca->bytes;
218
- ca_size_t count[CA_RANK_MAX];
219
- int8_t i;
220
- for (i = 0; i < ca->parent->ndim; i++) count[i] = 0;
221
- count[ca->parent->ndim] = ratio;
222
+ ca_size_t mdim[2], mstrides[2];
223
+ mdim[0] = ca->parent->elements;
224
+ mdim[1] = ratio;
225
+ mstrides[0] = 1;
226
+ mstrides[1] = 0;
222
227
  ca->mask0 =
223
- (CArray *) ca_repeat_new(ca->parent->mask, ca->parent->ndim + 1, count);
228
+ (CArray *) ca_stride_new(CA_OBJ_REPEAT, ca->parent->mask, CA_BOOLEAN, 1,
229
+ 2, mdim, mstrides, 0);
224
230
  ca_unset_flag(ca->mask0, CA_FLAG_READ_ONLY);
225
231
  /* mask0 has ratio cells per parent element, so the offset is counted
226
232
  in those. */
@@ -619,6 +625,9 @@ rb_ca_reshape (int argc, VALUE *argv, VALUE self)
619
625
 
620
626
  TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
621
627
 
628
+ if ( argc == 0 ) {
629
+ rb_raise(rb_eArgError, "reshape: give at least one axis");
630
+ }
622
631
  ca_check_rank_fits(argc, "reshape");
623
632
 
624
633
  /* Locate the (at most one) auto-infer placeholder. `-1` is the
data/ext/ca_obj_roll.c CHANGED
@@ -112,6 +112,8 @@ ca_roll_setup (CARoll *ca, CArray *parent, ca_size_t *shift)
112
112
  ca->parent = parent;
113
113
  ca->attach = 0;
114
114
  ca->nosync = 0;
115
+ ca->reps = NULL; /* set before either allocation can raise */
116
+ ca->dim = NULL;
115
117
  ca->reps = ALLOC_N(ca_size_t, ndim); /* repurposed as shift[] */
116
118
  ca->dim = ALLOC_N(ca_size_t, ndim);
117
119
 
@@ -130,15 +132,33 @@ ca_roll_setup (CARoll *ca, CArray *parent, ca_size_t *shift)
130
132
  return 0;
131
133
  }
132
134
 
135
+ typedef struct {
136
+ CARoll *ca;
137
+ CArray *parent;
138
+ ca_size_t *shift;
139
+ } ca_roll_new_arg;
140
+
141
+ static VALUE
142
+ ca_roll_new_setup (VALUE varg)
143
+ {
144
+ ca_roll_new_arg *a = (ca_roll_new_arg *) varg;
145
+ ca_roll_setup(a->ca, a->parent, a->shift);
146
+ return Qnil;
147
+ }
148
+
149
+ /* The setup allocates reps and dim after the struct: when either fails,
150
+ the struct goes too. */
133
151
  CARoll *
134
152
  ca_roll_new (CArray *parent, ca_size_t *shift)
135
153
  {
136
- CARoll *ca;
154
+ ca_roll_new_arg a;
137
155
  ca_roll_setup(NULL, parent, shift);
138
- ca = ALLOC(CARoll);
139
- ca_roll_setup(ca, parent, shift);
140
- ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
141
- return ca;
156
+ a.ca = ALLOC(CARoll);
157
+ a.parent = parent;
158
+ a.shift = shift;
159
+ ca_fill_or_free((CArray *) a.ca, ca_roll_new_setup, (VALUE) &a);
160
+ ca_fill_or_free((CArray *) a.ca, ca_view_inherit_mask, (VALUE) a.ca);
161
+ return a.ca;
142
162
  }
143
163
 
144
164
  static void
@@ -629,7 +649,7 @@ rb_ca_roll (int argc, VALUE *argv, VALUE self)
629
649
  }
630
650
  for ( i = 0; i < ca->ndim; i++ ) {
631
651
  if ( i < argc ) {
632
- shift[i] = NUM2SIZE(argv[i]);
652
+ shift[i] = ca_integer_arg(argv[i], "a shift", NULL);
633
653
  } else {
634
654
  shift[i] = 0;
635
655
  }
data/ext/ca_obj_select.c CHANGED
@@ -130,7 +130,8 @@ ca_select_snapshot (CArray *select)
130
130
 
131
131
  /* Set up the view over `parent` from an owned selector snapshot (see
132
132
  ca_select_snapshot) and pre-compute ca->indices (TRUE positions in flat
133
- parent order). Does not raise. */
133
+ parent order). Raises only when the indices cannot be allocated, after
134
+ every field free_ca_select reads is set. */
134
135
  static int
135
136
  ca_select_setup (CASelect *ca, CArray *parent, CArray *snapshot)
136
137
  {
@@ -211,14 +212,42 @@ ca_select_setup (CASelect *ca, CArray *parent, CArray *snapshot)
211
212
  return 0;
212
213
  }
213
214
 
215
+ typedef struct {
216
+ CASelect *ca;
217
+ CArray *parent;
218
+ CArray *snapshot;
219
+ } ca_select_new_arg;
220
+
221
+ static VALUE
222
+ ca_select_new_alloc (VALUE varg)
223
+ {
224
+ ca_select_new_arg *a = (ca_select_new_arg *) varg;
225
+ a->ca = ALLOC(CASelect);
226
+ return Qnil;
227
+ }
228
+
229
+ static VALUE
230
+ ca_select_new_setup (VALUE varg)
231
+ {
232
+ ca_select_new_arg *a = (ca_select_new_arg *) varg;
233
+ ca_select_setup(a->ca, a->parent, a->snapshot);
234
+ return Qnil;
235
+ }
236
+
237
+ /* The struct and the indices are allocated after the snapshot. When the
238
+ struct cannot be allocated the snapshot goes; when the indices cannot,
239
+ the struct goes and takes the snapshot with it (the setup stores the
240
+ snapshot before it allocates the indices). */
214
241
  CArray *
215
242
  ca_select_new (CArray *parent, CArray *select)
216
243
  {
217
- CArray *snapshot = ca_select_snapshot(select);
244
+ ca_select_new_arg a;
245
+ a.parent = parent;
246
+ a.snapshot = ca_select_snapshot(select);
218
247
  /* check: the setup checks nothing */
219
- CASelect *ca = ALLOC(CASelect);
220
- ca_select_setup(ca, parent, snapshot);
221
- return (CArray*) ca;
248
+ ca_fill_or_free(a.snapshot, ca_select_new_alloc, (VALUE) &a);
249
+ ca_fill_or_free((CArray *) a.ca, ca_select_new_setup, (VALUE) &a);
250
+ return (CArray*) a.ca;
222
251
  }
223
252
 
224
253
  /* Alias for source compatibility. Both constructors produce a
data/ext/ca_obj_shift.c CHANGED
@@ -120,16 +120,40 @@ ca_shift_setup (CAShift *ca, CArray *parent,
120
120
  return 0;
121
121
  }
122
122
 
123
+ typedef struct {
124
+ CAShift *ca;
125
+ CArray *parent;
126
+ ca_size_t *shift;
127
+ char *fill;
128
+ int8_t *roll;
129
+ int fill_mask;
130
+ } ca_shift_new_arg;
131
+
132
+ static VALUE
133
+ ca_shift_new_setup (VALUE varg)
134
+ {
135
+ ca_shift_new_arg *a = (ca_shift_new_arg *) varg;
136
+ ca_shift_setup(a->ca, a->parent, a->shift, a->fill, a->roll, a->fill_mask);
137
+ return Qnil;
138
+ }
139
+
140
+ /* The setup allocates the fill value after the struct and its pool: when
141
+ that fails, the struct goes too. */
123
142
  CAShift *
124
143
  ca_shift_new (CArray *parent, ca_size_t *shift, char *fill, int8_t *roll,
125
144
  int fill_mask)
126
145
  {
127
- CAShift *ca;
146
+ ca_shift_new_arg a;
128
147
  ca_shift_setup(NULL, parent, shift, fill, roll, fill_mask);
129
- ca = (CAShift *) ca_array_alloc(CA_OBJ_SHIFT, parent->ndim);
130
- ca_shift_setup(ca, parent, shift, fill, roll, fill_mask);
131
- ca_fill_or_free((CArray *) ca, ca_window_create_bounds_mask, (VALUE) ca);
132
- return ca;
148
+ a.ca = (CAShift *) ca_array_alloc(CA_OBJ_SHIFT, parent->ndim);
149
+ a.parent = parent;
150
+ a.shift = shift;
151
+ a.fill = fill;
152
+ a.roll = roll;
153
+ a.fill_mask = fill_mask;
154
+ ca_fill_or_free((CArray *) a.ca, ca_shift_new_setup, (VALUE) &a);
155
+ ca_fill_or_free((CArray *) a.ca, ca_window_create_bounds_mask, (VALUE) a.ca);
156
+ return a.ca;
133
157
  }
134
158
 
135
159
  /* Clone: produce a CAShift (not CAWindow) by rebuilding via setup.
@@ -216,7 +240,7 @@ rb_ca_shift (int argc, VALUE *argv, VALUE self)
216
240
  }
217
241
 
218
242
  for (i=0; i<ca->ndim; i++) {
219
- shift[i] = NUM2SIZE(argv[i]);
243
+ shift[i] = ca_integer_arg(argv[i], "a shift", NULL);
220
244
  }
221
245
 
222
246
  if ( rb_block_given_p() ) {
@@ -264,7 +288,7 @@ rb_ca_shift (int argc, VALUE *argv, VALUE self)
264
288
  }
265
289
 
266
290
  for (i=0; i<ca->ndim; i++) {
267
- roll[i] = NUM2INT(rb_ary_entry(rroll, i));
291
+ roll[i] = ca_integer_arg(rb_ary_entry(rroll, i), "a shift", NULL);
268
292
  }
269
293
  }
270
294
 
data/ext/ca_obj_stack.c CHANGED
@@ -970,6 +970,7 @@ ca_stack_func_fold_stride (void *ap, ca_fold_t *f, void **next_parent)
970
970
  correct (if non-folded) per-parent dispatch. */
971
971
  if ( ca->k_axis != 0 ) return 0;
972
972
  if ( ! ca_fast_paths_enabled() ) return 0;
973
+ if ( pbt == 0 ) return 0; /* empty parents: no parent holds the box */
973
974
 
974
975
  for ( i = 0; i < f->ndim; i++ ) {
975
976
  ca_size_t span = (f->counts[i] - 1) * f->strides[i];
data/ext/ca_obj_stride.c CHANGED
@@ -247,6 +247,10 @@ ca_stride_compose_through (CAStride *leaf, CAStride *parent,
247
247
  for (k = parent->ndim - 1; k >= 0; k--)
248
248
  prod[k] = prod[k + 1] * parent->dim[k];
249
249
 
250
+ /* A parent with a zero-length axis has no cell for the leaf to start at
251
+ (and prod[] below would divide by zero). */
252
+ if (prod[0] == 0) return 0;
253
+
250
254
  /* Compose base offset first. We need the per-dim base position
251
255
  to validate stride composition against parent dim bounds (a small
252
256
  forward stride starting near the end of a parent dim wraps into
@@ -1686,19 +1690,19 @@ ca_stride_func_fill_data (void *ap, void *ptr)
1686
1690
  }
1687
1691
  case 2: {
1688
1692
  int16_t v; memcpy(&v, ptr, 2);
1689
- int16_t *d = (int16_t *) dst;
1693
+ ca_u_int16_t *d = (ca_u_int16_t *) dst;
1690
1694
  for (i = 0; i < inner_count; i++) d[i] = v;
1691
1695
  break;
1692
1696
  }
1693
1697
  case 4: {
1694
1698
  int32_t v; memcpy(&v, ptr, 4);
1695
- int32_t *d = (int32_t *) dst;
1699
+ ca_u_int32_t *d = (ca_u_int32_t *) dst;
1696
1700
  for (i = 0; i < inner_count; i++) d[i] = v;
1697
1701
  break;
1698
1702
  }
1699
1703
  case 8: {
1700
1704
  int64_t v; memcpy(&v, ptr, 8);
1701
- int64_t *d = (int64_t *) dst;
1705
+ ca_u_int64_t *d = (ca_u_int64_t *) dst;
1702
1706
  for (i = 0; i < inner_count; i++) d[i] = v;
1703
1707
  break;
1704
1708
  }
@@ -2164,11 +2168,11 @@ rb_ca_as_strided (int argc, VALUE *argv, VALUE self)
2164
2168
  ca_check_rank_fits(len, NULL);
2165
2169
  ndim = (int8_t) len;
2166
2170
  for (i = 0; i < len; i++) {
2167
- shape[i] = NUM2SIZE(RARRAY_AREF(rshape, i));
2168
- strides[i] = NUM2SIZE(RARRAY_AREF(rstrides, i));
2171
+ shape[i] = ca_integer_arg(RARRAY_AREF(rshape, i), "a length", NULL);
2172
+ strides[i] = ca_integer_arg(RARRAY_AREF(rstrides, i), "a stride", NULL);
2169
2173
  }
2170
2174
  if (! NIL_P(roffset)) {
2171
- base_offset = NUM2SIZE(roffset);
2175
+ base_offset = ca_integer_arg(roffset, "offset", NULL);
2172
2176
  }
2173
2177
 
2174
2178
  VALUE obj = rb_ca_stride_new(self, parent->data_type, parent->bytes,
@@ -2215,12 +2219,12 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2215
2219
  RARRAY_LEN(wary), (int) ndim);
2216
2220
  }
2217
2221
  for (i = 0; i < ndim; i++) {
2218
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2222
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2219
2223
  }
2220
2224
  }
2221
2225
  else if (nargs == ndim) {
2222
2226
  for (i = 0; i < ndim; i++) {
2223
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2227
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2224
2228
  }
2225
2229
  }
2226
2230
  else {
@@ -2239,11 +2243,11 @@ rb_ca_sliding_windows (int argc, VALUE *argv, VALUE self)
2239
2243
  RARRAY_LEN(rstep), (int) ndim);
2240
2244
  }
2241
2245
  for (i = 0; i < ndim; i++) {
2242
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2246
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2243
2247
  }
2244
2248
  }
2245
2249
  else {
2246
- ca_size_t s = NUM2SIZE(rstep);
2250
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2247
2251
  for (i = 0; i < ndim; i++) step[i] = s;
2248
2252
  }
2249
2253
 
@@ -2326,13 +2330,13 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2326
2330
  }
2327
2331
  nspatial = (int8_t) RARRAY_LEN(wary);
2328
2332
  for (i = 0; i < nspatial; i++) {
2329
- window[i] = NUM2SIZE(RARRAY_AREF(wary, i));
2333
+ window[i] = ca_integer_arg(RARRAY_AREF(wary, i), "a window size", NULL);
2330
2334
  }
2331
2335
  }
2332
2336
  else if (nargs >= 1 && nargs <= ndim) {
2333
2337
  nspatial = (int8_t) nargs;
2334
2338
  for (i = 0; i < nspatial; i++) {
2335
- window[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2339
+ window[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a window size", NULL);
2336
2340
  }
2337
2341
  }
2338
2342
  else {
@@ -2355,11 +2359,11 @@ rb_ca_unfold (int argc, VALUE *argv, VALUE self)
2355
2359
  RARRAY_LEN(rstep), (int) nspatial);
2356
2360
  }
2357
2361
  for (i = 0; i < nspatial; i++) {
2358
- step[i] = NUM2SIZE(RARRAY_AREF(rstep, i));
2362
+ step[i] = ca_integer_arg(RARRAY_AREF(rstep, i), "a step", NULL);
2359
2363
  }
2360
2364
  }
2361
2365
  else {
2362
- ca_size_t s = NUM2SIZE(rstep);
2366
+ ca_size_t s = ca_integer_arg(rstep, "step", NULL);
2363
2367
  for (i = 0; i < nspatial; i++) step[i] = s;
2364
2368
  }
2365
2369
 
@@ -2441,12 +2445,12 @@ rb_ca_block_view (int argc, VALUE *argv, VALUE self)
2441
2445
  RARRAY_LEN(bary), (int) ndim);
2442
2446
  }
2443
2447
  for (i = 0; i < ndim; i++) {
2444
- block[i] = NUM2SIZE(RARRAY_AREF(bary, i));
2448
+ block[i] = ca_integer_arg(RARRAY_AREF(bary, i), "a block size", NULL);
2445
2449
  }
2446
2450
  }
2447
2451
  else if (nargs == ndim) {
2448
2452
  for (i = 0; i < ndim; i++) {
2449
- block[i] = NUM2SIZE(RARRAY_AREF(rposary, i));
2453
+ block[i] = ca_integer_arg(RARRAY_AREF(rposary, i), "a block size", NULL);
2450
2454
  }
2451
2455
  }
2452
2456
  else {
@@ -2571,7 +2575,12 @@ rb_ca_dim_view (int argc, VALUE *argv, VALUE self)
2571
2575
  if (! seen[i]) imap[k++] = i;
2572
2576
  }
2573
2577
 
2574
- return rb_ca_trans_new(self, imap);
2578
+ {
2579
+ /* A reordering of the axes, as transpose is: a Face stays its Face. */
2580
+ volatile VALUE obj = rb_ca_trans_new(self, imap);
2581
+ CA_WRAPPER_LIFT(obj, self, parent);
2582
+ return obj;
2583
+ }
2575
2584
  }
2576
2585
 
2577
2586
  /* flip(*axes) -- reverses the listed axes by negating their strides; with no
@@ -2740,14 +2749,14 @@ rb_ca_diagonal (int argc, VALUE *argv, VALUE self)
2740
2749
 
2741
2750
  /* offset from positional or keyword (not both) */
2742
2751
  if (nargs == 0) {
2743
- if (! NIL_P(roffset)) offset = NUM2SIZE(roffset);
2752
+ if (! NIL_P(roffset)) offset = ca_integer_arg(roffset, "offset", NULL);
2744
2753
  }
2745
2754
  else if (nargs == 1) {
2746
2755
  if (! NIL_P(roffset)) {
2747
2756
  rb_raise(rb_eArgError,
2748
2757
  "diagonal: give offset positionally OR as keyword, not both");
2749
2758
  }
2750
- offset = NUM2SIZE(RARRAY_AREF(rposary, 0));
2759
+ offset = ca_integer_arg(RARRAY_AREF(rposary, 0), "offset", NULL);
2751
2760
  }
2752
2761
  else {
2753
2762
  rb_raise(rb_eArgError,
data/ext/ca_obj_string.c CHANGED
@@ -216,7 +216,10 @@ ca_string_refuse_cell (VALUE v, const char *where)
216
216
  "UNDEF masks a cell)", where, rb_obj_classname(v));
217
217
  }
218
218
 
219
- /* Every cell of `parent` that is not masked is a String. */
219
+ /* Every cell of `parent` that is not masked is a String. Under a masked
220
+ cell of an entity that can be written, a value that is not a String (the
221
+ 0 an object array starts with) becomes an empty String, so that the cell
222
+ is still a String once it is unmasked. */
220
223
  static void
221
224
  ca_string_check_cells (VALUE parent_val, CArray *parent)
222
225
  {
@@ -226,8 +229,14 @@ ca_string_check_cells (VALUE parent_val, CArray *parent)
226
229
  VALUE *p = (VALUE *) parent->ptr;
227
230
  boolean8_t *m = ( parent->mask && parent->mask->ptr )
228
231
  ? (boolean8_t *) parent->mask->ptr : NULL;
232
+ int writable = ! ca_is_readonly(parent);
229
233
  for (i = 0; i < parent->elements; i++) {
230
- if ( m && m[i] ) continue;
234
+ if ( m && m[i] ) {
235
+ if ( writable && ! RB_TYPE_P(p[i], T_STRING) ) {
236
+ p[i] = rb_utf8_str_new("", 0);
237
+ }
238
+ continue;
239
+ }
231
240
  if ( ! RB_TYPE_P(p[i], T_STRING) ) {
232
241
  snprintf(where, sizeof(where), "cell %" PRId64, (int64_t) i);
233
242
  ca_string_refuse_cell(p[i], where);