carray 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/ext/ca_obj_face.c
CHANGED
|
@@ -19,16 +19,18 @@
|
|
|
19
19
|
ca_face_lift re-wrap a view as a Face when the source was
|
|
20
20
|
ca_strip_face walk down through Face parents to storage
|
|
21
21
|
CAFace abstract class organisational relay in the class hierarchy
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
storage_to_element / per-obj_type dispatch for element decode (read)
|
|
23
|
+
element_to_storage and encode (write) between surface and storage
|
|
24
24
|
state-compatible / homogeneity + portability registries used by
|
|
25
|
-
state-portable promote_list / CAStack
|
|
25
|
+
state-portable promote_list / CAStack
|
|
26
|
+
memory_view which Faces the MemoryView producer exports
|
|
26
27
|
|
|
27
28
|
---------------------------------------------------------------------------- */
|
|
28
29
|
|
|
29
30
|
#include "ruby.h"
|
|
30
31
|
#include "carray.h"
|
|
31
32
|
#include "ca_obj_face.h"
|
|
33
|
+
#include "carray_internal.h"
|
|
32
34
|
|
|
33
35
|
/* ---- shared op helpers (thin-forward) ---- */
|
|
34
36
|
|
|
@@ -37,7 +39,7 @@ ca_face_attach (void *ap)
|
|
|
37
39
|
{
|
|
38
40
|
CAView *cav = (CAView *) ap;
|
|
39
41
|
CArray *ca = (CArray *) ap;
|
|
40
|
-
ca_attach(cav->parent);
|
|
42
|
+
ca_attach(cav->parent); /* window: a Face's attach slot; its detach slot releases */
|
|
41
43
|
/* alias parent->ptr (Face has identical data layout, so copy is unneeded) */
|
|
42
44
|
ca->ptr = cav->parent->ptr;
|
|
43
45
|
}
|
|
@@ -100,6 +102,7 @@ void
|
|
|
100
102
|
ca_face_xfer_stride (void *ap, ca_size_t *starts, ca_size_t *counts,
|
|
101
103
|
ca_size_t *strides, void *data, int dir)
|
|
102
104
|
{
|
|
105
|
+
if ( ca_region_is_empty(((CArray *) ap)->ndim, counts) ) return;
|
|
103
106
|
CAView *cav = (CAView *) ap;
|
|
104
107
|
ca_xfer_stride(cav->parent, starts, counts, strides, data, dir);
|
|
105
108
|
}
|
|
@@ -129,10 +132,18 @@ rb_ca_face_template (VALUE original_face, CArray *new_parent,
|
|
|
129
132
|
TypedData_Get_Struct(original_face, CArray, &carray_data_type, src);
|
|
130
133
|
tdata = ca_typeddata[src->obj_type];
|
|
131
134
|
|
|
132
|
-
/*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
135
|
+
/* The struct with its subclass tail. struct_size is the struct alone;
|
|
136
|
+
a dsize callback also counts the buffers the struct points to (dim),
|
|
137
|
+
so it is only the fallback for a type registered without struct_size,
|
|
138
|
+
and copying that many bytes reads past the struct. */
|
|
139
|
+
if ( ca_func[src->obj_type].struct_size ) {
|
|
140
|
+
size = ca_func[src->obj_type].struct_size;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
size = (tdata && tdata->function.dsize)
|
|
144
|
+
? tdata->function.dsize(src)
|
|
145
|
+
: sizeof(CAView);
|
|
146
|
+
}
|
|
136
147
|
|
|
137
148
|
dst = (CArray *) xmalloc(size);
|
|
138
149
|
/* CAREFUL: the whole-struct memcpy is how a C-tail Face
|
|
@@ -174,6 +185,44 @@ rb_ca_face_template (VALUE original_face, CArray *new_parent,
|
|
|
174
185
|
dst);
|
|
175
186
|
}
|
|
176
187
|
|
|
188
|
+
VALUE
|
|
189
|
+
ca_face_relift_entity (VALUE face, VALUE result)
|
|
190
|
+
{
|
|
191
|
+
static ID id_copy_state = 0;
|
|
192
|
+
CArray *rc, *lc;
|
|
193
|
+
VALUE lifted;
|
|
194
|
+
if ( id_copy_state == 0 ) id_copy_state = rb_intern("copy_state");
|
|
195
|
+
TypedData_Get_Struct(result, CArray, &carray_data_type, rc);
|
|
196
|
+
lifted = rb_ca_face_template(face, rc, rc->dim);
|
|
197
|
+
rb_ca_set_parent(lifted, result);
|
|
198
|
+
if ( ca_has_mask(rc) ) {
|
|
199
|
+
/* The template starts without a mask. */
|
|
200
|
+
TypedData_Get_Struct(lifted, CArray, &carray_data_type, lc);
|
|
201
|
+
ca_copy_mask(lc, rc);
|
|
202
|
+
}
|
|
203
|
+
if ( rb_respond_to(face, id_copy_state) ) {
|
|
204
|
+
rb_funcall(lifted, id_copy_state, 1, face);
|
|
205
|
+
}
|
|
206
|
+
return lifted;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* A lift moves a view built on a wrapper (a Face or the lazy marker) one
|
|
210
|
+
* level down, onto what the wrapper wraps, so the view no longer keeps the
|
|
211
|
+
* wrapper alive. The view's mask was built over the wrapper's mask, which
|
|
212
|
+
* the wrapper owns and frees with itself; move it down the same level.
|
|
213
|
+
* The wrapper's mask is a same-shape refer of its parent's mask, so the
|
|
214
|
+
* mask view addresses the same cells there. */
|
|
215
|
+
static void
|
|
216
|
+
ca_lift_move_mask_down (CArray *view_ca, CArray *wp)
|
|
217
|
+
{
|
|
218
|
+
CArray *below = ((CAView *) wp)->parent;
|
|
219
|
+
if ( view_ca->mask && wp->mask && ca_is_view(wp->mask)
|
|
220
|
+
&& ((CAView *) wp->mask)->parent == below->mask
|
|
221
|
+
&& ((CAView *) view_ca->mask)->parent == wp->mask ) {
|
|
222
|
+
((CAView *) view_ca->mask)->parent = below->mask;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
177
226
|
/* ca_face_lift(view, face_parent) — at the tail of user-facing entry
|
|
178
227
|
* points (`ca[...]` etc.), when the operand was a Face, re-wrap the
|
|
179
228
|
* resulting view as a Face of the same subclass. Thin wrapper over
|
|
@@ -236,6 +285,7 @@ ca_face_lift (VALUE view, VALUE face_parent)
|
|
|
236
285
|
&& ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
|
|
237
286
|
((CAView *) view_ca)->parent = ((CAView *) fp)->parent; /* C pointer: one level */
|
|
238
287
|
rb_ca_set_parent(view, rb_ca_parent(face_parent)); /* @parent ivar: lockstep */
|
|
288
|
+
ca_lift_move_mask_down(view_ca, fp);
|
|
239
289
|
}
|
|
240
290
|
}
|
|
241
291
|
|
|
@@ -245,6 +295,12 @@ ca_face_lift (VALUE view, VALUE face_parent)
|
|
|
245
295
|
lifted = rb_ca_face_template(face_parent, view_ca, view_ca->dim);
|
|
246
296
|
/* Pin view as a GC root of lifted so the slice is not collected. */
|
|
247
297
|
rb_ca_set_parent(lifted, view);
|
|
298
|
+
/* The Face the view was taken from is off the parent chain now; keep it
|
|
299
|
+
for the attributes set on it. A fresh result the ordering kernels
|
|
300
|
+
re-wrap is not a view of it and takes none. */
|
|
301
|
+
if ( ca_is_view(view_ca) ) {
|
|
302
|
+
rb_ca_set_lifted_from(lifted, face_parent);
|
|
303
|
+
}
|
|
248
304
|
|
|
249
305
|
/* Optional Ruby callback for ivar / state carry. Faces holding
|
|
250
306
|
semantic state in the struct tail (CATime / CATimedelta
|
|
@@ -322,6 +378,7 @@ ca_lazy_marker_lift (VALUE view, VALUE marker)
|
|
|
322
378
|
&& ! ca_test_flag(view_ca, CA_FLAG_MULTI_PARENTS) ) {
|
|
323
379
|
((CAView *) view_ca)->parent = ((CAView *) mp)->parent; /* C pointer */
|
|
324
380
|
rb_ca_set_parent(view, rb_ca_parent(marker)); /* @parent ivar */
|
|
381
|
+
ca_lift_move_mask_down(view_ca, mp);
|
|
325
382
|
}
|
|
326
383
|
|
|
327
384
|
return rb_ca_lazy_marker_new(view);
|
|
@@ -419,37 +476,62 @@ rb_ca_face_s_allocate_forbidden (VALUE klass)
|
|
|
419
476
|
"(CATime / CATimedelta / ...)");
|
|
420
477
|
}
|
|
421
478
|
|
|
422
|
-
|
|
479
|
+
VALUE
|
|
480
|
+
ca_face_storage_value (CArray *ca, VALUE raw)
|
|
481
|
+
{
|
|
482
|
+
CArray *storage;
|
|
483
|
+
CArray as_storage;
|
|
484
|
+
if ( ca->data_type != CA_FIXLEN || TYPE(raw) != T_STRING ) {
|
|
485
|
+
return raw;
|
|
486
|
+
}
|
|
487
|
+
storage = ca_strip_face(ca);
|
|
488
|
+
if ( storage == NULL
|
|
489
|
+
|| storage->data_type == CA_FIXLEN
|
|
490
|
+
|| ca_is_object_type(storage)
|
|
491
|
+
|| RSTRING_LEN(raw) != storage->bytes ) {
|
|
492
|
+
return raw;
|
|
493
|
+
}
|
|
494
|
+
if ( storage->data_type == CA_BOOLEAN ) {
|
|
495
|
+
return *(uint8_t *) RSTRING_PTR(raw) ? Qtrue : Qfalse;
|
|
496
|
+
}
|
|
497
|
+
/* The cell lives in a String's buffer, not in the storage: read it
|
|
498
|
+
through a stand-in that carries only the storage's type. */
|
|
499
|
+
as_storage.data_type = storage->data_type;
|
|
500
|
+
as_storage.bytes = storage->bytes;
|
|
501
|
+
return ca_ptr2obj(&as_storage, RSTRING_PTR(raw));
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/* Face-local C dispatch table for storage_to_element (the read/decode
|
|
423
505
|
direction). Sized to CA_OBJ_TYPE_MAX so any registered obj_type can be
|
|
424
506
|
indexed directly; file-scope zero-init leaves unregistered slots NULL. */
|
|
425
|
-
|
|
507
|
+
ca_face_storage_to_element_fn ca_face_storage_to_element_table[CA_OBJ_TYPE_MAX];
|
|
426
508
|
|
|
427
509
|
void
|
|
428
|
-
|
|
510
|
+
ca_face_register_storage_to_element (int obj_type, ca_face_storage_to_element_fn fn)
|
|
429
511
|
{
|
|
430
512
|
if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
|
|
431
513
|
rb_raise(rb_eArgError,
|
|
432
|
-
"
|
|
514
|
+
"ca_face_register_storage_to_element: obj_type %d out of range",
|
|
433
515
|
obj_type);
|
|
434
516
|
}
|
|
435
|
-
|
|
517
|
+
ca_face_storage_to_element_table[obj_type] = fn;
|
|
436
518
|
}
|
|
437
519
|
|
|
438
|
-
/* Face-local C dispatch table for
|
|
520
|
+
/* Face-local C dispatch table for element_to_storage (the write/encode
|
|
439
521
|
direction), the mirror of the decode table above. Unregistered slots
|
|
440
522
|
stay NULL; a Face without a C fast path relies on the Ruby fallback in
|
|
441
|
-
|
|
442
|
-
|
|
523
|
+
ca_face_element_to_storage (carray_cast.c). */
|
|
524
|
+
ca_face_element_to_storage_fn ca_face_element_to_storage_table[CA_OBJ_TYPE_MAX];
|
|
443
525
|
|
|
444
526
|
void
|
|
445
|
-
|
|
527
|
+
ca_face_register_element_to_storage (int obj_type, ca_face_element_to_storage_fn fn)
|
|
446
528
|
{
|
|
447
529
|
if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
|
|
448
530
|
rb_raise(rb_eArgError,
|
|
449
|
-
"
|
|
531
|
+
"ca_face_register_element_to_storage: obj_type %d out of range",
|
|
450
532
|
obj_type);
|
|
451
533
|
}
|
|
452
|
-
|
|
534
|
+
ca_face_element_to_storage_table[obj_type] = fn;
|
|
453
535
|
}
|
|
454
536
|
|
|
455
537
|
/* -- Face state homogeneity check -- */
|
|
@@ -473,15 +555,10 @@ ca_face_register_state_compatible (int obj_type, ca_face_state_compatible_fn fn)
|
|
|
473
555
|
static int
|
|
474
556
|
ca_face_has_ruby_state_check (VALUE va)
|
|
475
557
|
{
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
558
|
+
ID id_face_state_compatible_p = rb_intern("face_state_compatible?");
|
|
559
|
+
ID id_method = rb_intern("method");
|
|
560
|
+
ID id_owner = rb_intern("owner");
|
|
479
561
|
VALUE m, owner;
|
|
480
|
-
if ( id_face_state_compatible_p == 0 ) {
|
|
481
|
-
id_face_state_compatible_p = rb_intern("face_state_compatible?");
|
|
482
|
-
id_method = rb_intern("method");
|
|
483
|
-
id_owner = rb_intern("owner");
|
|
484
|
-
}
|
|
485
562
|
m = rb_funcall(va, id_method, 1, ID2SYM(id_face_state_compatible_p));
|
|
486
563
|
owner = rb_funcall(m, id_owner, 0);
|
|
487
564
|
return ( owner != rb_cCArray ) ? 1 : 0;
|
|
@@ -524,6 +601,31 @@ rb_ca_face_state_compatible_p (VALUE self, VALUE other)
|
|
|
524
601
|
return ca_face_state_compatible(self, a, other, b) ? Qtrue : Qfalse;
|
|
525
602
|
}
|
|
526
603
|
|
|
604
|
+
/* -- Face MemoryView export -- */
|
|
605
|
+
|
|
606
|
+
/* A Face is exported through MemoryView only when its storage bytes carry
|
|
607
|
+
its values without the Face (a struct record, fixed-width bytes). Zero
|
|
608
|
+
(the default) refuses the export: the consumer would see the storage
|
|
609
|
+
with nothing to say what it means. */
|
|
610
|
+
static int8_t ca_face_memory_view_table[CA_OBJ_TYPE_MAX];
|
|
611
|
+
|
|
612
|
+
void
|
|
613
|
+
ca_face_register_memory_view (int obj_type, int exportable)
|
|
614
|
+
{
|
|
615
|
+
if ( obj_type < 0 || obj_type >= CA_OBJ_TYPE_MAX ) {
|
|
616
|
+
rb_raise(rb_eArgError,
|
|
617
|
+
"ca_face_register_memory_view: obj_type %d out of range",
|
|
618
|
+
obj_type);
|
|
619
|
+
}
|
|
620
|
+
ca_face_memory_view_table[obj_type] = exportable ? 1 : 0;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
int
|
|
624
|
+
ca_face_memory_view_exportable (CArray *ca)
|
|
625
|
+
{
|
|
626
|
+
return ca_face_memory_view_table[ca->obj_type] == 1;
|
|
627
|
+
}
|
|
628
|
+
|
|
527
629
|
/* -- Face state portability -- */
|
|
528
630
|
|
|
529
631
|
/* int8_t per slot to distinguish "unregistered" (-1, dispatch falls
|
|
@@ -576,16 +678,11 @@ ca_face_register_state_portable (int obj_type, int portable)
|
|
|
576
678
|
static int
|
|
577
679
|
ca_face_class_has_portable_method (VALUE klass)
|
|
578
680
|
{
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
681
|
+
ID id_face_state_portable_p = rb_intern("face_state_portable?");
|
|
682
|
+
ID id_method = rb_intern("method");
|
|
683
|
+
ID id_owner = rb_intern("owner");
|
|
582
684
|
VALUE m, owner;
|
|
583
685
|
if ( NIL_P(klass) ) return 0;
|
|
584
|
-
if ( id_face_state_portable_p == 0 ) {
|
|
585
|
-
id_face_state_portable_p = rb_intern("face_state_portable?");
|
|
586
|
-
id_method = rb_intern("method");
|
|
587
|
-
id_owner = rb_intern("owner");
|
|
588
|
-
}
|
|
589
686
|
if ( !rb_respond_to(klass, id_face_state_portable_p) ) return 0;
|
|
590
687
|
m = rb_funcall(klass, id_method, 1, ID2SYM(id_face_state_portable_p));
|
|
591
688
|
owner = rb_funcall(m, id_owner, 0);
|
|
@@ -595,6 +692,132 @@ ca_face_class_has_portable_method (VALUE klass)
|
|
|
595
692
|
return ( owner != rb_singleton_class(rb_cCArray) ) ? 1 : 0;
|
|
596
693
|
}
|
|
597
694
|
|
|
695
|
+
/* The conversion a refusal of a Face whose cells encode its values points
|
|
696
|
+
at: a string Face gives its strings with #to_string, any other its values
|
|
697
|
+
with to_type(:object). */
|
|
698
|
+
const char *
|
|
699
|
+
ca_face_conversion_hint (VALUE face)
|
|
700
|
+
{
|
|
701
|
+
return rb_respond_to(face, rb_intern("to_string"))
|
|
702
|
+
? "#to_string gives its strings"
|
|
703
|
+
: "to_type(:object) gives its values";
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/* The method that gives the storage under a Face, for a refusal to point
|
|
707
|
+
at: #ticks for a time or a duration, #codes for a categorical. NULL for
|
|
708
|
+
a Face that names its storage some other way, or not at all. */
|
|
709
|
+
const char *
|
|
710
|
+
ca_face_storage_method (VALUE face)
|
|
711
|
+
{
|
|
712
|
+
static const char *names[] = { "ticks", "codes" };
|
|
713
|
+
size_t i;
|
|
714
|
+
for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
|
|
715
|
+
if ( rb_respond_to(face, rb_intern(names[i])) ) {
|
|
716
|
+
return names[i];
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
return NULL;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/* ca.face_storage_method -- the name of the method that gives the storage
|
|
723
|
+
under ca (ca_face_storage_method), as a String, or nil. For refusals
|
|
724
|
+
written in Ruby. */
|
|
725
|
+
static VALUE
|
|
726
|
+
rb_ca_face_storage_method (VALUE self)
|
|
727
|
+
{
|
|
728
|
+
const char *name = ca_face_storage_method(self);
|
|
729
|
+
return name ? rb_str_new_cstr(name) : Qnil;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/* Refuse to order (or, with verb "count", to count by value) a Face whose
|
|
733
|
+
storage does not hold its values in order, pointing at what does: its
|
|
734
|
+
values, or its storage when that is what is meant. */
|
|
735
|
+
void
|
|
736
|
+
ca_face_refuse_order (const char *name, VALUE face, const char *verb)
|
|
737
|
+
{
|
|
738
|
+
const char *storage = ca_face_storage_method(face);
|
|
739
|
+
if ( storage ) {
|
|
740
|
+
rb_raise(rb_eArgError,
|
|
741
|
+
"%s: %s is not orderable by storage (a cell encodes the value "
|
|
742
|
+
"rather than being it); %s its values instead -- %s -- or %s "
|
|
743
|
+
"its storage, which #%s gives",
|
|
744
|
+
name, rb_obj_classname(face), verb,
|
|
745
|
+
ca_face_conversion_hint(face), verb, storage);
|
|
746
|
+
}
|
|
747
|
+
rb_raise(rb_eArgError,
|
|
748
|
+
"%s: %s is not orderable by storage (a cell encodes the value "
|
|
749
|
+
"rather than being it); %s its values instead -- %s",
|
|
750
|
+
name, rb_obj_classname(face), verb,
|
|
751
|
+
ca_face_conversion_hint(face));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/* The rule every family that orders, compares, searches or hashes the cells
|
|
755
|
+
of a Face reference keeps: the storage stands for the values only when the
|
|
756
|
+
Face declares ORDERABLE_STORAGE. A fixlen storage is no exception -- its
|
|
757
|
+
bytes are an order only when the Face says so (CAFixlenString). Returns
|
|
758
|
+
when the Face is ORDERABLE; refuses otherwise (ca_face_refuse_order). A
|
|
759
|
+
family that can order by a key asks ca_face_orders_by_itself first. */
|
|
760
|
+
void
|
|
761
|
+
ca_face_require_orderable (VALUE face, const char *name, const char *verb)
|
|
762
|
+
{
|
|
763
|
+
CArray *ca;
|
|
764
|
+
TypedData_Get_Struct(face, CArray, &carray_data_type, ca);
|
|
765
|
+
if ( ! ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
|
|
766
|
+
ca_face_refuse_order(name, face, verb);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/* Whether a Face is ordered by its own #order rather than by its storage:
|
|
771
|
+
it is not ORDERABLE and answers #order itself (CAConstString, CARecord),
|
|
772
|
+
so a family that orders by a key can take that order. CArray#order is
|
|
773
|
+
not one: it sorts the storage and comes back to the gate. */
|
|
774
|
+
int
|
|
775
|
+
ca_face_orders_by_itself (VALUE face)
|
|
776
|
+
{
|
|
777
|
+
ID id_order = rb_intern("order");
|
|
778
|
+
ID id_method = rb_intern("method");
|
|
779
|
+
ID id_owner = rb_intern("owner");
|
|
780
|
+
CArray *ca;
|
|
781
|
+
VALUE m;
|
|
782
|
+
TypedData_Get_Struct(face, CArray, &carray_data_type, ca);
|
|
783
|
+
if ( ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
|
|
784
|
+
return 0;
|
|
785
|
+
}
|
|
786
|
+
m = rb_funcall(face, id_method, 1, ID2SYM(id_order));
|
|
787
|
+
return ( rb_funcall(m, id_owner, 0) != rb_cCArray ) ? 1 : 0;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/* Refuse a numeric operation (arithmetic, math, a reduction) on a Face,
|
|
791
|
+
whose cells encode its values rather than being numbers, pointing at what
|
|
792
|
+
gives numbers. Returns when `self` is not a Face. */
|
|
793
|
+
void
|
|
794
|
+
ca_face_refuse_numeric (VALUE self, const char *name, VALUE eclass)
|
|
795
|
+
{
|
|
796
|
+
CArray *ca;
|
|
797
|
+
if ( ! rb_obj_is_carray(self) ) {
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
GetCArray(self, ca);
|
|
801
|
+
if ( ! ca_is_face(ca) ) {
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
804
|
+
{
|
|
805
|
+
const char *storage = ca_face_storage_method(self);
|
|
806
|
+
if ( storage ) {
|
|
807
|
+
rb_raise(eclass,
|
|
808
|
+
"%s: %s does not define it (a cell encodes the value rather "
|
|
809
|
+
"than being a number); compute on its values instead -- %s -- "
|
|
810
|
+
"or on its storage, which #%s gives",
|
|
811
|
+
name, rb_obj_classname(self), ca_face_conversion_hint(self),
|
|
812
|
+
storage);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
rb_raise(eclass,
|
|
816
|
+
"%s: %s does not define it (a cell encodes the value rather than "
|
|
817
|
+
"being a number); compute on its values instead -- %s",
|
|
818
|
+
name, rb_obj_classname(self), ca_face_conversion_hint(self));
|
|
819
|
+
}
|
|
820
|
+
|
|
598
821
|
VALUE
|
|
599
822
|
ca_face_operand_descend (VALUE operand, const char *name)
|
|
600
823
|
{
|
|
@@ -609,16 +832,203 @@ ca_face_operand_descend (VALUE operand, const char *name)
|
|
|
609
832
|
if ( ca_test_flag(op, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
|
|
610
833
|
return rb_ca_strip_face_value(operand);
|
|
611
834
|
}
|
|
835
|
+
{
|
|
836
|
+
const char *storage = ca_face_storage_method(operand);
|
|
837
|
+
if ( storage ) {
|
|
838
|
+
rb_raise(rb_eArgError,
|
|
839
|
+
"%s: cannot take a %s operand: its storage is not its surface "
|
|
840
|
+
"(a cell encodes the value rather than being it), so comparing "
|
|
841
|
+
"the storage would compare the encoding. Convert the operand "
|
|
842
|
+
"to the receiver's space first -- %s -- or compare the storage "
|
|
843
|
+
"of both sides, which #%s gives",
|
|
844
|
+
name, rb_obj_classname(operand),
|
|
845
|
+
ca_face_conversion_hint(operand), storage);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
612
848
|
rb_raise(rb_eArgError,
|
|
613
849
|
"%s: cannot take a %s operand: its storage is not its surface "
|
|
614
850
|
"(a cell encodes the value rather than being it), so comparing "
|
|
615
851
|
"the storage would compare the encoding. Convert the operand to "
|
|
616
|
-
"the receiver's space first --
|
|
617
|
-
|
|
618
|
-
|
|
852
|
+
"the receiver's space first -- %s",
|
|
853
|
+
name, rb_obj_classname(operand), ca_face_conversion_hint(operand));
|
|
854
|
+
return Qnil; /* not reached */
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/* Whether two arrays, at least one a Face other than a record array, are
|
|
858
|
+
equal (`==`, strict = 0) or the same key (`eql?`, strict = 1).
|
|
859
|
+
|
|
860
|
+
A Face equals only a Face of its own class. When the two share their
|
|
861
|
+
state (a time array's unit) and the storage holds the values in their
|
|
862
|
+
order, equal storage is equal values, so the storage is compared.
|
|
863
|
+
Otherwise `==` compares the values themselves (two time arrays of
|
|
864
|
+
different units, two const strings over different pools, two
|
|
865
|
+
categoricals with differently ordered labels), and `eql?`, which has to
|
|
866
|
+
agree with #hash, answers false. */
|
|
867
|
+
int
|
|
868
|
+
ca_face_equal (VALUE va, VALUE vb, int strict)
|
|
869
|
+
{
|
|
870
|
+
CArray *a, *b;
|
|
871
|
+
int8_t i;
|
|
872
|
+
TypedData_Get_Struct(va, CArray, &carray_data_type, a);
|
|
873
|
+
TypedData_Get_Struct(vb, CArray, &carray_data_type, b);
|
|
874
|
+
if ( ! ca_is_face(a) || ! ca_is_face(b) ) {
|
|
875
|
+
return 0;
|
|
876
|
+
}
|
|
877
|
+
if ( rb_obj_class(va) != rb_obj_class(vb) ) {
|
|
878
|
+
return 0;
|
|
879
|
+
}
|
|
880
|
+
if ( a->ndim != b->ndim ) {
|
|
881
|
+
return 0;
|
|
882
|
+
}
|
|
883
|
+
for (i = 0; i < a->ndim; i++) {
|
|
884
|
+
if ( a->dim[i] != b->dim[i] ) {
|
|
885
|
+
return 0;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
if ( ca_face_state_compatible(va, a, vb, b)
|
|
889
|
+
&& ( strict || ca_test_flag(a, CA_FLAG_FACE_ORDERABLE_STORAGE) ) ) {
|
|
890
|
+
return strict ? ca_eql_storage(ca_strip_face(a), ca_strip_face(b))
|
|
891
|
+
: ca_equal(ca_strip_face(a), ca_strip_face(b));
|
|
892
|
+
}
|
|
893
|
+
if ( strict ) {
|
|
894
|
+
return 0;
|
|
895
|
+
}
|
|
896
|
+
return RTEST(rb_equal(rb_funcall(va, rb_intern("to_a"), 0),
|
|
897
|
+
rb_funcall(vb, rb_intern("to_a"), 0)));
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/* An operand Face whose storage is not even in the order of its values
|
|
901
|
+
(a CAConstString, a CACategorical) is read by its values: its strings
|
|
902
|
+
(#to_string) or, for any other, its values (to_type(:object)). Any
|
|
903
|
+
other operand is returned as it is. */
|
|
904
|
+
static VALUE
|
|
905
|
+
ca_face_operand_values (VALUE operand)
|
|
906
|
+
{
|
|
907
|
+
CArray *op;
|
|
908
|
+
if ( ! RTEST(rb_obj_is_carray(operand)) ) {
|
|
909
|
+
return operand;
|
|
910
|
+
}
|
|
911
|
+
TypedData_Get_Struct(operand, CArray, &carray_data_type, op);
|
|
912
|
+
if ( ! ca_is_face(op) || op->obj_type == CA_OBJ_RECORD
|
|
913
|
+
|| ca_test_flag(op, CA_FLAG_FACE_ORDERABLE_STORAGE) ) {
|
|
914
|
+
return operand;
|
|
915
|
+
}
|
|
916
|
+
if ( rb_respond_to(operand, rb_intern("to_string")) ) {
|
|
917
|
+
return rb_funcall(operand, rb_intern("to_string"), 0);
|
|
918
|
+
}
|
|
919
|
+
return rb_funcall(operand, rb_intern("to_type"), 1, INT2NUM(CA_OBJECT));
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/* Bring `operand` into the storage space of `reference`, a Face, so the
|
|
923
|
+
two can be compared, searched, counted or hashed cell by cell. Every
|
|
924
|
+
family that takes an operand against a Face goes through here; the
|
|
925
|
+
family keeps its own gate on the reference (ORDERABLE, a record's own
|
|
926
|
+
rules) and its own handling of a scalar operand afterwards.
|
|
927
|
+
|
|
928
|
+
- A record reference takes records of its own data_class (stripped to
|
|
929
|
+
their bytes) and plain operands as they are; any other Face operand is
|
|
930
|
+
refused with TypeError.
|
|
931
|
+
- A COMPARABLE reference stores its values as they are, so a plain
|
|
932
|
+
operand is taken as it is, a Face whose cells are its values is
|
|
933
|
+
stripped, a string or categorical Face is read by its values, and a
|
|
934
|
+
Face whose cells encode its values in order (a time) is refused
|
|
935
|
+
(ca_face_operand_descend).
|
|
936
|
+
- A reference whose storage is not in the order of its values refuses,
|
|
937
|
+
naming the conversion that gives its values.
|
|
938
|
+
- A reference with to_comparable brings any operand into its own space
|
|
939
|
+
(a unit, an instant), and refuses what it cannot convert.
|
|
940
|
+
- Any other reference has no way to read the operand in its space, and
|
|
941
|
+
refuses it.
|
|
942
|
+
UNDEF is undefined in every space, so it passes as it is and the entry
|
|
943
|
+
answers it as it does for a plain array. */
|
|
944
|
+
VALUE
|
|
945
|
+
ca_face_reconcile_operand (VALUE reference, VALUE operand, const char *name)
|
|
946
|
+
{
|
|
947
|
+
CArray *ca;
|
|
948
|
+
if ( operand == CA_UNDEF ) {
|
|
949
|
+
return operand;
|
|
950
|
+
}
|
|
951
|
+
TypedData_Get_Struct(reference, CArray, &carray_data_type, ca);
|
|
952
|
+
if ( ca->obj_type == CA_OBJ_RECORD ) {
|
|
953
|
+
if ( RTEST(rb_obj_is_carray(operand)) ) {
|
|
954
|
+
CArray *op;
|
|
955
|
+
TypedData_Get_Struct(operand, CArray, &carray_data_type, op);
|
|
956
|
+
if ( ca_is_face(op) ) {
|
|
957
|
+
if ( op->obj_type != CA_OBJ_RECORD ) {
|
|
958
|
+
rb_raise(rb_eTypeError,
|
|
959
|
+
"%s: can not take a %s operand for a record array of "
|
|
960
|
+
"%"PRIsVALUE, name, rb_obj_classname(operand),
|
|
961
|
+
rb_ca_data_class(reference));
|
|
962
|
+
}
|
|
963
|
+
if ( rb_ca_data_class(operand) != rb_ca_data_class(reference) ) {
|
|
964
|
+
rb_raise(rb_eTypeError,
|
|
965
|
+
"%s: can not take a record array of %"PRIsVALUE
|
|
966
|
+
" for a record array of %"PRIsVALUE, name,
|
|
967
|
+
rb_ca_data_class(operand), rb_ca_data_class(reference));
|
|
968
|
+
}
|
|
969
|
+
return rb_ca_strip_face_value(operand);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return operand;
|
|
973
|
+
}
|
|
974
|
+
if ( ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ) {
|
|
975
|
+
return ca_face_operand_descend(ca_face_operand_values(operand), name);
|
|
976
|
+
}
|
|
977
|
+
ca_face_require_orderable(reference, name, "compare");
|
|
978
|
+
if ( rb_respond_to(reference, rb_intern("to_comparable")) ) {
|
|
979
|
+
return rb_ca_strip_face_value(rb_funcall(reference,
|
|
980
|
+
rb_intern("to_comparable"),
|
|
981
|
+
1, operand));
|
|
982
|
+
}
|
|
983
|
+
{
|
|
984
|
+
const char *storage = ca_face_storage_method(reference);
|
|
985
|
+
if ( storage ) {
|
|
986
|
+
rb_raise(rb_eArgError,
|
|
987
|
+
"%s: %s is not comparable by storage and has no to_comparable "
|
|
988
|
+
"to bring the operand into its space; compare the storage of "
|
|
989
|
+
"both sides, which #%s gives",
|
|
990
|
+
name, rb_obj_classname(reference), storage);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
rb_raise(rb_eArgError,
|
|
994
|
+
"%s: %s is not comparable by storage and has no to_comparable "
|
|
995
|
+
"to bring the operand into its space",
|
|
996
|
+
name, rb_obj_classname(reference));
|
|
619
997
|
return Qnil; /* not reached */
|
|
620
998
|
}
|
|
621
999
|
|
|
1000
|
+
/* The reference and the operand of a comparison (an operator, count(v), a
|
|
1001
|
+
search, linear_section) brought into one space. A time reference answers
|
|
1002
|
+
both through comparable_pair: it keeps itself when the operand sits on its
|
|
1003
|
+
grid, and otherwise meets the operand in the finer unit the two share,
|
|
1004
|
+
where to_comparable would refuse a value off its grid. Every other
|
|
1005
|
+
reference keeps itself and reconciles the operand
|
|
1006
|
+
(ca_face_reconcile_operand). An assignment, which has to land on the
|
|
1007
|
+
reference's grid, uses ca_face_reconcile_operand. */
|
|
1008
|
+
void
|
|
1009
|
+
ca_face_reconcile_pair (volatile VALUE *pref, volatile VALUE *poperand,
|
|
1010
|
+
const char *name)
|
|
1011
|
+
{
|
|
1012
|
+
static ID id_comparable_pair = 0;
|
|
1013
|
+
VALUE ref = *pref, operand = *poperand;
|
|
1014
|
+
CArray *ca;
|
|
1015
|
+
if ( id_comparable_pair == 0 ) {
|
|
1016
|
+
id_comparable_pair = rb_intern("comparable_pair");
|
|
1017
|
+
}
|
|
1018
|
+
TypedData_Get_Struct(ref, CArray, &carray_data_type, ca);
|
|
1019
|
+
if ( operand != CA_UNDEF
|
|
1020
|
+
&& ca->obj_type != CA_OBJ_RECORD
|
|
1021
|
+
&& ! ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE)
|
|
1022
|
+
&& ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE)
|
|
1023
|
+
&& rb_respond_to(ref, id_comparable_pair) ) {
|
|
1024
|
+
VALUE pair = rb_funcall(ref, id_comparable_pair, 1, operand);
|
|
1025
|
+
*pref = rb_ary_entry(pair, 0);
|
|
1026
|
+
*poperand = rb_ca_strip_face_value(rb_ary_entry(pair, 1));
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
*poperand = ca_face_reconcile_operand(ref, operand, name);
|
|
1030
|
+
}
|
|
1031
|
+
|
|
622
1032
|
int
|
|
623
1033
|
ca_face_state_portable (int obj_type, VALUE klass)
|
|
624
1034
|
{
|
|
@@ -635,6 +1045,84 @@ ca_face_state_portable (int obj_type, VALUE klass)
|
|
|
635
1045
|
return 1; /* default portable */
|
|
636
1046
|
}
|
|
637
1047
|
|
|
1048
|
+
VALUE
|
|
1049
|
+
ca_face_list_class (VALUE list, const char *who)
|
|
1050
|
+
{
|
|
1051
|
+
long n = RARRAY_LEN(list), i;
|
|
1052
|
+
int any_face = 0, all_face = 1;
|
|
1053
|
+
VALUE face_class = Qnil, face = Qnil;
|
|
1054
|
+
CArray *ref = NULL;
|
|
1055
|
+
|
|
1056
|
+
for ( i = 0; i < n; i++ ) {
|
|
1057
|
+
VALUE p = rb_ary_entry(list, i);
|
|
1058
|
+
CArray *ca;
|
|
1059
|
+
rb_check_carray_object(p);
|
|
1060
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
|
|
1061
|
+
if ( ! ca_is_face(ca) ) { all_face = 0; continue; }
|
|
1062
|
+
any_face = 1;
|
|
1063
|
+
if ( NIL_P(face_class) ) {
|
|
1064
|
+
face_class = rb_obj_class(p);
|
|
1065
|
+
face = p;
|
|
1066
|
+
ref = ca;
|
|
1067
|
+
}
|
|
1068
|
+
else if ( rb_obj_class(p) != face_class ) {
|
|
1069
|
+
all_face = 0;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
if ( ! any_face ) return Qnil;
|
|
1073
|
+
if ( ! all_face ) {
|
|
1074
|
+
const char *storage = ca_face_storage_method(face);
|
|
1075
|
+
if ( ref->obj_type == CA_OBJ_STRING ) {
|
|
1076
|
+
rb_raise(rb_eArgError,
|
|
1077
|
+
"%s: cannot mix Face and non-Face arrays, or arrays of "
|
|
1078
|
+
"different Face classes; convert them first to one Face "
|
|
1079
|
+
"class or to no Face (CArray.string(a) makes a CAString of "
|
|
1080
|
+
"an object array of Strings)",
|
|
1081
|
+
who);
|
|
1082
|
+
}
|
|
1083
|
+
if ( storage ) {
|
|
1084
|
+
rb_raise(rb_eArgError,
|
|
1085
|
+
"%s: cannot mix Face and non-Face arrays, or arrays of "
|
|
1086
|
+
"different Face classes; convert them first to one Face "
|
|
1087
|
+
"class or to no Face, or combine their storage (#%s gives "
|
|
1088
|
+
"the storage of a %s)",
|
|
1089
|
+
who, storage, rb_obj_classname(face));
|
|
1090
|
+
}
|
|
1091
|
+
rb_raise(rb_eArgError,
|
|
1092
|
+
"%s: cannot mix Face and non-Face arrays, or arrays of "
|
|
1093
|
+
"different Face classes; convert them first to one Face "
|
|
1094
|
+
"class or to no Face",
|
|
1095
|
+
who);
|
|
1096
|
+
}
|
|
1097
|
+
if ( n > 1 && ! ca_face_state_portable(ref->obj_type, face_class) ) {
|
|
1098
|
+
rb_raise(rb_eArgError,
|
|
1099
|
+
"%s: %s state is not portable across several arrays (each "
|
|
1100
|
+
"keeps its values in storage of its own); convert them first "
|
|
1101
|
+
"(%s)",
|
|
1102
|
+
who, rb_class2name(face_class),
|
|
1103
|
+
ca_face_conversion_hint(face));
|
|
1104
|
+
}
|
|
1105
|
+
for ( i = 1; i < n; i++ ) {
|
|
1106
|
+
VALUE p = rb_ary_entry(list, i);
|
|
1107
|
+
CArray *ca;
|
|
1108
|
+
TypedData_Get_Struct(p, CArray, &carray_data_type, ca);
|
|
1109
|
+
if ( ! ca_face_state_compatible(rb_ary_entry(list, 0), ref, p, ca) ) {
|
|
1110
|
+
rb_raise(rb_eArgError,
|
|
1111
|
+
"%s: Face state mismatch (the %s at index %ld differs from "
|
|
1112
|
+
"index 0)", who, rb_class2name(face_class), i);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
return face_class;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
VALUE
|
|
1119
|
+
ca_face_strip_one (VALUE v)
|
|
1120
|
+
{
|
|
1121
|
+
CArray *c;
|
|
1122
|
+
TypedData_Get_Struct(v, CArray, &carray_data_type, c);
|
|
1123
|
+
return ca_is_face(c) ? rb_ca_parent(v) : v;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
638
1126
|
/* CArray.face_state_portable? — class method on CArray. Subclasses
|
|
639
1127
|
(Face classes) override by defining their own `def self.face_state_portable?`.
|
|
640
1128
|
This default returns true (= 1) for non-Face CArray classes; the actual
|
|
@@ -645,6 +1133,57 @@ rb_ca_s_face_state_portable_p (VALUE klass)
|
|
|
645
1133
|
return Qtrue;
|
|
646
1134
|
}
|
|
647
1135
|
|
|
1136
|
+
/* ca.orderable_storage? -- whether ca's storage holds its values, in
|
|
1137
|
+
their order, so that a lookup may compare the storage: true for an array
|
|
1138
|
+
that is not a Face and for a Face that declares
|
|
1139
|
+
CA_FLAG_FACE_ORDERABLE_STORAGE, false for a Face whose cells encode the
|
|
1140
|
+
value (a const string's ranges, a categorical's codes). */
|
|
1141
|
+
static VALUE
|
|
1142
|
+
rb_ca_orderable_storage_p (VALUE self)
|
|
1143
|
+
{
|
|
1144
|
+
CArray *ca;
|
|
1145
|
+
GetCArray(self, ca);
|
|
1146
|
+
if ( ! ca_is_face(ca) ) return Qtrue;
|
|
1147
|
+
return ca_test_flag(ca, CA_FLAG_FACE_ORDERABLE_STORAGE) ? Qtrue : Qfalse;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
/* ca.comparable_storage? -- whether ca's storage holds its values as
|
|
1151
|
+
they are, so that a plain operand may be compared with the storage: true
|
|
1152
|
+
for an array that is not a Face and for a Face that declares
|
|
1153
|
+
CA_FLAG_FACE_COMPARABLE_STORAGE. */
|
|
1154
|
+
static VALUE
|
|
1155
|
+
rb_ca_comparable_storage_p (VALUE self)
|
|
1156
|
+
{
|
|
1157
|
+
CArray *ca;
|
|
1158
|
+
GetCArray(self, ca);
|
|
1159
|
+
if ( ! ca_is_face(ca) ) return Qtrue;
|
|
1160
|
+
return ca_test_flag(ca, CA_FLAG_FACE_COMPARABLE_STORAGE) ? Qtrue : Qfalse;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
/* ca.face_storage -- the storage under a Face (ca itself when it is not
|
|
1164
|
+
a Face). */
|
|
1165
|
+
static VALUE
|
|
1166
|
+
rb_ca_face_storage (VALUE self)
|
|
1167
|
+
{
|
|
1168
|
+
return rb_ca_strip_face_value(self);
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/* ca.reconcile_operand(operand, name) -- operand brought into ca's
|
|
1172
|
+
storage space by the rule every operand family follows
|
|
1173
|
+
(ca_face_reconcile_operand for a Face, ca_face_operand_descend for an
|
|
1174
|
+
array that is not one). For the families written in Ruby. */
|
|
1175
|
+
static VALUE
|
|
1176
|
+
rb_ca_reconcile_operand (VALUE self, VALUE operand, VALUE rname)
|
|
1177
|
+
{
|
|
1178
|
+
CArray *ca;
|
|
1179
|
+
const char *name = StringValueCStr(rname);
|
|
1180
|
+
GetCArray(self, ca);
|
|
1181
|
+
if ( ca_is_face(ca) ) {
|
|
1182
|
+
return ca_face_reconcile_operand(self, operand, name);
|
|
1183
|
+
}
|
|
1184
|
+
return ca_face_operand_descend(operand, name);
|
|
1185
|
+
}
|
|
1186
|
+
|
|
648
1187
|
/* CArray#face_lift(face_parent) — Ruby surface for ca_face_lift.
|
|
649
1188
|
Used by CArray.stack to re-wrap a raw CAStack with the homogeneous
|
|
650
1189
|
Face class carried by face_parent (typically list[0] after the
|
|
@@ -666,21 +1205,19 @@ rb_ca_face_lift_method (VALUE self, VALUE face_parent)
|
|
|
666
1205
|
|
|
667
1206
|
/* ca_face_reconcile_comparison(pself, pother) — Face gate for the element-
|
|
668
1207
|
* wise comparison operators (< <= > >=, eq, ne; <=> composes from > and <).
|
|
669
|
-
* Mirrors the search query gate (
|
|
1208
|
+
* Mirrors the search query gate (docs/authoring/FaceOrderingSearch.md): when the LHS
|
|
670
1209
|
* is a Face over NUMERIC storage, memcmp on the surface fixlen bytes would
|
|
671
1210
|
* mis-order the storage, so descend to storage (ORDERABLE licenses that the
|
|
672
1211
|
* numeric order equals the surface order) and reconcile the RHS:
|
|
673
1212
|
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
*
|
|
677
|
-
* - RHS is plain (bare number / non-Face array / Scalar): a direct storage
|
|
678
|
-
* compare, which only a COMPARABLE self licenses -- a non-COMPARABLE
|
|
679
|
-
* Face (e.g. a unit-bearing CATime) rejects it.
|
|
1213
|
+
* the RHS goes through ca_face_reconcile_operand: a COMPARABLE self takes
|
|
1214
|
+
* a plain RHS or a COMPARABLE Face, a self with to_comparable brings any
|
|
1215
|
+
* RHS into its space, and anything else is refused.
|
|
680
1216
|
*
|
|
681
|
-
* A Face over FIXLEN storage (CARecord / string faces)
|
|
1217
|
+
* A Face over FIXLEN storage (CARecord / string faces) keeps its Face:
|
|
682
1218
|
* memcmp on its bytes is already the correct order/equality, same as a plain
|
|
683
|
-
* fixlen array. Both operands are rebound
|
|
1219
|
+
* fixlen array; only the RHS is reconciled. Both operands are rebound via
|
|
1220
|
+
* *pself / *pother.
|
|
684
1221
|
* No-op when self is not a Face. */
|
|
685
1222
|
void
|
|
686
1223
|
ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
|
|
@@ -698,54 +1235,30 @@ ca_face_reconcile_comparison (volatile VALUE *pself, volatile VALUE *pother)
|
|
|
698
1235
|
|
|
699
1236
|
storage = ca_strip_face(sc);
|
|
700
1237
|
if ( storage->data_type == CA_FIXLEN ) {
|
|
701
|
-
/* genuine fixlen semantics: memcmp is the correct comparison,
|
|
702
|
-
|
|
1238
|
+
/* genuine fixlen semantics: memcmp is the correct comparison, so self
|
|
1239
|
+
keeps its Face (same result as a plain fixlen array) and only the
|
|
1240
|
+
operand is brought to bytes of the same meaning. */
|
|
1241
|
+
*pother = ca_face_reconcile_operand(self, other, "comparison");
|
|
703
1242
|
return;
|
|
704
1243
|
}
|
|
705
1244
|
|
|
706
|
-
|
|
707
|
-
rb_raise(rb_eArgError,
|
|
708
|
-
"comparison: Face operand (%s) is not orderable by storage; "
|
|
709
|
-
"use ca.parent to descend to storage",
|
|
710
|
-
rb_obj_classname(self));
|
|
711
|
-
}
|
|
1245
|
+
ca_face_require_orderable(self, "comparison", "compare");
|
|
712
1246
|
|
|
713
1247
|
{
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
int
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
/* directly comparable Face reference: strip a Face operand, take a
|
|
726
|
-
plain operand as-is (storage compare is licensed). */
|
|
727
|
-
if ( other_is_face ) {
|
|
728
|
-
other = rb_ca_strip_face_value(other);
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
else if ( rb_respond_to(self_ref, rb_intern("to_comparable")) ) {
|
|
732
|
-
/* non-COMPARABLE Face reference: the reference brings the operand into
|
|
733
|
-
its own space (unit alignment / instant lift), whatever the operand
|
|
734
|
-
type -- Face CArray, our Scalar, a Ruby Time / DateTime, etc. The
|
|
735
|
-
reference's to_comparable raises if it cannot reconcile it. */
|
|
736
|
-
other = rb_funcall(self_ref, rb_intern("to_comparable"), 1, other);
|
|
737
|
-
other = rb_ca_strip_face_value(other);
|
|
1248
|
+
/* A Ruby Array is a list of values (to_comparable reads it cell by
|
|
1249
|
+
cell), not one value, so it keeps the length it was given. */
|
|
1250
|
+
int other_was_scalar = ! RTEST(rb_obj_is_carray(other))
|
|
1251
|
+
&& ! RB_TYPE_P(other, T_ARRAY);
|
|
1252
|
+
volatile VALUE ref = self, op = other;
|
|
1253
|
+
ca_face_reconcile_pair(&ref, &op, "comparison");
|
|
1254
|
+
/* The reconcile lifts a scalar operand to a length-1 array; unwrap it
|
|
1255
|
+
so the comparison stays array-vs-scalar instead of a shape check
|
|
1256
|
+
against [1] (which only a 1-D reference would pass). */
|
|
1257
|
+
if ( other_was_scalar && RTEST(rb_obj_is_carray(op)) ) {
|
|
1258
|
+
op = rb_ca_fetch_addr(op, 0);
|
|
738
1259
|
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
"comparison: non-comparable Face reference (%s) has no "
|
|
742
|
-
"to_comparable to reconcile the operand; use ca.parent to "
|
|
743
|
-
"compare the hidden storage explicitly",
|
|
744
|
-
rb_obj_classname(self_ref));
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
*pself = rb_ca_strip_face_value(self);
|
|
748
|
-
*pother = other;
|
|
1260
|
+
*pself = rb_ca_strip_face_value(ref);
|
|
1261
|
+
*pother = op;
|
|
749
1262
|
}
|
|
750
1263
|
}
|
|
751
1264
|
|
|
@@ -771,4 +1284,14 @@ Init_ca_face (void)
|
|
|
771
1284
|
rb_define_singleton_method(rb_cCArray, "face_state_portable?",
|
|
772
1285
|
rb_ca_s_face_state_portable_p, 0);
|
|
773
1286
|
rb_define_method(rb_cCArray, "face_lift", rb_ca_face_lift_method, 1);
|
|
1287
|
+
rb_define_private_method(rb_cCArray, "orderable_storage?",
|
|
1288
|
+
rb_ca_orderable_storage_p, 0);
|
|
1289
|
+
rb_define_private_method(rb_cCArray, "comparable_storage?",
|
|
1290
|
+
rb_ca_comparable_storage_p, 0);
|
|
1291
|
+
rb_define_private_method(rb_cCArray, "face_storage",
|
|
1292
|
+
rb_ca_face_storage, 0);
|
|
1293
|
+
rb_define_private_method(rb_cCArray, "reconcile_operand",
|
|
1294
|
+
rb_ca_reconcile_operand, 2);
|
|
1295
|
+
rb_define_private_method(rb_cCArray, "face_storage_method",
|
|
1296
|
+
rb_ca_face_storage_method, 0);
|
|
774
1297
|
}
|