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/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
|
|
@@ -153,13 +153,14 @@ class CArray
|
|
|
153
153
|
|
|
154
154
|
# @overload extract(regexp, replace = '\0')
|
|
155
155
|
# Returns a CArray whose cells are each cell's first match of `regexp`
|
|
156
|
-
# transformed by `String#sub(regexp, replace)
|
|
157
|
-
#
|
|
156
|
+
# transformed by `String#sub(regexp, replace)`. A cell that does not
|
|
157
|
+
# match is masked (`UNDEF`), so it stays apart from a match of the empty
|
|
158
|
+
# string, and a later `cast` to a number does not read it as 0.
|
|
158
159
|
# @param regexp [Regexp]
|
|
159
160
|
# @param replace [String]
|
|
160
161
|
# @return [CAString]
|
|
161
162
|
def extract (regexp, replace = '\0')
|
|
162
|
-
string_map { |s| regexp.match(s) { |m| m[0].sub(regexp, replace) } ||
|
|
163
|
+
string_map { |s| regexp.match(s) { |m| m[0].sub(regexp, replace) } || UNDEF }
|
|
163
164
|
end
|
|
164
165
|
|
|
165
166
|
# --- inter-Face conversions (String Faces only) ----------------------
|
|
@@ -277,8 +278,15 @@ class CArray
|
|
|
277
278
|
# boolean array has no string reading and is rejected -- stringify explicitly
|
|
278
279
|
# with #format / CArray.format. (This mirrors the numeric gate that rejects
|
|
279
280
|
# numeric reductions on a String Face.)
|
|
280
|
-
def self.string_face_of (ca)
|
|
281
|
+
def self.string_face_of (ca, verb = "CArray.string")
|
|
281
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
|
|
282
290
|
case ca.data_type
|
|
283
291
|
when :object then CAString.wrap(ca)
|
|
284
292
|
when :fixlen then CAFixlenString.wrap(ca)
|
data/lib/carray/struct.rb
CHANGED
|
@@ -217,6 +217,14 @@ class CAStruct
|
|
|
217
217
|
return self::MEMBERS
|
|
218
218
|
end
|
|
219
219
|
|
|
220
|
+
# @overload order_by
|
|
221
|
+
# Returns the members the records are ordered by (`order_by:` of
|
|
222
|
+
# {CArray.struct}), or nil when the struct declares no order.
|
|
223
|
+
# @return [Array<String>, nil]
|
|
224
|
+
def order_by
|
|
225
|
+
const_defined?(:ORDER_BY) ? self::ORDER_BY : nil
|
|
226
|
+
end
|
|
227
|
+
|
|
220
228
|
# @overload fields
|
|
221
229
|
# Returns the struct's members as {CAStruct::Field} objects
|
|
222
230
|
# in declaration order. Cached on the class.
|
|
@@ -318,64 +326,83 @@ class CAStruct
|
|
|
318
326
|
end
|
|
319
327
|
end
|
|
320
328
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
def
|
|
324
|
-
|
|
329
|
+
# A copy owns its bytes (dup and clone of the struct object would
|
|
330
|
+
# otherwise share the one-record array behind it).
|
|
331
|
+
def initialize_copy (other)
|
|
332
|
+
super
|
|
333
|
+
@data = @data.copy
|
|
325
334
|
end
|
|
326
335
|
|
|
327
|
-
|
|
336
|
+
# Freezing an element also stops writes to its bytes.
|
|
337
|
+
# @return [self]
|
|
338
|
+
def freeze
|
|
339
|
+
@data.freeze
|
|
340
|
+
super
|
|
341
|
+
end
|
|
328
342
|
|
|
329
|
-
# Member access
|
|
330
|
-
#
|
|
331
|
-
#
|
|
332
|
-
#
|
|
333
|
-
#
|
|
334
|
-
# lookup + one Proc#call. Unknown names fall back to `send(name)`
|
|
335
|
-
# so subclasses can define computed members via plain Ruby methods.
|
|
343
|
+
# Member access. A member is named by a String (`s["lat"]`) or its
|
|
344
|
+
# Integer position (`s[0]`); its accessor (`s.lat`) reads it too. A
|
|
345
|
+
# Symbol is not a member name here, and a name that is not a member
|
|
346
|
+
# raises. These Ruby bodies state the rule; the methods that run are
|
|
347
|
+
# the C ones installed at the bottom of this file.
|
|
336
348
|
# @overload [](name)
|
|
337
|
-
# Returns the value of member `name
|
|
338
|
-
#
|
|
339
|
-
# @param name [Symbol, String, Integer]
|
|
349
|
+
# Returns the value of member `name`.
|
|
350
|
+
# @param name [String, Integer]
|
|
340
351
|
# @return [Object]
|
|
352
|
+
# @raise [TypeError] for a Symbol or another type of name.
|
|
353
|
+
# @raise [ArgumentError] when `name` is not a member.
|
|
341
354
|
def [] (name)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
end
|
|
345
|
-
pair = self.class::DISPATCH_TABLE[name.to_s]
|
|
346
|
-
if pair
|
|
347
|
-
pair[0].call(@data)
|
|
348
|
-
else
|
|
349
|
-
send(name)
|
|
350
|
-
end
|
|
355
|
+
pair = self.class::DISPATCH_TABLE[member_name(name)]
|
|
356
|
+
pair[0].call(@data)
|
|
351
357
|
end
|
|
352
358
|
|
|
353
359
|
# @overload []=(name, val)
|
|
354
|
-
# Sets member `name` to `val`.
|
|
355
|
-
#
|
|
356
|
-
# @param name [Symbol, String, Integer]
|
|
360
|
+
# Sets member `name` to `val`.
|
|
361
|
+
# @param name [String, Integer]
|
|
357
362
|
# @param val [Object]
|
|
358
363
|
# @return [Object] `val`.
|
|
364
|
+
# @raise [TypeError] for a Symbol or another type of name.
|
|
365
|
+
# @raise [ArgumentError] when `name` is not a member.
|
|
359
366
|
def []= (name, val)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
367
|
+
pair = self.class::DISPATCH_TABLE[member_name(name)]
|
|
368
|
+
pair[1].call(@data, val)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
private def member_name (name)
|
|
372
|
+
case name
|
|
373
|
+
when Symbol
|
|
374
|
+
if self.class::MEMBER_TABLE.key?(name.to_s)
|
|
375
|
+
raise TypeError, "a struct member is named by a String (s[\"#{name}\"]) " \
|
|
376
|
+
"or read by its accessor (s.#{name}), not by a Symbol"
|
|
377
|
+
end
|
|
378
|
+
raise TypeError, "a struct member is named by a String, not by a Symbol " \
|
|
379
|
+
"(#{self.class.inspect} has no member #{name.to_s.inspect} either)"
|
|
380
|
+
when Integer
|
|
381
|
+
key = members[name]
|
|
382
|
+
when String
|
|
383
|
+
key = name
|
|
366
384
|
else
|
|
367
|
-
|
|
385
|
+
raise TypeError, "a struct member is named by a String or an Integer " \
|
|
386
|
+
"position (got #{name.class})"
|
|
368
387
|
end
|
|
388
|
+
unless key && self.class::MEMBER_TABLE.key?(key)
|
|
389
|
+
raise ArgumentError, "#{self.class.inspect} has no member #{name.inspect} " \
|
|
390
|
+
"(members: #{members.inspect})"
|
|
391
|
+
end
|
|
392
|
+
key
|
|
369
393
|
end
|
|
370
394
|
|
|
371
395
|
# @overload each { |value| ... }
|
|
372
|
-
# Yields each member value in declaration order
|
|
396
|
+
# Yields each member value in declaration order (an Enumerator
|
|
397
|
+
# without a block).
|
|
373
398
|
# @yieldparam value [Object]
|
|
374
399
|
# @return [self]
|
|
375
400
|
def each
|
|
401
|
+
return to_enum(:each) { length } unless block_given?
|
|
376
402
|
members.each do |name|
|
|
377
403
|
yield(self[name])
|
|
378
404
|
end
|
|
405
|
+
self
|
|
379
406
|
end
|
|
380
407
|
|
|
381
408
|
# @overload each_pair { |name, value| ... }
|
|
@@ -385,9 +412,19 @@ class CAStruct
|
|
|
385
412
|
# @yieldparam value [Object]
|
|
386
413
|
# @return [self]
|
|
387
414
|
def each_pair
|
|
415
|
+
return to_enum(:each_pair) { length } unless block_given?
|
|
388
416
|
members.each do |name|
|
|
389
417
|
yield(name.intern, self[name])
|
|
390
418
|
end
|
|
419
|
+
self
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# @overload to_h
|
|
423
|
+
# Returns the members as a Hash of member name (Symbol) to value, in
|
|
424
|
+
# declaration order.
|
|
425
|
+
# @return [Hash{Symbol => Object}]
|
|
426
|
+
def to_h
|
|
427
|
+
each_pair.to_h
|
|
391
428
|
end
|
|
392
429
|
|
|
393
430
|
# @overload length
|
|
@@ -432,16 +469,54 @@ class CAStruct
|
|
|
432
469
|
return ["<", self.class.inspect, " ", table.inspect[1..-2], ">"].join
|
|
433
470
|
end
|
|
434
471
|
|
|
435
|
-
|
|
436
|
-
|
|
472
|
+
alias to_s inspect
|
|
473
|
+
|
|
474
|
+
# Equality with another record of the same class, by the record's bytes
|
|
475
|
+
# (the same as {#eql?}): a struct is its bytes, so a NaN member equals
|
|
476
|
+
# itself, 0.0 and -0.0 differ, and so do records whose padding bytes
|
|
477
|
+
# differ. A record array's `eq` compares records the same way.
|
|
437
478
|
# @return [Boolean]
|
|
438
479
|
def == (other)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
480
|
+
other.is_a?(self.class) && encode == other.encode
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Order against a record of the same class, by the members the struct
|
|
484
|
+
# was declared with in `order_by:`, compared in that order. NaN sorts
|
|
485
|
+
# after every number. nil when the other is not a record of this class,
|
|
486
|
+
# or when the struct declares no order.
|
|
487
|
+
# @return [Integer, nil]
|
|
488
|
+
def <=> (other)
|
|
489
|
+
keys = self.class.order_by
|
|
490
|
+
return nil unless keys && other.class.equal?(self.class)
|
|
491
|
+
keys.each do |key|
|
|
492
|
+
c = CAStruct.compare_key(self[key], other[key])
|
|
493
|
+
return c unless c == 0
|
|
444
494
|
end
|
|
495
|
+
0
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# @!method <(other)
|
|
499
|
+
# @raise [ArgumentError] when the struct declares no `order_by:`, or
|
|
500
|
+
# `other` is not a record of the same class.
|
|
501
|
+
# @!method <=(other)
|
|
502
|
+
# @!method >(other)
|
|
503
|
+
# @!method >=(other)
|
|
504
|
+
[:<, :<=, :>, :>=].each do |op|
|
|
505
|
+
define_method(op, &Ractor.make_shareable(proc do |other|
|
|
506
|
+
c = (self <=> other)
|
|
507
|
+
if c.nil?
|
|
508
|
+
raise ArgumentError,
|
|
509
|
+
"comparison of #{self.class.inspect} with #{other.class.inspect} failed" +
|
|
510
|
+
(self.class.order_by ? "" : " (the struct declares no order_by:)")
|
|
511
|
+
end
|
|
512
|
+
c.public_send(op, 0)
|
|
513
|
+
end))
|
|
514
|
+
end
|
|
515
|
+
|
|
516
|
+
# One member of {#<=>}: the order a sort puts the member's values in
|
|
517
|
+
# (NaN after every number).
|
|
518
|
+
def self.compare_key (a, b)
|
|
519
|
+
CArray.send(:object_order_cmp, a, b)
|
|
445
520
|
end
|
|
446
521
|
|
|
447
522
|
# Byte-level identity. Two CAStruct instances are eql? iff they are
|
|
@@ -465,6 +540,7 @@ class CAStruct
|
|
|
465
540
|
# @return [self]
|
|
466
541
|
# @raise [CAStruct::DecodeError] on an unsupported input.
|
|
467
542
|
def decode (data)
|
|
543
|
+
raise FrozenError.new("can't modify frozen #{self.class}", receiver: self) if frozen?
|
|
468
544
|
case data
|
|
469
545
|
when String
|
|
470
546
|
@data.load_binary(data)
|
|
@@ -486,25 +562,18 @@ class CAStruct
|
|
|
486
562
|
end
|
|
487
563
|
|
|
488
564
|
# @overload swap_bytes!
|
|
489
|
-
# Byte-swaps `self` in place (endian flip).
|
|
565
|
+
# Byte-swaps each member of `self` in place (endian flip).
|
|
490
566
|
# @return [self]
|
|
491
567
|
def swap_bytes!
|
|
492
|
-
|
|
493
|
-
return self
|
|
568
|
+
decode(swap_bytes.encode)
|
|
494
569
|
end
|
|
495
570
|
|
|
496
571
|
# @overload swap_bytes
|
|
497
|
-
# Returns a fresh
|
|
572
|
+
# Returns a fresh copy of `self` with each member byte-swapped, as
|
|
573
|
+
# a record array's swap_bytes swaps its records.
|
|
498
574
|
# @return [CAStruct]
|
|
499
575
|
def swap_bytes
|
|
500
|
-
return
|
|
501
|
-
end
|
|
502
|
-
|
|
503
|
-
# @overload to_ptr
|
|
504
|
-
# Returns a `Fiddle::Pointer` to the backing storage.
|
|
505
|
-
# @return [Fiddle::Pointer]
|
|
506
|
-
def to_ptr
|
|
507
|
-
return @data.to_ptr
|
|
576
|
+
return CARecord.wrap(@data, self.class).swap_bytes[0]
|
|
508
577
|
end
|
|
509
578
|
|
|
510
579
|
end
|
|
@@ -527,9 +596,19 @@ class CArray
|
|
|
527
596
|
# Returns a new {CAStruct} subclass defined by the block via
|
|
528
597
|
# {CAStruct::Builder}. Options control alignment, packing, and
|
|
529
598
|
# endianness.
|
|
530
|
-
#
|
|
599
|
+
#
|
|
600
|
+
# `order_by:` names the members records are ordered by, compared in
|
|
601
|
+
# that order (`order_by: [:lat, :id]` sorts by `lat`, then `id` among
|
|
602
|
+
# equal `lat`). It gives the records `<=>` and the comparison
|
|
603
|
+
# operators, and gives a {CARecord} of them `sort`, `min`, `max`,
|
|
604
|
+
# `partition_copy` and their index forms. A struct without it has no
|
|
605
|
+
# order, and those raise. NaN sorts after every number, as in a
|
|
606
|
+
# numeric array.
|
|
607
|
+
# @param opt [Hash] `pack:`, `size:`, `order_by:`.
|
|
531
608
|
# @yield DSL calls in {CAStruct::Builder}.
|
|
532
609
|
# @return [Class] anonymous {CAStruct} subclass.
|
|
610
|
+
# @raise [CAStruct::DefinitionError] when `order_by:` names something
|
|
611
|
+
# that is not a member, or a nested record.
|
|
533
612
|
def self.struct (opt={}, &block)
|
|
534
613
|
return CAStruct::Builder.new(:struct, opt).define(&block)
|
|
535
614
|
end
|
|
@@ -637,8 +716,8 @@ end
|
|
|
637
716
|
# in Builder sets them up before any instance is created). Subclasses
|
|
638
717
|
# created by Builder later don't need re-installation -- they inherit the
|
|
639
718
|
# C methods from CAStruct.
|
|
640
|
-
if CArray.respond_to?(:
|
|
641
|
-
CArray.
|
|
719
|
+
if CArray.respond_to?(:install_struct_methods, true)
|
|
720
|
+
CArray.send(:install_struct_methods, CAStruct)
|
|
642
721
|
end
|
|
643
722
|
|
|
644
|
-
|
|
723
|
+
require 'carray/record_order'
|