carray 3.0.3 → 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 +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- 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/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/lib/carray/serialize.rb
CHANGED
|
@@ -63,6 +63,7 @@ class CArray::Serializer # :nodoc:
|
|
|
63
63
|
|
|
64
64
|
# @!visibility private
|
|
65
65
|
MAGIC = "_CARRAY3"
|
|
66
|
+
Ractor.make_shareable(MAGIC)
|
|
66
67
|
# @!visibility private
|
|
67
68
|
HEADER_BYTES = 256
|
|
68
69
|
# @!visibility private
|
|
@@ -119,7 +120,8 @@ class CArray::Serializer # :nodoc:
|
|
|
119
120
|
# `Marshal.dump(ca)` instead), or carries a data_class the
|
|
120
121
|
# v1.0 flat-primitive schema cannot express.
|
|
121
122
|
# @raise [TypeError] when `ca` is a Face other than a record (save
|
|
122
|
-
# its storage
|
|
123
|
+
# its storage through the Face's own accessor, such as `#ticks` or
|
|
124
|
+
# `#codes`, or its values with `Marshal.dump(ca.to_type(:object))`).
|
|
123
125
|
def save (ca, endian: nil)
|
|
124
126
|
if ca.data_type == :object
|
|
125
127
|
raise ArgumentError,
|
|
@@ -131,9 +133,12 @@ class CArray::Serializer # :nodoc:
|
|
|
131
133
|
# would carry its storage without what makes it the Face (a unit, the
|
|
132
134
|
# string buffer, the labels), and load could not put that back.
|
|
133
135
|
if ca.face? && !ca.is_a?(CARecord)
|
|
136
|
+
storage = ca.__send__(:face_storage_method)
|
|
134
137
|
raise TypeError,
|
|
135
138
|
"CArray.save cannot carry a #{ca.class} yet (its Face would be " \
|
|
136
|
-
"lost);
|
|
139
|
+
"lost); " +
|
|
140
|
+
(storage ? "save its storage, which ##{storage} gives, or " : "") +
|
|
141
|
+
"keep its values with Marshal.dump(ca.to_type(:object))"
|
|
137
142
|
end
|
|
138
143
|
|
|
139
144
|
file_endian = endian || CArray.endian
|
|
@@ -390,7 +395,7 @@ class CArray::Serializer # :nodoc:
|
|
|
390
395
|
if trailer.key?("attrs")
|
|
391
396
|
attrs = trailer["attrs"]
|
|
392
397
|
if attrs.is_a?(Hash) and not attrs.empty?
|
|
393
|
-
ca.
|
|
398
|
+
ca.set_attrs(attrs)
|
|
394
399
|
end
|
|
395
400
|
end
|
|
396
401
|
ca
|
|
@@ -615,7 +620,8 @@ class CArray
|
|
|
615
620
|
["object",
|
|
616
621
|
target.shape,
|
|
617
622
|
target.value.to_a,
|
|
618
|
-
(target.has_mask? ? target.mask.to_a : nil)
|
|
623
|
+
(target.has_mask? ? target.mask.to_a : nil),
|
|
624
|
+
target.attrs.to_h]
|
|
619
625
|
else
|
|
620
626
|
["portable", CArray.dump(target)]
|
|
621
627
|
end
|
|
@@ -627,7 +633,10 @@ class CArray
|
|
|
627
633
|
# @param data [Array] Marshal payload.
|
|
628
634
|
# @return [void]
|
|
629
635
|
def marshal_load (data)
|
|
630
|
-
|
|
636
|
+
source = CArray.send(:array_from_marshal_payload, data)
|
|
637
|
+
initialize_copy(source)
|
|
638
|
+
attrs = source.attrs
|
|
639
|
+
set_attrs(attrs) unless attrs.empty?
|
|
631
640
|
end
|
|
632
641
|
|
|
633
642
|
# The array a {#marshal_dump} payload describes.
|
|
@@ -635,19 +644,22 @@ class CArray
|
|
|
635
644
|
tag, *rest = data
|
|
636
645
|
case tag
|
|
637
646
|
when "object"
|
|
638
|
-
shape, values, mask = rest
|
|
647
|
+
shape, values, mask, attrs = rest
|
|
639
648
|
ca = CArray.object(*shape)
|
|
640
649
|
ca[] = values
|
|
641
650
|
if mask
|
|
642
651
|
ca.mask = 0
|
|
643
652
|
ca.mask[] = mask
|
|
644
653
|
end
|
|
654
|
+
ca.set_attrs(attrs) if attrs && !attrs.empty?
|
|
645
655
|
ca
|
|
646
656
|
when "portable"
|
|
647
657
|
CArray.load(StringIO.new(rest[0]))
|
|
648
658
|
when "record"
|
|
649
|
-
data_class, storage = rest
|
|
650
|
-
CARecord.wrap(array_from_marshal_payload(storage), data_class)
|
|
659
|
+
data_class, storage, attrs = rest
|
|
660
|
+
ca = CARecord.wrap(array_from_marshal_payload(storage), data_class)
|
|
661
|
+
ca.set_attrs(attrs) if attrs && !attrs.empty?
|
|
662
|
+
ca
|
|
651
663
|
else
|
|
652
664
|
raise TypeError, "unrecognised CArray Marshal payload"
|
|
653
665
|
end
|
|
@@ -668,9 +680,11 @@ end
|
|
|
668
680
|
# @return [String]
|
|
669
681
|
def _dump (_level)
|
|
670
682
|
if face?
|
|
683
|
+
storage = face_storage_method
|
|
671
684
|
raise TypeError,
|
|
672
685
|
"Marshal cannot carry a #{self.class} yet (its Face would be lost); " \
|
|
673
|
-
"dump its
|
|
686
|
+
"dump its values with to_type(:object)" +
|
|
687
|
+
(storage ? ", or its storage, which ##{storage} gives" : "")
|
|
674
688
|
end
|
|
675
689
|
Marshal.dump(CArray.instance_method(:marshal_dump).bind_call(self))
|
|
676
690
|
end
|
|
@@ -719,7 +733,7 @@ class CARecord
|
|
|
719
733
|
def marshal_dump
|
|
720
734
|
return CArray.instance_method(:marshal_dump).bind_call(self) unless data_class.name
|
|
721
735
|
["record", data_class,
|
|
722
|
-
CArray.instance_method(:marshal_dump).bind_call(copy.parent)]
|
|
736
|
+
CArray.instance_method(:marshal_dump).bind_call(copy.parent), attrs.to_h]
|
|
723
737
|
end
|
|
724
738
|
|
|
725
739
|
end
|
data/lib/carray/slab_iterator.rb
CHANGED
|
@@ -174,6 +174,27 @@ class CASlabIterator < CAIterator
|
|
|
174
174
|
RUBY
|
|
175
175
|
end
|
|
176
176
|
|
|
177
|
+
# @!method first
|
|
178
|
+
# Per-slab first present value, in row-major order over the slab axes;
|
|
179
|
+
# UNDEF for a slab with none.
|
|
180
|
+
# @return [CArray] one value per slab
|
|
181
|
+
# @!method last
|
|
182
|
+
# Per-slab last present value; UNDEF for a slab with none.
|
|
183
|
+
# @return [CArray] one value per slab
|
|
184
|
+
# @!method nunique
|
|
185
|
+
# Per-slab number of distinct present values (int64); 0 for a slab with
|
|
186
|
+
# none.
|
|
187
|
+
# @return [CArray] one count per slab
|
|
188
|
+
#
|
|
189
|
+
# These take no `min_count:` or `fill_value:`, as the core ones do not.
|
|
190
|
+
[:first, :last, :nunique].each do |op|
|
|
191
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
192
|
+
def #{op} (**kw)
|
|
193
|
+
@reference.#{op}(axis: @slab_axes, **CAIterator.reduce_keywords(:#{op}, kw))
|
|
194
|
+
end
|
|
195
|
+
RUBY
|
|
196
|
+
end
|
|
197
|
+
|
|
177
198
|
# @overload count(v = <none>)
|
|
178
199
|
# Per-slab count, delegating to `reference.count(..., axis: slab_axes)`.
|
|
179
200
|
# No argument counts present (non-masked) cells; `count(UNDEF)` counts
|
|
@@ -344,6 +365,35 @@ class CASlabIterator < CAIterator
|
|
|
344
365
|
RUBY
|
|
345
366
|
end
|
|
346
367
|
|
|
368
|
+
# @overload lag(n = 1)
|
|
369
|
+
# Per-slab value `n` steps earlier along the slab axis (UNDEF where the
|
|
370
|
+
# slab has none), as `reference.lag(n, axis: slab_axis)`: a view of the
|
|
371
|
+
# reference. A multi-axis slab has no single direction to step in, so it
|
|
372
|
+
# raises, as the scans do.
|
|
373
|
+
# @return [CArray] reference-shaped
|
|
374
|
+
def lag (n = 1)
|
|
375
|
+
@reference.lag(n, axis: single_scan_axis(:lag))
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
# @overload strip_mask(method:)
|
|
379
|
+
# Per-slab fill of the masked cells along the slab axis, as
|
|
380
|
+
# `reference.strip_mask(method:, axis: slab_axis)`: `:forward` / `:ffill`
|
|
381
|
+
# or `:backward` / `:bfill`. A multi-axis slab raises, as the scans do.
|
|
382
|
+
# @return [CArray] reference-shaped
|
|
383
|
+
def strip_mask (method:)
|
|
384
|
+
CAIterator.hold_backward?(method)
|
|
385
|
+
@reference.strip_mask(method: method, axis: single_scan_axis(:strip_mask))
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# @overload order(descending: false, method: :ordinal, kind: :quick)
|
|
389
|
+
# Per-slab rank of each cell along the slab axis (int64), as
|
|
390
|
+
# `reference.order(axis: slab_axis, ...)`. A multi-axis slab has no
|
|
391
|
+
# single sort axis, so it raises, as sort_addr does.
|
|
392
|
+
# @return [CArray] reference-shaped
|
|
393
|
+
def order (**kw)
|
|
394
|
+
@reference.order(axis: single_sort_axis(:order), **CAIterator.reduce_keywords(:order, kw))
|
|
395
|
+
end
|
|
396
|
+
|
|
347
397
|
private
|
|
348
398
|
|
|
349
399
|
# The single slab axis for the order surface (core sort takes one integer
|
data/lib/carray/string.rb
CHANGED
|
@@ -17,20 +17,28 @@ class CArray
|
|
|
17
17
|
# CArray.string([a, nil, b]) # nil → masked element
|
|
18
18
|
# CArray.string(other_ca) # from a String Face / object / raw fixlen
|
|
19
19
|
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# a
|
|
23
|
-
#
|
|
24
|
-
#
|
|
20
|
+
# A cell of a CAString is a String. In the Array and block forms `nil`
|
|
21
|
+
# becomes a masked cell; "" (empty) is a valid distinct value. Any other
|
|
22
|
+
# value (a Symbol, a number, a nested Array) is refused: the values are one
|
|
23
|
+
# cell each, so a CAString of more than one dimension is built from an
|
|
24
|
+
# object array (`CArray.string(CA_OBJECT(nested))`).
|
|
25
|
+
# A CArray source is normalised through a String Face: a String Face
|
|
26
|
+
# converts, CA_OBJECT storage wraps zero-copy (every cell that is not
|
|
27
|
+
# masked must be a String, `nil` included: mask it instead), a raw
|
|
28
|
+
# CA_FIXLEN reads as NUL-stripped strings; a numeric / boolean array is
|
|
29
|
+
# rejected (stringify with {#format} / {CArray.format}).
|
|
25
30
|
# @overload string(values)
|
|
26
31
|
# Returns a {CAString} wrapping a CA_OBJECT entity of the given values.
|
|
27
32
|
# @param values [Array<String, nil>] source values.
|
|
28
33
|
# @return [CAString]
|
|
34
|
+
# @raise [TypeError] if a value is neither a String nor `nil`.
|
|
29
35
|
# @overload string(ca)
|
|
30
36
|
# Returns a {CAString} of the string-bearing CArray `ca`.
|
|
31
37
|
# @param ca [CArray] a String Face, CA_OBJECT, or raw CA_FIXLEN array.
|
|
32
38
|
# @return [CAString]
|
|
33
39
|
# @raise [CArray::DataTypeError] if `ca` is numeric / boolean.
|
|
40
|
+
# @raise [TypeError] if a cell of an object array that is not masked is
|
|
41
|
+
# not a String.
|
|
34
42
|
# @overload string(n) { |i| ... }
|
|
35
43
|
# Returns an `n`-element {CAString} filled by the block, following the
|
|
36
44
|
# arity-0 broadcast convention.
|
|
@@ -55,6 +63,12 @@ class CArray
|
|
|
55
63
|
|
|
56
64
|
entity = CArray.object(values.size)
|
|
57
65
|
values.each_with_index do |s, i|
|
|
66
|
+
if s.is_a?(Array)
|
|
67
|
+
raise TypeError,
|
|
68
|
+
"CArray.string: value #{i} is an Array; a CAString cell is a " \
|
|
69
|
+
"String (for a CAString of another shape, give " \
|
|
70
|
+
"CArray.string an object array: CArray.string(CA_OBJECT(values)))"
|
|
71
|
+
end
|
|
58
72
|
entity[i] = s.nil? ? UNDEF : s
|
|
59
73
|
end
|
|
60
74
|
CAString.wrap(entity)
|
|
@@ -66,4 +80,90 @@ end
|
|
|
66
80
|
class CAString
|
|
67
81
|
include CArray::StringOperationMixin
|
|
68
82
|
include CArray::StringOperationMixin::Mutable
|
|
83
|
+
|
|
84
|
+
# The String operators and the picks by order run on the object storage,
|
|
85
|
+
# cell by cell, as on any object array. A String's +, * and % give a
|
|
86
|
+
# String (or raise), and a pick gives one of the operands, so the result
|
|
87
|
+
# is text and comes back as a CAString, as the bang forms (add!, mul!)
|
|
88
|
+
# keep it. A lazy operand is computed here.
|
|
89
|
+
|
|
90
|
+
# Per-cell `String#+`.
|
|
91
|
+
# @return [CAString] shape and mask carried.
|
|
92
|
+
def + (other) ; as_text(super) ; end
|
|
93
|
+
# Per-cell `String#+` (the word form of {#+}).
|
|
94
|
+
# @return [CAString]
|
|
95
|
+
def add (other) ; as_text(super) ; end
|
|
96
|
+
# Per-cell `String#*`.
|
|
97
|
+
# @return [CAString] shape and mask carried.
|
|
98
|
+
def * (other) ; as_text(super) ; end
|
|
99
|
+
# Per-cell `String#*` (the word form of {#*}).
|
|
100
|
+
# @return [CAString]
|
|
101
|
+
def mul (other) ; as_text(super) ; end
|
|
102
|
+
# Per-cell `String#%` (format).
|
|
103
|
+
# @return [CAString] shape and mask carried.
|
|
104
|
+
def % (other) ; as_text(super) ; end
|
|
105
|
+
# Per-cell `String#%` (the word form of {#%}).
|
|
106
|
+
# @return [CAString]
|
|
107
|
+
def mod (other) ; as_text(super) ; end
|
|
108
|
+
# The later String of each cell and `other`, cell by cell.
|
|
109
|
+
# @return [CAString]
|
|
110
|
+
def pmax (other) ; as_text(super) ; end
|
|
111
|
+
# The earlier String of each cell and `other`, cell by cell.
|
|
112
|
+
# @return [CAString]
|
|
113
|
+
def pmin (other) ; as_text(super) ; end
|
|
114
|
+
# Another name of {#pmax} (a String has no NaN to let win).
|
|
115
|
+
# @return [CAString]
|
|
116
|
+
def maximum (other) ; as_text(super) ; end
|
|
117
|
+
# Another name of {#pmin}.
|
|
118
|
+
# @return [CAString]
|
|
119
|
+
def minimum (other) ; as_text(super) ; end
|
|
120
|
+
# Another name of {#pmax}.
|
|
121
|
+
# @return [CAString]
|
|
122
|
+
def fmax (other) ; as_text(super) ; end
|
|
123
|
+
# Another name of {#pmin}.
|
|
124
|
+
# @return [CAString]
|
|
125
|
+
def fmin (other) ; as_text(super) ; end
|
|
126
|
+
|
|
127
|
+
# The String a blank cell holds: the cell a fresh {#template} gives and
|
|
128
|
+
# the one {#shift} fills by default. Frozen, because every blank cell
|
|
129
|
+
# shares it.
|
|
130
|
+
BLANK = "".freeze
|
|
131
|
+
|
|
132
|
+
# A same-shape CAString. With no block every cell is {BLANK}; a block
|
|
133
|
+
# fills the cells as for any array, and its values must be Strings or
|
|
134
|
+
# UNDEF. Given a data type, the result is a plain array of that type.
|
|
135
|
+
# @overload template(type = nil, **opts)
|
|
136
|
+
# @return [CAString, CArray]
|
|
137
|
+
def template (*args, **opts, &block)
|
|
138
|
+
return super if block || ! args.empty? || ! opts.empty?
|
|
139
|
+
out = super
|
|
140
|
+
out[] = BLANK
|
|
141
|
+
out
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Shifts the cells as {CArray#shift} does. The cells shifted in hold
|
|
145
|
+
# {BLANK} unless `fill_value:` names a String or UNDEF.
|
|
146
|
+
# @return [CAString] a view over the shifted cells.
|
|
147
|
+
def shift (*shifts, fill_value: BLANK, **opts)
|
|
148
|
+
super(*shifts, fill_value: fill_value, **opts)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# A window as {CArray#window} gives. The cells outside the array hold
|
|
152
|
+
# {BLANK} unless `fill_value:` names a String or UNDEF.
|
|
153
|
+
# @return [CAString] a view over the window.
|
|
154
|
+
def window (*ranges, fill_value: BLANK, **opts)
|
|
155
|
+
super(*ranges, fill_value: fill_value, **opts)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# A resized copy as {CArray#resize} gives. The new cells hold {BLANK}
|
|
159
|
+
# unless `fill_value:` names a String or UNDEF.
|
|
160
|
+
# @return [CAString]
|
|
161
|
+
def resize (*newdim, fill_value: BLANK)
|
|
162
|
+
super(*newdim, fill_value: fill_value)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
private def as_text (result)
|
|
166
|
+
result = result.copy if result.lazy_view?
|
|
167
|
+
CAString.wrap(result)
|
|
168
|
+
end
|
|
69
169
|
end
|
|
@@ -278,8 +278,15 @@ class CArray
|
|
|
278
278
|
# boolean array has no string reading and is rejected -- stringify explicitly
|
|
279
279
|
# with #format / CArray.format. (This mirrors the numeric gate that rejects
|
|
280
280
|
# numeric reductions on a String Face.)
|
|
281
|
-
def self.string_face_of (ca)
|
|
281
|
+
def self.string_face_of (ca, verb = "CArray.string")
|
|
282
282
|
return ca if ca.is_a?(CArray::StringOperationMixin)
|
|
283
|
+
# Another Face's storage is not its text: a CATime's fixlen surface
|
|
284
|
+
# holds ticks, a categorical's codes.
|
|
285
|
+
if ca.face?
|
|
286
|
+
raise TypeError,
|
|
287
|
+
"#{verb}: a #{ca.class} is not text " \
|
|
288
|
+
"(convert its values to Strings first)"
|
|
289
|
+
end
|
|
283
290
|
case ca.data_type
|
|
284
291
|
when :object then CAString.wrap(ca)
|
|
285
292
|
when :fixlen then CAFixlenString.wrap(ca)
|
data/lib/carray/struct.rb
CHANGED
|
@@ -340,14 +340,6 @@ class CAStruct
|
|
|
340
340
|
super
|
|
341
341
|
end
|
|
342
342
|
|
|
343
|
-
protected
|
|
344
|
-
|
|
345
|
-
def __data__
|
|
346
|
-
@data
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
public
|
|
350
|
-
|
|
351
343
|
# Member access. A member is named by a String (`s["lat"]`) or its
|
|
352
344
|
# Integer position (`s[0]`); its accessor (`s.lat`) reads it too. A
|
|
353
345
|
# Symbol is not a member name here, and a name that is not a member
|
|
@@ -509,26 +501,22 @@ class CAStruct
|
|
|
509
501
|
# @!method <=(other)
|
|
510
502
|
# @!method >(other)
|
|
511
503
|
# @!method >=(other)
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
define_method(op) do |other|
|
|
504
|
+
[:<, :<=, :>, :>=].each do |op|
|
|
505
|
+
define_method(op, &Ractor.make_shareable(proc do |other|
|
|
515
506
|
c = (self <=> other)
|
|
516
507
|
if c.nil?
|
|
517
508
|
raise ArgumentError,
|
|
518
509
|
"comparison of #{self.class.inspect} with #{other.class.inspect} failed" +
|
|
519
510
|
(self.class.order_by ? "" : " (the struct declares no order_by:)")
|
|
520
511
|
end
|
|
521
|
-
|
|
522
|
-
end
|
|
512
|
+
c.public_send(op, 0)
|
|
513
|
+
end))
|
|
523
514
|
end
|
|
524
515
|
|
|
525
|
-
# One member of {#<=>}:
|
|
516
|
+
# One member of {#<=>}: the order a sort puts the member's values in
|
|
517
|
+
# (NaN after every number).
|
|
526
518
|
def self.compare_key (a, b)
|
|
527
|
-
|
|
528
|
-
b_nan = b.is_a?(Float) && b.nan?
|
|
529
|
-
return (a_nan ? 0 : -1) if b_nan
|
|
530
|
-
return 1 if a_nan
|
|
531
|
-
a <=> b
|
|
519
|
+
CArray.send(:object_order_cmp, a, b)
|
|
532
520
|
end
|
|
533
521
|
|
|
534
522
|
# Byte-level identity. Two CAStruct instances are eql? iff they are
|
|
@@ -728,8 +716,8 @@ end
|
|
|
728
716
|
# in Builder sets them up before any instance is created). Subclasses
|
|
729
717
|
# created by Builder later don't need re-installation -- they inherit the
|
|
730
718
|
# C methods from CAStruct.
|
|
731
|
-
if CArray.respond_to?(:
|
|
732
|
-
CArray.
|
|
719
|
+
if CArray.respond_to?(:install_struct_methods, true)
|
|
720
|
+
CArray.send(:install_struct_methods, CAStruct)
|
|
733
721
|
end
|
|
734
722
|
|
|
735
723
|
require 'carray/record_order'
|
|
@@ -288,6 +288,15 @@ class CAStruct::Builder # :nodoc:
|
|
|
288
288
|
end
|
|
289
289
|
|
|
290
290
|
# @!visibility private
|
|
291
|
+
# Shared with every Ractor when it can be. A member whose type is an
|
|
292
|
+
# array template holds a CArray, which cannot be shared, and its struct
|
|
293
|
+
# is then used in the main Ractor only.
|
|
294
|
+
def self.shareable (object)
|
|
295
|
+
Ractor.make_shareable(object)
|
|
296
|
+
rescue Ractor::Error
|
|
297
|
+
object.freeze
|
|
298
|
+
end
|
|
299
|
+
|
|
291
300
|
ALIGN_TABLE = {
|
|
292
301
|
CA_FIXLEN => CA_ALIGN_FIXLEN,
|
|
293
302
|
CA_BOOLEAN => CA_ALIGN_INT8,
|
|
@@ -304,6 +313,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
304
313
|
CA_CMPLX64 => CA_ALIGN_FLOAT32,
|
|
305
314
|
CA_CMPLX128 => CA_ALIGN_FLOAT64,
|
|
306
315
|
}
|
|
316
|
+
Ractor.make_shareable(ALIGN_TABLE)
|
|
307
317
|
|
|
308
318
|
# Raises ArgumentError when `opt` names a key outside `allowed`. The
|
|
309
319
|
# options are read one key at a time, so a misspelt key would otherwise
|
|
@@ -463,9 +473,9 @@ class CAStruct::Builder # :nodoc:
|
|
|
463
473
|
end
|
|
464
474
|
table.freeze
|
|
465
475
|
names.freeze
|
|
466
|
-
klass.const_set(:MEMBER_TABLE, table) ### required element as data class
|
|
467
|
-
klass.const_set(:MEMBERS, names)
|
|
468
|
-
klass.const_set(:ORDER_BY, order_keys(table)) if @order_by
|
|
476
|
+
klass.const_set(:MEMBER_TABLE, self.class.shareable(table)) ### required element as data class
|
|
477
|
+
klass.const_set(:MEMBERS, self.class.shareable(names))
|
|
478
|
+
klass.const_set(:ORDER_BY, self.class.shareable(order_keys(table))) if @order_by
|
|
469
479
|
# ---
|
|
470
480
|
# Step 3 dispatch table: precompute a per-member [reader, writer]
|
|
471
481
|
# Proc pair so `record[name]` is one Hash lookup + one Proc call,
|
|
@@ -473,7 +483,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
473
483
|
# checks and view-shape decisions happen here, once, at class
|
|
474
484
|
# definition time.
|
|
475
485
|
klass.const_set(:DISPATCH_TABLE,
|
|
476
|
-
self.class.build_dispatch_table(table, @offset)
|
|
486
|
+
self.class.shareable(self.class.build_dispatch_table(table, @offset)))
|
|
477
487
|
# Also precompute FAST_PRIMITIVES, the subset of members that the
|
|
478
488
|
# C-side `[]` / `[]=` can handle without
|
|
479
489
|
# allocating a CAField view. Entries are `[offset, type_code]`;
|
|
@@ -491,15 +501,16 @@ class CAStruct::Builder # :nodoc:
|
|
|
491
501
|
klass.module_eval {
|
|
492
502
|
taken = ->(m) { method_defined?(m) || private_method_defined?(m) }
|
|
493
503
|
names.each do |name|
|
|
504
|
+
Ractor.make_shareable(name) # the accessors below capture it
|
|
494
505
|
unless taken.(name)
|
|
495
|
-
define_method(name
|
|
506
|
+
define_method(name, &Ractor.make_shareable(proc {
|
|
496
507
|
return self[name]
|
|
497
|
-
}
|
|
508
|
+
}))
|
|
498
509
|
end
|
|
499
510
|
unless taken.("#{name}=")
|
|
500
|
-
define_method("#{name}="
|
|
511
|
+
define_method("#{name}=", &Ractor.make_shareable(proc { |val|
|
|
501
512
|
return self[name] = val
|
|
502
|
-
}
|
|
513
|
+
}))
|
|
503
514
|
end
|
|
504
515
|
end
|
|
505
516
|
}
|