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
data/ext/ca_obj_face.c CHANGED
@@ -187,8 +187,22 @@ rb_ca_face_template (VALUE original_face, CArray *new_parent,
187
187
  dst);
188
188
  }
189
189
 
190
+ /* A Face put on a fresh entity has nothing to be read-only for: the template
191
+ copied the flag of the Face it came from, which may be read-only only as
192
+ that view was. A Face that cannot hold a new value keeps it. */
193
+ static void
194
+ ca_face_fresh_entity_is_writable (volatile VALUE lifted, VALUE face)
195
+ {
196
+ CArray *lc;
197
+ TypedData_Get_Struct(lifted, CArray, &carray_data_type, lc);
198
+ if ( ca_test_flag(lc, CA_FLAG_READ_ONLY)
199
+ && RTEST(rb_funcall(face, rb_intern("holds_new_values?"), 0)) ) {
200
+ ca_unset_flag(lc, CA_FLAG_READ_ONLY);
201
+ }
202
+ }
203
+
190
204
  VALUE
191
- ca_face_relift_entity (VALUE face, VALUE result)
205
+ ca_face_relift_entity (VALUE face, volatile VALUE result)
192
206
  {
193
207
  static ID id_copy_state = 0;
194
208
  CArray *rc, *lc;
@@ -197,6 +211,7 @@ ca_face_relift_entity (VALUE face, VALUE result)
197
211
  TypedData_Get_Struct(result, CArray, &carray_data_type, rc);
198
212
  lifted = rb_ca_face_template(face, rc, rc->dim);
199
213
  rb_ca_set_parent(lifted, result);
214
+ ca_face_fresh_entity_is_writable(lifted, face);
200
215
  if ( ca_has_mask(rc) ) {
201
216
  /* The template starts without a mask. */
202
217
  TypedData_Get_Struct(lifted, CArray, &carray_data_type, lc);
@@ -304,6 +319,9 @@ ca_face_lift (VALUE view, VALUE face_parent)
304
319
  if ( ca_is_view(view_ca) ) {
305
320
  rb_ca_set_lifted_from(lifted, face_parent);
306
321
  }
322
+ else {
323
+ ca_face_fresh_entity_is_writable(lifted, face_parent);
324
+ }
307
325
 
308
326
  /* Optional Ruby callback for ivar / state carry. Faces holding
309
327
  semantic state in the struct tail (CATime / CATimedelta
@@ -358,7 +376,7 @@ ca_face_lift (VALUE view, VALUE face_parent)
358
376
  * builder handed us a view whose parent is the marker, and leaving it
359
377
  * there would stack a redundant marker in the middle. */
360
378
  static VALUE
361
- ca_lazy_marker_lift (VALUE view, VALUE marker)
379
+ ca_lazy_marker_lift (VALUE view, volatile VALUE marker)
362
380
  {
363
381
  CArray *view_ca, *mp;
364
382
  extern VALUE rb_ca_lazy_marker_new (VALUE cary);
@@ -741,16 +759,16 @@ ca_face_refuse_order (const char *name, VALUE face, const char *verb)
741
759
  const char *storage = ca_face_storage_method(face);
742
760
  if ( storage ) {
743
761
  rb_raise(rb_eArgError,
744
- "%s: %s is not orderable by storage (a cell encodes the value "
762
+ "%s: %"PRIsVALUE" is not orderable by storage (a cell encodes the value "
745
763
  "rather than being it); %s its values instead -- %s -- or %s "
746
764
  "its storage, which #%s gives",
747
- name, rb_obj_classname(face), verb,
765
+ name, rb_obj_class(face), verb,
748
766
  ca_face_conversion_hint(face), verb, storage);
749
767
  }
750
768
  rb_raise(rb_eArgError,
751
- "%s: %s is not orderable by storage (a cell encodes the value "
769
+ "%s: %"PRIsVALUE" is not orderable by storage (a cell encodes the value "
752
770
  "rather than being it); %s its values instead -- %s",
753
- name, rb_obj_classname(face), verb,
771
+ name, rb_obj_class(face), verb,
754
772
  ca_face_conversion_hint(face));
755
773
  }
756
774
 
@@ -808,17 +826,17 @@ ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass)
808
826
  const char *storage = ca_face_storage_method(self);
809
827
  if ( storage ) {
810
828
  rb_raise(eclass,
811
- "%s: %s does not define it (a cell encodes the value rather "
829
+ "%s: %"PRIsVALUE" does not define it (a cell encodes the value rather "
812
830
  "than being a number); compute on its values instead -- %s -- "
813
831
  "or on its storage, which #%s gives",
814
- name, rb_obj_classname(self), ca_face_conversion_hint(self),
832
+ name, rb_obj_class(self), ca_face_conversion_hint(self),
815
833
  storage);
816
834
  }
817
835
  }
818
836
  rb_raise(eclass,
819
- "%s: %s does not define it (a cell encodes the value rather than "
837
+ "%s: %"PRIsVALUE" does not define it (a cell encodes the value rather than "
820
838
  "being a number); compute on its values instead -- %s",
821
- name, rb_obj_classname(self), ca_face_conversion_hint(self));
839
+ name, rb_obj_class(self), ca_face_conversion_hint(self));
822
840
  }
823
841
 
824
842
  VALUE
@@ -839,21 +857,21 @@ ca_face_operand_descend (VALUE operand, const char *name)
839
857
  const char *storage = ca_face_storage_method(operand);
840
858
  if ( storage ) {
841
859
  rb_raise(rb_eArgError,
842
- "%s: cannot take a %s operand: its storage is not its surface "
860
+ "%s: cannot take a %"PRIsVALUE" operand: its storage is not its surface "
843
861
  "(a cell encodes the value rather than being it), so comparing "
844
862
  "the storage would compare the encoding. Convert the operand "
845
863
  "to the receiver's space first -- %s -- or compare the storage "
846
864
  "of both sides, which #%s gives",
847
- name, rb_obj_classname(operand),
865
+ name, rb_obj_class(operand),
848
866
  ca_face_conversion_hint(operand), storage);
849
867
  }
850
868
  }
851
869
  rb_raise(rb_eArgError,
852
- "%s: cannot take a %s operand: its storage is not its surface "
870
+ "%s: cannot take a %"PRIsVALUE" operand: its storage is not its surface "
853
871
  "(a cell encodes the value rather than being it), so comparing "
854
872
  "the storage would compare the encoding. Convert the operand to "
855
873
  "the receiver's space first -- %s",
856
- name, rb_obj_classname(operand), ca_face_conversion_hint(operand));
874
+ name, rb_obj_class(operand), ca_face_conversion_hint(operand));
857
875
  return Qnil; /* not reached */
858
876
  }
859
877
 
@@ -862,11 +880,12 @@ ca_face_operand_descend (VALUE operand, const char *name)
862
880
 
863
881
  A Face equals only a Face of its own class. When the two share their
864
882
  state (a time array's unit) and the storage holds the values in their
865
- order, equal storage is equal values, so the storage is compared.
866
- Otherwise `==` compares the values themselves (two time arrays of
867
- different units, two const strings over different pools, two
868
- categoricals with differently ordered labels), and `eql?`, which has to
869
- agree with #hash, answers false. */
883
+ order, cell for cell at one width, equal storage is equal values, so the
884
+ storage is compared. Otherwise `==` compares the values themselves (two
885
+ time arrays of different units, two const strings over different pools,
886
+ two categoricals with differently ordered labels, two fixed-length
887
+ strings of different widths), and `eql?`, which has to agree with #hash,
888
+ answers false. */
870
889
  int
871
890
  ca_face_equal (VALUE va, VALUE vb, int strict)
872
891
  {
@@ -889,7 +908,8 @@ ca_face_equal (VALUE va, VALUE vb, int strict)
889
908
  }
890
909
  }
891
910
  if ( ca_face_state_compatible(va, a, vb, b)
892
- && ( strict || ca_test_flag(a, CA_FLAG_FACE_ORDERABLE_STORAGE) ) ) {
911
+ && ( strict || ( ca_test_flag(a, CA_FLAG_FACE_ORDERABLE_STORAGE)
912
+ && a->bytes == b->bytes ) ) ) {
893
913
  return strict ? ca_eql_storage(ca_strip_face(a), ca_strip_face(b))
894
914
  : ca_equal(ca_strip_face(a), ca_strip_face(b));
895
915
  }
@@ -959,8 +979,8 @@ ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
959
979
  if ( ca_is_face(op) ) {
960
980
  if ( op->obj_type != CA_OBJ_RECORD ) {
961
981
  rb_raise(rb_eTypeError,
962
- "%s: can not take a %s operand for a record array of "
963
- "%"PRIsVALUE, name, rb_obj_classname(operand),
982
+ "%s: can not take a %"PRIsVALUE" operand for a record array of "
983
+ "%"PRIsVALUE, name, rb_obj_class(operand),
964
984
  rb_ca_data_class(reference));
965
985
  }
966
986
  if ( rb_ca_data_class(operand) != rb_ca_data_class(reference) ) {
@@ -987,16 +1007,16 @@ ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
987
1007
  const char *storage = ca_face_storage_method(reference);
988
1008
  if ( storage ) {
989
1009
  rb_raise(rb_eArgError,
990
- "%s: %s is not comparable by storage and has no to_comparable "
1010
+ "%s: %"PRIsVALUE" is not comparable by storage and has no to_comparable "
991
1011
  "to bring the operand into its space; compare the storage of "
992
1012
  "both sides, which #%s gives",
993
- name, rb_obj_classname(reference), storage);
1013
+ name, rb_obj_class(reference), storage);
994
1014
  }
995
1015
  }
996
1016
  rb_raise(rb_eArgError,
997
- "%s: %s is not comparable by storage and has no to_comparable "
1017
+ "%s: %"PRIsVALUE" is not comparable by storage and has no to_comparable "
998
1018
  "to bring the operand into its space",
999
- name, rb_obj_classname(reference));
1019
+ name, rb_obj_class(reference));
1000
1020
  return Qnil; /* not reached */
1001
1021
  }
1002
1022
 
@@ -1088,8 +1108,8 @@ ca_face_list_class (VALUE list, const char *who)
1088
1108
  "%s: cannot mix Face and non-Face arrays, or arrays of "
1089
1109
  "different Face classes; convert them first to one Face "
1090
1110
  "class or to no Face, or combine their storage (#%s gives "
1091
- "the storage of a %s)",
1092
- who, storage, rb_obj_classname(face));
1111
+ "the storage of a %"PRIsVALUE")",
1112
+ who, storage, rb_obj_class(face));
1093
1113
  }
1094
1114
  rb_raise(rb_eArgError,
1095
1115
  "%s: cannot mix Face and non-Face arrays, or arrays of "
@@ -1099,10 +1119,10 @@ ca_face_list_class (VALUE list, const char *who)
1099
1119
  }
1100
1120
  if ( n > 1 && ! ca_face_state_portable(ref->obj_type, face_class) ) {
1101
1121
  rb_raise(rb_eArgError,
1102
- "%s: %s state is not portable across several arrays (each "
1122
+ "%s: %"PRIsVALUE" state is not portable across several arrays (each "
1103
1123
  "keeps its values in storage of its own); convert them first "
1104
1124
  "(%s)",
1105
- who, rb_class2name(face_class),
1125
+ who, face_class,
1106
1126
  ca_face_conversion_hint(face));
1107
1127
  }
1108
1128
  for ( i = 1; i < n; i++ ) {
@@ -1111,8 +1131,8 @@ ca_face_list_class (VALUE list, const char *who)
1111
1131
  TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
1112
1132
  if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref, p, ca) ) {
1113
1133
  rb_raise(rb_eArgError,
1114
- "%s: Face state mismatch (the %s at index %ld differs from "
1115
- "index 0)", who, rb_class2name(face_class), i);
1134
+ "%s: Face state mismatch (the %"PRIsVALUE" at index %ld differs from "
1135
+ "index 0)", who, face_class, i);
1116
1136
  }
1117
1137
  }
1118
1138
  return face_class;
@@ -1195,7 +1215,7 @@ rb_ca_reconcile_operand (VALUE self, VALUE operand, VALUE rname)
1195
1215
  static VALUE
1196
1216
  rb_ca_face_lift_method (VALUE self, VALUE face_parent)
1197
1217
  {
1198
- CArray *fp;
1218
+ CArray *fp, *vc, *storage;
1199
1219
  if ( ! rb_obj_is_kind_of(face_parent, rb_cCArray) ) {
1200
1220
  rb_raise(rb_eArgError, "face_lift: face_parent must be a CArray");
1201
1221
  }
@@ -1203,6 +1223,24 @@ rb_ca_face_lift_method (VALUE self, VALUE face_parent)
1203
1223
  if ( ! ca_is_face(fp) ) {
1204
1224
  rb_raise(rb_eArgError, "face_lift: face_parent must be a Face instance");
1205
1225
  }
1226
+ rb_check_carray_object(self);
1227
+ TypedData_Get_Struct(self, CArray, &carray_data_type, vc);
1228
+ /* A Face of the same class is already lifted (see ca_face_lift). */
1229
+ if ( ca_is_face(vc) && rb_obj_class(self) == rb_obj_class(face_parent) ) {
1230
+ return self;
1231
+ }
1232
+ storage = ca_strip_face(fp);
1233
+ /* A Face reads the cells under it as its own, so they must be held in
1234
+ its surface type or its storage type. A Face receiver is held in its
1235
+ surface type, and is checked the same way. */
1236
+ if ( ! ( vc->data_type == fp->data_type && vc->bytes == fp->bytes )
1237
+ && ! ( vc->data_type == storage->data_type
1238
+ && vc->bytes == storage->bytes ) ) {
1239
+ rb_raise(rb_eArgError,
1240
+ "face_lift: a %"PRIsVALUE" holds its cells as %s, not as %s",
1241
+ rb_obj_class(face_parent), ca_type_name[storage->data_type],
1242
+ ca_type_name[vc->data_type]);
1243
+ }
1206
1244
  return ca_face_lift(self, face_parent);
1207
1245
  }
1208
1246
 
data/ext/ca_obj_fake.c CHANGED
@@ -154,6 +154,71 @@ ca_fake_func_clone (void *ap)
154
154
  return ca_fake_new(ca->parent, ca->data_type, ca->bytes);
155
155
  }
156
156
 
157
+ /* A read does not raise for a masked cell (ca_fake_cast_get), as attach
158
+ does not: the value under a mask is not defined, and converting it can
159
+ raise (NaN to an integer type). The view's cells are its parent's at the same
160
+ addresses, so the parent's mask is read for the same cells. The view's
161
+ own mask is not made here: once it exists, sync leaves masked cells
162
+ alone, and a read must not change what a later write does. */
163
+ static CArray *
164
+ ca_fake_read_mask (CAFake *ca)
165
+ {
166
+ if ( ca->mask ) {
167
+ return ca->mask;
168
+ }
169
+ if ( ca_has_mask(ca->parent) ) {
170
+ return ca->parent->mask;
171
+ }
172
+ return NULL;
173
+ }
174
+
175
+ /* Convert n cells read from the parent. A masked cell is converted as
176
+ well, so that the cell holds what the parent holds (a walk that sends
177
+ back only the cells a kernel changed compares against it), but a value
178
+ that cannot be converted (NaN to an integer) leaves zero bytes instead
179
+ of raising: the value under a mask is not defined. */
180
+ typedef struct {
181
+ CArray *src_ca, *dst_ca;
182
+ char *src, *dst;
183
+ } ca_fake_cell_ctx_t;
184
+
185
+ static VALUE
186
+ ca_fake_cast_cell (VALUE arg)
187
+ {
188
+ ca_fake_cell_ctx_t *c = (ca_fake_cell_ctx_t *) arg;
189
+ ca_ptr2ptr(c->src_ca, c->src, c->dst_ca, c->dst);
190
+ return Qnil;
191
+ }
192
+
193
+ static void
194
+ ca_fake_cast_get (CAFake *ca, ca_size_t n, char *src, char *dst, boolean8_t *m)
195
+ {
196
+ CArray *parent = ca->parent;
197
+ ca_size_t i;
198
+ if ( ! m ) {
199
+ ca_cast_block(n, parent, src, (CArray *) ca, dst);
200
+ return;
201
+ }
202
+ ca_cast_block_skip_masked(n, parent, src, (CArray *) ca, dst, m);
203
+ for ( i = 0; i < n; i++ ) {
204
+ if ( m[i] ) {
205
+ ca_fake_cell_ctx_t c;
206
+ int state = 0;
207
+ c.src_ca = parent;
208
+ c.dst_ca = (CArray *) ca;
209
+ c.src = src + i * parent->bytes;
210
+ c.dst = dst + i * ca->bytes;
211
+ /* A masked cell that cannot be converted reads as zero bytes. */
212
+ /* protect: converts a masked cell; nothing is held */
213
+ rb_protect(ca_fake_cast_cell, (VALUE) &c, &state);
214
+ if ( state ) {
215
+ rb_set_errinfo(Qnil);
216
+ memset(c.dst, 0, ca->bytes);
217
+ }
218
+ }
219
+ }
220
+ }
221
+
157
222
  /* Per-cell GET/PUT via a parent-sized scratch cell — same code services
158
223
  both directions, casting through the parent data_type. */
159
224
  static void
@@ -161,10 +226,16 @@ ca_fake_func_xfer_index (void *ap, ca_size_t *idx, void *data, int dir)
161
226
  {
162
227
  CAFake *ca = (CAFake *) ap;
163
228
  volatile VALUE holder;
164
- char *v = ALLOCV_N(char, holder, ca->parent->bytes);
229
+ char *v;
230
+ v = ALLOCV_N(char, holder, ca->parent->bytes);
165
231
  if ( dir == CA_XFER_GET ) {
232
+ CArray *mask = ca_fake_read_mask(ca);
233
+ boolean8_t masked = 0;
234
+ if ( mask ) {
235
+ ca_fetch_index(mask, idx, &masked);
236
+ }
166
237
  ca_fetch_index(ca->parent, idx, v);
167
- ca_ptr2ptr(ca->parent, v, ca, data);
238
+ ca_fake_cast_get(ca, 1, v, (char *) data, mask ? &masked : NULL);
168
239
  }
169
240
  else {
170
241
  ca_ptr2ptr(ca, data, ca->parent, v);
@@ -184,10 +255,22 @@ ca_fake_func_xfer_addrs (void *ap, ca_size_t n, ca_size_t *addrs,
184
255
  CAFake *ca = (CAFake *) ap;
185
256
  CArray *parent = ca->parent;
186
257
  CArray *eff;
258
+ CArray *mask = ( dir == CA_XFER_GET ) ? ca_fake_read_mask(ca) : NULL;
187
259
  ca_size_t base;
188
260
  char *v;
189
261
  volatile VALUE holder;
190
262
 
263
+ if ( mask ) {
264
+ volatile VALUE mholder;
265
+ boolean8_t *m = ALLOCV_N(boolean8_t, mholder, n + n * parent->bytes);
266
+ v = (char *) (m + n);
267
+ ca_xfer_addrs(mask, n, addrs, m, CA_XFER_GET);
268
+ ca_xfer_addrs(parent, n, addrs, v, CA_XFER_GET);
269
+ ca_fake_cast_get(ca, n, v, (char *) data, m);
270
+ ALLOCV_END(mholder);
271
+ return;
272
+ }
273
+
191
274
  /* Sequential-run + live-ptr fast path: cast directly from
192
275
  parent->ptr + base*pbytes to data, no intermediate scratch. */
193
276
  eff = ca_resolve_attached_root_via_identity(parent);
@@ -251,6 +334,24 @@ ca_fake_func_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
251
334
  int8_t k;
252
335
  char *v;
253
336
  volatile VALUE holder;
337
+ CArray *mask = ( dir == CA_XFER_GET ) ? ca_fake_read_mask(ca) : NULL;
338
+
339
+ if ( mask ) {
340
+ ca_size_t mstrides[CA_RANK_MAX];
341
+ boolean8_t *m;
342
+ for ( k = 0; k < ndim; k++ ) {
343
+ n *= counts[k];
344
+ pstrides[k] = strides[k] / ca->bytes * ca->parent->bytes;
345
+ mstrides[k] = strides[k] / ca->bytes;
346
+ }
347
+ m = ALLOCV_N(boolean8_t, holder, n + n * ca->parent->bytes);
348
+ v = (char *) (m + n);
349
+ ca_xfer_stride(mask, starts, counts, mstrides, m, CA_XFER_GET);
350
+ ca_xfer_stride(ca->parent, starts, counts, pstrides, v, CA_XFER_GET);
351
+ ca_fake_cast_get(ca, n, v, (char *) data, m);
352
+ ALLOCV_END(holder);
353
+ return;
354
+ }
254
355
 
255
356
  /* Fast path: fused walk + cast for GET/PUT */
256
357
  if ( ca_xfer_stride_transform_fused((CArray *)ca, ca->parent,
@@ -282,21 +383,19 @@ static void
282
383
  ca_fake_func_allocate (void *ap)
283
384
  {
284
385
  CAFake *ca = (CAFake *) ap;
285
- ca_attach(ca->parent);
286
- ca->ptr = ca_alloc_cells((CArray *) ca);
386
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
287
387
  }
288
388
 
289
389
  static void
290
390
  ca_fake_func_attach (void *ap)
291
391
  {
292
392
  CAFake *ca = (CAFake *) ap;
293
- ca_attach(ca->parent);
294
- ca->ptr = ca_alloc_cells((CArray *) ca);
393
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
295
394
 
296
395
  if ( ca->parent->mask ) {
297
- ca_cast_block_with_mask(ca->elements, ca->parent, ca->parent->ptr,
298
- ca, ca->ptr,
299
- (boolean8_t*)ca->parent->mask->ptr);
396
+ ca_cast_block_skip_masked(ca->elements, ca->parent, ca->parent->ptr,
397
+ ca, ca->ptr,
398
+ (boolean8_t*)ca->parent->mask->ptr);
300
399
  }
301
400
  else {
302
401
  ca_cast_block(ca->elements, ca->parent, ca->parent->ptr, ca, ca->ptr);
@@ -309,8 +408,8 @@ ca_fake_func_sync (void *ap)
309
408
  CAFake *ca = (CAFake *) ap;
310
409
  ca_update_mask(ca);
311
410
  if ( ca->mask ) {
312
- ca_cast_block_with_mask(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr,
313
- (boolean8_t *)ca->mask->ptr);
411
+ ca_cast_block_skip_masked(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr,
412
+ (boolean8_t *)ca->mask->ptr);
314
413
  }
315
414
  else {
316
415
  ca_cast_block(ca->elements, ca, ca->ptr, ca->parent, ca->parent->ptr);
@@ -426,9 +525,9 @@ rb_ca_fake_new (VALUE cary, int8_t data_type, ca_size_t bytes)
426
525
  Both ways down stay open and say which one they are. A Numeric Face
427
526
  is not one of these: its surface is its storage. */
428
527
  rb_raise(rb_eTypeError,
429
- "%s has no view of its values in another data_type: "
528
+ "%"PRIsVALUE" has no view of its values in another data_type: "
430
529
  "#to_type gives the values, #parent.fake the raw storage",
431
- rb_obj_classname(cary));
530
+ rb_obj_class(cary));
432
531
  }
433
532
  ca = ca_fake_new(parent, data_type, bytes);
434
533
  obj = ca_wrap_struct(ca);
data/ext/ca_obj_farray.c CHANGED
@@ -8,6 +8,7 @@
8
8
 
9
9
  #include "carray.h"
10
10
  #include "carray_internal.h" /* ca_view_inherit_mask */
11
+ #include "ca_obj_face.h" /* CA_WRAPPER_LIFT */
11
12
 
12
13
  extern ca_operation_function_t ca_stride_func;
13
14
 
@@ -87,7 +88,12 @@ rb_ca_farray_new (VALUE cary)
87
88
  VALUE
88
89
  rb_ca_farray (VALUE self)
89
90
  {
90
- return rb_ca_farray_new(self);
91
+ volatile VALUE obj = rb_ca_farray_new(self);
92
+ CArray *ca;
93
+
94
+ TypedData_Get_Struct(self, CArray, &carray_data_type, ca);
95
+ CA_WRAPPER_LIFT(obj, self, ca);
96
+ return obj;
91
97
  }
92
98
 
93
99
  static VALUE
@@ -10,8 +10,9 @@
10
10
  fixlen bytes as a struct via the data_class encode/decode hooks); here
11
11
  the bytes are the string, fetched and stored by the native fixlen path.
12
12
 
13
- The Face's only job is to carry the CAFixlenString class across the view
14
- chain and to organise the String method surface onto a dedicated class.
13
+ The Face carries the CAFixlenString class and the encoding of the text
14
+ across the view chain, and organises the String method surface onto a
15
+ dedicated class.
15
16
  Because the storage is CA_FIXLEN (portable), the Face wrap can be carried
16
17
  across multi-parent constructions (CAStack / Marshal / MemoryView).
17
18
 
@@ -23,6 +24,7 @@
23
24
  #include "carray.h"
24
25
  #include "ca_obj_face.h"
25
26
  #include "carray_internal.h"
27
+ #include <ruby/encoding.h>
26
28
 
27
29
  typedef struct {
28
30
  /* === CAView prefix === */
@@ -39,7 +41,8 @@ typedef struct {
39
41
  CArray *parent;
40
42
  uint32_t attach;
41
43
  uint8_t nosync;
42
- /* === Face tail === (none: CAFixlenString carries no per-instance state) */
44
+ /* === Face tail === */
45
+ int encoding_id; /* rb_enc_to_index() value of the cells' text */
43
46
  } CAFixlenString;
44
47
 
45
48
  static size_t
@@ -83,7 +86,7 @@ static VALUE rb_cCAFixlenString;
83
86
  /* ------------------------------------------------------------------- */
84
87
 
85
88
  int
86
- ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
89
+ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent, int encoding_id)
87
90
  {
88
91
  if ( parent->data_type != CA_FIXLEN ) {
89
92
  rb_raise(rb_eTypeError,
@@ -136,6 +139,7 @@ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
136
139
  ca->parent = parent;
137
140
  ca->attach = 0;
138
141
  ca->nosync = 0;
142
+ ca->encoding_id = encoding_id;
139
143
 
140
144
 
141
145
  if ( ca_is_scalar(parent) ) {
@@ -150,12 +154,12 @@ ca_fixlen_string_setup (CAFixlenString *ca, CArray *parent)
150
154
  }
151
155
 
152
156
  CAFixlenString *
153
- ca_fixlen_string_new (CArray *parent)
157
+ ca_fixlen_string_new (CArray *parent, int encoding_id)
154
158
  {
155
159
  CAFixlenString *ca;
156
- ca_fixlen_string_setup(NULL, parent);
160
+ ca_fixlen_string_setup(NULL, parent, encoding_id);
157
161
  ca = (CAFixlenString *) ca_array_alloc(CA_OBJ_FIXLEN_STRING, parent->ndim);
158
- ca_fixlen_string_setup(ca, parent);
162
+ ca_fixlen_string_setup(ca, parent, encoding_id);
159
163
  ca_fill_or_free((CArray *) ca, ca_view_inherit_mask, (VALUE) ca);
160
164
  return ca;
161
165
  }
@@ -182,7 +186,7 @@ static void *
182
186
  ca_fixlen_string_func_clone (void *ap)
183
187
  {
184
188
  CAFixlenString *ca = (CAFixlenString *) ap;
185
- return ca_fixlen_string_new(ca->parent);
189
+ return ca_fixlen_string_new(ca->parent, ca->encoding_id);
186
190
  }
187
191
 
188
192
  static void
@@ -216,9 +220,10 @@ ca_operation_function_t ca_fixlen_string_func = {
216
220
 
217
221
  /* ------------------------------------------------------------------- */
218
222
 
219
- /* CAFixlenString.wrap(fixlen_array) — zero-copy Face wrap */
223
+ /* CAFixlenString.wrap(fixlen_array, encoding: nil) — zero-copy Face wrap.
224
+ Without an encoding the cells are bytes that claim no text encoding. */
220
225
  VALUE
221
- rb_ca_fixlen_string_wrap (VALUE parent_val)
226
+ rb_ca_fixlen_string_wrap (VALUE parent_val, int encoding_id)
222
227
  {
223
228
  volatile VALUE obj;
224
229
  CArray *parent;
@@ -227,17 +232,41 @@ rb_ca_fixlen_string_wrap (VALUE parent_val)
227
232
  rb_check_carray_object(parent_val);
228
233
  TypedData_Get_Struct(parent_val, CArray, &carray_data_type, parent);
229
234
 
230
- ca = ca_fixlen_string_new(parent);
235
+ ca = ca_fixlen_string_new(parent, encoding_id);
231
236
  obj = ca_wrap_struct(ca);
232
237
  rb_ca_set_parent(obj, parent_val);
233
238
  return obj;
234
239
  }
235
240
 
236
241
  static VALUE
237
- rb_ca_fixlen_string_wrap_method (VALUE klass, VALUE raw)
242
+ rb_ca_fixlen_string_wrap_method (int argc, VALUE *argv, VALUE klass)
238
243
  {
244
+ VALUE raw, ropt, renc = Qnil;
239
245
  (void) klass;
240
- return rb_ca_fixlen_string_wrap(raw);
246
+ rb_scan_args(argc, argv, "1:", &raw, &ropt);
247
+ rb_scan_options(ropt, "encoding", &renc);
248
+ return rb_ca_fixlen_string_wrap(raw, NIL_P(renc) ? rb_ascii8bit_encindex()
249
+ : rb_to_encoding_index(renc));
250
+ }
251
+
252
+ /* The encoding of the text in the cells; BINARY when the cells claim none. */
253
+ static VALUE
254
+ rb_ca_fixlen_string_encoding (VALUE self)
255
+ {
256
+ CAFixlenString *ca;
257
+ TypedData_Get_Struct(self, CAFixlenString, &cafixlen_string_data_type, ca);
258
+ return rb_enc_from_encoding(rb_enc_from_index(ca->encoding_id));
259
+ }
260
+
261
+ /* Arrays of two text encodings cannot be read as one column; cells that
262
+ claim no encoding go with either. */
263
+ static int
264
+ ca_fixlen_string_state_compatible (CArray *a, CArray *b)
265
+ {
266
+ int ea = ((CAFixlenString *) a)->encoding_id;
267
+ int eb = ((CAFixlenString *) b)->encoding_id;
268
+ int binary = rb_ascii8bit_encindex();
269
+ return ( ea == eb || ea == binary || eb == binary ) ? 1 : 0;
241
270
  }
242
271
 
243
272
  /* Per-cell scalar decode: strip trailing NUL padding from the raw K-byte
@@ -249,14 +278,15 @@ rb_ca_fixlen_string_wrap_method (VALUE klass, VALUE raw)
249
278
  static VALUE
250
279
  rb_ca_fixlen_string_storage_to_element (VALUE self, VALUE raw)
251
280
  {
252
- (void) self;
253
281
  if ( TYPE(raw) == T_STRING ) {
282
+ CAFixlenString *ca;
254
283
  const char *p = RSTRING_PTR(raw);
255
284
  long n = RSTRING_LEN(raw);
256
285
  while ( n > 0 && p[n - 1] == '\0' ) {
257
286
  n--;
258
287
  }
259
- return rb_str_new(p, n);
288
+ TypedData_Get_Struct(self, CAFixlenString, &cafixlen_string_data_type, ca);
289
+ return rb_enc_str_new(p, n, rb_enc_from_index(ca->encoding_id));
260
290
  }
261
291
  return raw;
262
292
  }
@@ -279,7 +309,7 @@ rb_ca_fixlen_string_initialize_copy (VALUE self, VALUE other)
279
309
  if ( ca_func[CA_OBJ_FIXLEN_STRING].pool_init ) {
280
310
  ca_array_pool_alloc(ca, CA_OBJ_FIXLEN_STRING, cs->parent->ndim);
281
311
  }
282
- ca_fixlen_string_setup(ca, cs->parent);
312
+ ca_fixlen_string_setup(ca, cs->parent, cs->encoding_id);
283
313
  ca_view_inherit_mask((VALUE) ca);
284
314
  return self;
285
315
  }
@@ -304,7 +334,9 @@ Init_ca_obj_fixlen_string (void)
304
334
  rb_define_method(rb_cCAFixlenString, "initialize_copy",
305
335
  rb_ca_fixlen_string_initialize_copy, 1);
306
336
  rb_define_singleton_method(rb_cCAFixlenString, "wrap",
307
- rb_ca_fixlen_string_wrap_method, 1);
337
+ rb_ca_fixlen_string_wrap_method, -1);
338
+ rb_define_method(rb_cCAFixlenString, "encoding",
339
+ rb_ca_fixlen_string_encoding, 0);
308
340
  rb_define_method(rb_cCAFixlenString, "storage_to_element",
309
341
  rb_ca_fixlen_string_storage_to_element, 1);
310
342
  ca_face_register_storage_to_element(CA_OBJ_FIXLEN_STRING,
@@ -314,6 +346,8 @@ Init_ca_obj_fixlen_string (void)
314
346
  or per-parent buffer), so the Face can be carried across multi-parent
315
347
  constructions like CAStack. */
316
348
  ca_face_register_state_portable(CA_OBJ_FIXLEN_STRING, 1);
349
+ ca_face_register_state_compatible(CA_OBJ_FIXLEN_STRING,
350
+ ca_fixlen_string_state_compatible);
317
351
 
318
352
  /* The bytes are the strings: a MemoryView consumer reads them as "Ns". */
319
353
  ca_face_register_memory_view(CA_OBJ_FIXLEN_STRING, 1);
data/ext/ca_obj_grid.c CHANGED
@@ -15,6 +15,7 @@
15
15
 
16
16
  #include "carray.h"
17
17
  #include "carray_internal.h" /* ca_attach_window */
18
+ #include "ca_obj_face.h" /* CA_WRAPPER_LIFT */
18
19
 
19
20
  /* Per-axis tagged kind. Each axis is either STRIDE (start/step/count,
20
21
  no allocation) or INDEX (an owned ca_size_t index snapshot). A Range
@@ -1087,9 +1088,11 @@ ca_grid_func_allocate (void *ap)
1087
1088
  {
1088
1089
  CAGrid *ca = (CAGrid *) ap;
1089
1090
  if ( ! cag_owns_buffer(ca) ) {
1090
- ca_attach(ca->parent);
1091
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
1092
+ }
1093
+ else {
1094
+ ca->ptr = ca_alloc_cells((CArray *) ca);
1091
1095
  }
1092
- ca->ptr = ca_alloc_cells((CArray *) ca);
1093
1096
  }
1094
1097
 
1095
1098
  static void
@@ -1104,10 +1107,10 @@ ca_grid_func_attach (void *ap)
1104
1107
  return;
1105
1108
  }
1106
1109
 
1107
- ca_attach(ca->parent);
1110
+ ca->ptr = ca_alloc_cells_then_attach((CArray *) ca, 1, &ca->parent);
1108
1111
  ca_grid_describe_axes(ca, desc, pdims);
1109
- ca->ptr = ca_axis_dispatch_attach(ca->parent, pdims, desc, ca->ndim,
1110
- ca->bytes, ca->elements, NULL);
1112
+ ca_axis_dispatch_gather(ca->parent, pdims, desc, ca->ndim,
1113
+ ca->bytes, ca->elements, NULL, ca->ptr);
1111
1114
  }
1112
1115
 
1113
1116
  static void
@@ -1422,8 +1425,9 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
1422
1425
  extern int ca_csa_dispatch_bypass;
1423
1426
  if ( ! ca_csa_dispatch_bypass && argc > 0 && TYPE(argv[0]) == T_DATA ) {
1424
1427
  if ( rb_ca_select_axis_eligible_p(argc, argv, self) ) {
1425
- return ca_grid_drop_scalar_axes(rb_ca_select_axis(argc, argv, self),
1426
- argc, argv);
1428
+ obj = rb_ca_select_axis(argc, argv, self);
1429
+ CA_WRAPPER_LIFT(obj, self, ca);
1430
+ return ca_grid_drop_scalar_axes(obj, argc, argv);
1427
1431
  }
1428
1432
  }
1429
1433
 
@@ -1492,6 +1496,7 @@ rb_ca_grid (int argc, VALUE *argv, VALUE self)
1492
1496
  (VALUE) &c);
1493
1497
  }
1494
1498
 
1499
+ CA_WRAPPER_LIFT(obj, self, ca);
1495
1500
  return ca_grid_drop_scalar_axes(obj, argc, argv);
1496
1501
  }
1497
1502