carray 3.0.4 → 3.0.6
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 +438 -0
- data/README.md +1 -1
- data/carray.gemspec +3 -2
- data/ext/ca_array_pool.c +25 -1
- data/ext/ca_axis_dispatch.c +19 -20
- data/ext/ca_axis_group.c +1 -1
- data/ext/ca_categorical_iterator.c +34 -27
- data/ext/ca_compare.c +4 -4
- data/ext/ca_composite_dispatch.c +9 -9
- data/ext/ca_kernel_iterator.c +122 -237
- data/ext/ca_kernel_iterator.h +522 -337
- data/ext/ca_obj_array.c +69 -19
- data/ext/ca_obj_bincmp.c +10 -1
- data/ext/ca_obj_binop.c +11 -1
- data/ext/ca_obj_bitarray.c +5 -1
- data/ext/ca_obj_bitfield.c +60 -43
- data/ext/ca_obj_byte_swap.c +4 -1
- data/ext/ca_obj_const_string.c +15 -3
- data/ext/ca_obj_face.c +44 -1
- data/ext/ca_obj_fake.c +3 -1
- data/ext/ca_obj_field.c +13 -3
- data/ext/ca_obj_grid.c +219 -17
- data/ext/ca_obj_meld.c +69 -113
- data/ext/ca_obj_moncmp.c +9 -1
- data/ext/ca_obj_monop.c +86 -12
- data/ext/ca_obj_object.c +118 -32
- data/ext/ca_obj_record.c +3 -0
- data/ext/ca_obj_reduce.c +19 -1
- data/ext/ca_obj_refer.c +80 -26
- data/ext/ca_obj_remap.c +69 -10
- data/ext/ca_obj_roll.c +27 -6
- data/ext/ca_obj_select.c +38 -6
- data/ext/ca_obj_select_axis.c +1 -0
- data/ext/ca_obj_shift.c +32 -7
- data/ext/ca_obj_stack.c +311 -111
- data/ext/ca_obj_stride.c +159 -64
- data/ext/ca_obj_string.c +27 -2
- data/ext/ca_obj_tile.c +28 -7
- data/ext/ca_obj_transpose.c +8 -4
- data/ext/ca_obj_triop.c +12 -1
- data/ext/ca_obj_window.c +80 -50
- data/ext/ca_op_ipower.c +12 -12
- data/ext/ca_sort_kernels.h +6 -3
- data/ext/caframe_csv_reader.c +1 -1
- data/ext/carray.h +35 -1
- data/ext/carray_access.c +43 -67
- data/ext/carray_address_basis.c +134 -0
- data/ext/carray_attribute.c +32 -0
- data/ext/carray_broadcast.c +3 -4
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_cast.c +251 -93
- data/ext/carray_cast_func.rb +56 -26
- data/ext/carray_conversion.c +11 -7
- data/ext/carray_core.c +814 -72
- data/ext/carray_count.c +6 -0
- data/ext/carray_element.c +22 -13
- data/ext/carray_factorize.c +20 -20
- data/ext/carray_generate.c +8 -2
- data/ext/carray_histogram.c +26 -4
- data/ext/carray_index_classifier.c +4 -4
- data/ext/carray_internal.h +159 -12
- data/ext/carray_kernels_bincmp.c +2174 -170
- data/ext/carray_kernels_binop.c +25653 -5487
- data/ext/carray_kernels_init.c +8 -8
- data/ext/carray_kernels_moncmp.c +96 -96
- data/ext/carray_kernels_monop.c +12253 -5235
- data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
- data/ext/carray_kernels_reduce_boolean.c +1533 -15
- data/ext/carray_kernels_reduce_cumulative.c +2442 -882
- data/ext/carray_kernels_reduce_extreme.c +5658 -1661
- data/ext/carray_kernels_reduce_variance.c +2534 -402
- data/ext/carray_kernels_scan.c +368 -368
- data/ext/carray_kernels_search.c +4551 -604
- data/ext/carray_kernels_sort.c +659 -751
- data/ext/carray_kernels_triop.c +114 -114
- data/ext/carray_key_windows.c +4 -0
- data/ext/carray_lazy.c +47 -0
- data/ext/carray_loop.c +1 -1
- data/ext/carray_mask.c +175 -161
- data/ext/carray_median_percentile.c +4 -4
- data/ext/carray_memory_view.c +15 -9
- data/ext/carray_operator.c +152 -49
- data/ext/carray_order.c +230 -10
- data/ext/carray_random.c +22 -19
- data/ext/carray_scatter.c +24 -7
- data/ext/carray_slab.c +128 -29
- data/ext/carray_sort.c +224 -16
- data/ext/carray_sort_kernel.c +5 -7
- data/ext/carray_struct.c +35 -43
- data/ext/carray_test.c +74 -19
- data/ext/carray_time_text.c +6 -4
- data/ext/carray_utils.c +63 -21
- data/ext/extconf.rb +11 -0
- data/ext/mkkernel.rb +546 -199
- data/ext/ruby_carray.c +4 -1
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +42 -5
- data/lib/carray/autoload_carray.rb +3 -3
- data/lib/carray/autoload_method_extension.rb +5 -3
- data/lib/carray/axis_group.rb +13 -7
- data/lib/carray/basics.rb +28 -8
- data/lib/carray/block_iterator.rb +17 -3
- data/lib/carray/categorical.rb +35 -23
- data/lib/carray/categorical_iterator.rb +8 -8
- data/lib/carray/complex.rb +2 -2
- data/lib/carray/conditional.rb +3 -2
- data/lib/carray/const_string.rb +10 -0
- data/lib/carray/data_type_extension.rb +8 -1
- data/lib/carray/fixlen_string.rb +41 -2
- data/lib/carray/frame/column_verbs.rb +48 -16
- data/lib/carray/frame/concat.rb +17 -3
- data/lib/carray/frame/convert.rb +19 -0
- data/lib/carray/frame/csv_parser.rb +32 -32
- data/lib/carray/frame/describe.rb +4 -2
- data/lib/carray/frame/frame.rb +405 -85
- data/lib/carray/frame/group.rb +777 -64
- data/lib/carray/frame/io.rb +212 -72
- data/lib/carray/frame/join.rb +146 -37
- data/lib/carray/frame/records.rb +3 -3
- data/lib/carray/frame/reshape.rb +89 -11
- data/lib/carray/frame/sort.rb +57 -9
- data/lib/carray/frame/verbs.rb +384 -49
- data/lib/carray/frame/windows.rb +44 -6
- data/lib/carray/fuse_source.rb +31 -2
- data/lib/carray/inspect.rb +7 -2
- data/lib/carray/iterator.rb +32 -0
- data/lib/carray/lazy.rb +52 -8
- data/lib/carray/mask_gap_fill.rb +37 -17
- data/lib/carray/meld_reduce.rb +1 -1
- data/lib/carray/methods/choose.rb +3 -2
- data/lib/carray/methods/is_in.rb +8 -1
- data/lib/carray/methods/meshgrid.rb +3 -1
- data/lib/carray/runtime.rb +47 -8
- data/lib/carray/segment_iterator.rb +35 -16
- data/lib/carray/serialize.rb +53 -19
- data/lib/carray/stack.rb +33 -18
- data/lib/carray/string.rb +8 -0
- data/lib/carray/string_operation_extension.rb +13 -5
- data/lib/carray/struct_builder.rb +5 -3
- data/lib/carray/time.rb +112 -68
- data/lib/carray/window_iterator.rb +16 -4
- data/yard-stubs/ca_obj_array.rb +26 -1
- data/yard-stubs/ca_obj_transpose.rb +5 -4
- data/yard-stubs/carray_attribute.rb +3 -1
- data/yard-stubs/carray_cast.rb +10 -5
- data/yard-stubs/carray_conversion.rb +13 -3
- data/yard-stubs/carray_element.rb +7 -6
- data/yard-stubs/carray_mask.rb +28 -4
- data/yard-stubs/carray_math.rb +45 -20
- data/yard-stubs/carray_memory_view.rb +7 -0
- data/yard-stubs/carray_order.rb +100 -4
- data/yard-stubs/carray_scatter.rb +7 -5
- data/yard-stubs/carray_test.rb +7 -4
- metadata +18 -4
data/lib/carray/serialize.rb
CHANGED
|
@@ -141,7 +141,15 @@ class CArray::Serializer # :nodoc:
|
|
|
141
141
|
"keep its values with Marshal.dump(ca.to_type(:object))"
|
|
142
142
|
end
|
|
143
143
|
|
|
144
|
-
file_endian =
|
|
144
|
+
file_endian = case endian
|
|
145
|
+
when nil, :native then CArray.endian
|
|
146
|
+
when :big, CA_BIG_ENDIAN then CA_BIG_ENDIAN
|
|
147
|
+
when :little, CA_LITTLE_ENDIAN then CA_LITTLE_ENDIAN
|
|
148
|
+
else
|
|
149
|
+
raise ArgumentError,
|
|
150
|
+
"endian: must be :big, :little, :native, " \
|
|
151
|
+
"CA_BIG_ENDIAN or CA_LITTLE_ENDIAN (got #{endian.inspect})"
|
|
152
|
+
end
|
|
145
153
|
swap = (file_endian != CArray.endian)
|
|
146
154
|
|
|
147
155
|
elements = ca.elements
|
|
@@ -383,6 +391,7 @@ class CArray::Serializer # :nodoc:
|
|
|
383
391
|
"members" => members,
|
|
384
392
|
}
|
|
385
393
|
schema["name"] = klass.name if klass.name
|
|
394
|
+
schema["order_by"] = klass.order_by.map(&:to_s) if klass.order_by
|
|
386
395
|
schema
|
|
387
396
|
end
|
|
388
397
|
|
|
@@ -439,7 +448,15 @@ class CArray::Serializer # :nodoc:
|
|
|
439
448
|
members = schema["members"]
|
|
440
449
|
record_bytes = schema["record_bytes"]
|
|
441
450
|
builder = (schema["kind"] == "union") ? CArray.method(:union) : CArray.method(:struct)
|
|
442
|
-
|
|
451
|
+
order_by = schema["order_by"]
|
|
452
|
+
names = members.map { |m| m["name"].to_s }
|
|
453
|
+
if order_by && !(order_by.is_a?(Array) && order_by.all? { |n| names.include?(n.to_s) })
|
|
454
|
+
raise ArgumentError, "corrupt CArray binary data (order_by #{order_by.inspect} " \
|
|
455
|
+
"names a member the data_class does not have)"
|
|
456
|
+
end
|
|
457
|
+
opts = { :pack => 1, :size => record_bytes }
|
|
458
|
+
opts[:order_by] = order_by.map(&:to_sym) if order_by
|
|
459
|
+
builder.call(**opts) do
|
|
443
460
|
members.each do |m|
|
|
444
461
|
sym = PEP_TO_SYMBOL[m["type"]]
|
|
445
462
|
unless sym
|
|
@@ -668,11 +685,11 @@ class CArray
|
|
|
668
685
|
|
|
669
686
|
end
|
|
670
687
|
|
|
671
|
-
# Marshal of a view and a scalar: the {CArray#marshal_dump} payload,
|
|
672
|
-
# back by the class method rather than into an empty instance. A
|
|
673
|
-
# back as a plain array (a copy owns its
|
|
674
|
-
#
|
|
675
|
-
[CAView, CAWrap, CScalar].each do |klass|
|
|
688
|
+
# Marshal of a view, a mask and a scalar: the {CArray#marshal_dump} payload,
|
|
689
|
+
# loaded back by the class method rather than into an empty instance. A
|
|
690
|
+
# view, a wrap and a mask come back as a plain array (a copy owns its
|
|
691
|
+
# memory, as #copy gives); a scalar comes back a scalar.
|
|
692
|
+
[CAView, CAWrap, CScalar, CArrayMask, CAObjectMask].each do |klass|
|
|
676
693
|
klass.class_eval do
|
|
677
694
|
# A Face other than a record is refused: the payload would carry its
|
|
678
695
|
# storage without what makes it the Face (a unit, labels), and load
|
|
@@ -686,28 +703,45 @@ end
|
|
|
686
703
|
"dump its values with to_type(:object)" +
|
|
687
704
|
(storage ? ", or its storage, which ##{storage} gives" : "")
|
|
688
705
|
end
|
|
706
|
+
return dump_payload unless data_type == CA_OBJECT
|
|
707
|
+
# An object view dumps its cells by a nested Marshal.dump, which does
|
|
708
|
+
# not know the objects the outer dump has written. A cell holding the
|
|
709
|
+
# view again (directly or through other views) would dump it without
|
|
710
|
+
# end, so dumping the same view from inside its own dump is refused,
|
|
711
|
+
# as reading such a cell is.
|
|
712
|
+
refuse_reentry("Marshal.dump") { dump_payload }
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
private
|
|
716
|
+
|
|
717
|
+
# Marshal writes the instance variables of an object that has _dump
|
|
718
|
+
# after the String it returns, unless the String has instance
|
|
719
|
+
# variables of its own. A view keeps its parent in one, so a view of
|
|
720
|
+
# a large array would carry the whole parent, and a parent holding the
|
|
721
|
+
# view in a cell would be an object cycle. The String is given one of
|
|
722
|
+
# its own, which _load ignores.
|
|
723
|
+
def dump_payload
|
|
689
724
|
Marshal.dump(CArray.instance_method(:marshal_dump).bind_call(self))
|
|
725
|
+
.tap { |s| s.instance_variable_set(:@carray_payload, true) }
|
|
690
726
|
end
|
|
691
727
|
end
|
|
692
728
|
end
|
|
693
729
|
|
|
694
|
-
|
|
730
|
+
[CAView, CArrayMask, CAObjectMask].each do |klass|
|
|
695
731
|
# @return [CArray] the plain array a {#_dump} string describes.
|
|
696
|
-
def
|
|
697
|
-
|
|
732
|
+
# Defined with def, not define_singleton_method: a block's method keeps
|
|
733
|
+
# the block's self and cannot be called from another Ractor.
|
|
734
|
+
klass.singleton_class.class_eval do
|
|
735
|
+
def _load (str)
|
|
736
|
+
CArray.send(:array_from_marshal_payload, Marshal.load(str))
|
|
737
|
+
end
|
|
698
738
|
end
|
|
699
739
|
end
|
|
700
740
|
|
|
701
741
|
class CAWrap
|
|
702
|
-
# A wrap borrows memory, which Marshal cannot carry to another
|
|
703
|
-
#
|
|
704
|
-
#
|
|
705
|
-
def _dump (_level)
|
|
706
|
-
raise TypeError,
|
|
707
|
-
"Marshal cannot carry a #{self.class}: it borrows its memory; " \
|
|
708
|
-
"dump .copy instead"
|
|
709
|
-
end
|
|
710
|
-
|
|
742
|
+
# A wrap borrows its memory, which Marshal cannot carry to another
|
|
743
|
+
# process: {#_dump} above writes its values, and the load gives a plain
|
|
744
|
+
# array that owns a copy of them, as it does for a view of the wrap.
|
|
711
745
|
def self._load (str)
|
|
712
746
|
CArray.send(:array_from_marshal_payload, Marshal.load(str))
|
|
713
747
|
end
|
data/lib/carray/stack.rb
CHANGED
|
@@ -69,27 +69,28 @@ class CArray
|
|
|
69
69
|
# No data is copied; reads gather from parents on demand and writes flow
|
|
70
70
|
# back to them (chain composability preserved).
|
|
71
71
|
#
|
|
72
|
-
# Pieces must agree on `ndim
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
72
|
+
# Pieces must agree on `ndim` and every axis length except `axis` (the
|
|
73
|
+
# "meld axis"). Pieces of different data types are brought to a common
|
|
74
|
+
# one as {CArray.stack} brings them (`promote_list`), through read-only
|
|
75
|
+
# views of the pieces that need it.
|
|
76
76
|
#
|
|
77
77
|
# "meld" = melt + weld — pieces dissolve their boundaries along the named
|
|
78
78
|
# axis and are regarded as one.
|
|
79
79
|
#
|
|
80
|
-
# @overload meld(*arrays, axis: 0)
|
|
81
|
-
# @overload meld(list, axis: 0)
|
|
80
|
+
# @overload meld(*arrays, axis: 0, data_type: nil)
|
|
81
|
+
# @overload meld(list, axis: 0, data_type: nil)
|
|
82
82
|
# Convenience form: a single Array argument is treated as the list.
|
|
83
83
|
# @param arrays [Array<CArray>] pieces to weld. A single Array
|
|
84
84
|
# argument is accepted for compatibility with older callers.
|
|
85
85
|
# @param axis [Integer, nil] existing axis to extend (normalises
|
|
86
86
|
# negative values against the reference ndim); `nil` is the same
|
|
87
87
|
# as `0`.
|
|
88
|
+
# @param data_type [Symbol, Integer, nil] result `data_type`;
|
|
89
|
+
# inferred when `nil`.
|
|
88
90
|
# @return [CAMeld] view over the welded pieces.
|
|
89
91
|
# @raise [ArgumentError] when the list is empty, ndim mismatch,
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
def self.meld (*arrays, axis: 0)
|
|
92
|
+
# or non-axis dim mismatch across pieces (surfaced by CAMeld.new).
|
|
93
|
+
def self.meld (*arrays, axis: 0, data_type: nil)
|
|
93
94
|
if arrays.length == 1 && arrays[0].is_a?(Array)
|
|
94
95
|
arrays = arrays[0]
|
|
95
96
|
end
|
|
@@ -100,15 +101,19 @@ class CArray
|
|
|
100
101
|
raise ArgumentError, "meld: entries must be CArray (got #{first.class})"
|
|
101
102
|
end
|
|
102
103
|
axis_norm = CArray.normalize_axis(axis, first.ndim, "meld")
|
|
104
|
+
arrays = CArray.promote_list(arrays, data_type: data_type)
|
|
103
105
|
# Flatten nested CAMeld inputs that share our meld axis: they already
|
|
104
106
|
# describe a segment sequence, so absorbing their parents keeps chain
|
|
105
107
|
# depth at 1 (avoids 2-level xfer_all / reduce chains through the
|
|
106
108
|
# intermediate CAMeld). A CAMeld with a different meld_axis is left
|
|
107
|
-
# intact — its segment structure is orthogonal.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
# intact — its segment structure is orthogonal. So is a read-only one
|
|
110
|
+
# (frozen or flagged): writes through it are refused, and absorbing it
|
|
111
|
+
# would drop that. The mask of a meld is a CAMeld with no parents of
|
|
112
|
+
# its own; it is passed on, to be refused as a part.
|
|
113
|
+
absorbed = ->(a) { a.is_a?(CAMeld) && a.meld_axis == axis_norm &&
|
|
114
|
+
!a.read_only? && !a.mask_array? }
|
|
115
|
+
if arrays.any?(&absorbed)
|
|
116
|
+
arrays = arrays.flat_map { |a| absorbed.(a) ? a.parents : [a] }
|
|
112
117
|
end
|
|
113
118
|
CAMeld.new(arrays, axis: axis_norm)
|
|
114
119
|
end
|
|
@@ -119,14 +124,15 @@ class CArray
|
|
|
119
124
|
# Instance form of {CArray.meld}; non-destructive, see the class method
|
|
120
125
|
# for full semantics.
|
|
121
126
|
#
|
|
122
|
-
# @overload meld(*others, axis: 0)
|
|
127
|
+
# @overload meld(*others, axis: 0, data_type: nil)
|
|
123
128
|
# @param others [Array<CArray>] additional pieces.
|
|
124
129
|
# @param axis [Integer] existing axis to extend.
|
|
130
|
+
# @param data_type [Symbol, Integer, nil] result `data_type`.
|
|
125
131
|
# @return [CAMeld]
|
|
126
132
|
# @raise [ArgumentError] when no `others` are given.
|
|
127
|
-
def meld (*others, axis: 0)
|
|
133
|
+
def meld (*others, axis: 0, data_type: nil)
|
|
128
134
|
raise ArgumentError, "meld: at least one other array required" if others.empty?
|
|
129
|
-
CArray.meld(self, *others, axis: axis)
|
|
135
|
+
CArray.meld(self, *others, axis: axis, data_type: data_type)
|
|
130
136
|
end
|
|
131
137
|
|
|
132
138
|
# Arrange `list` of uniform-shape pieces in a `tdim`-shape grid that
|
|
@@ -288,10 +294,19 @@ class CAStack
|
|
|
288
294
|
# preserving `self.k_axis`. Distinct from `CArray#stack`, which
|
|
289
295
|
# always treats `self` as a single parent of a new K-stack.
|
|
290
296
|
# @param others [Array<CArray>] extra parents to append.
|
|
297
|
+
# A read-only stack hands on read-only views of its parents, so
|
|
298
|
+
# writes into its cells stay refused.
|
|
291
299
|
# @return [CAStack]
|
|
292
300
|
# @raise [ArgumentError] when no `others` are given.
|
|
293
301
|
def append (*others)
|
|
294
302
|
raise ArgumentError, "append: at least one parent required" if others.empty?
|
|
295
|
-
|
|
303
|
+
if mask_array?
|
|
304
|
+
raise ArgumentError, "append: a mask array can have no mask and can not be a part"
|
|
305
|
+
end
|
|
306
|
+
mine = self.parents
|
|
307
|
+
if read_only?
|
|
308
|
+
mine = mine.map { |p| p[].tap(&:set_read_only_flag) }
|
|
309
|
+
end
|
|
310
|
+
CArray.stack(mine + others, axis: self.k_axis)
|
|
296
311
|
end
|
|
297
312
|
end
|
data/lib/carray/string.rb
CHANGED
|
@@ -57,6 +57,14 @@ class CArray
|
|
|
57
57
|
else
|
|
58
58
|
values = Array.new(n) { |i| block.call(i) }
|
|
59
59
|
end
|
|
60
|
+
elsif arg.is_a?(Integer)
|
|
61
|
+
raise ArgumentError,
|
|
62
|
+
"CArray.string: a count is given with a block that makes each " \
|
|
63
|
+
"String (CArray.string(n) { |i| ... })"
|
|
64
|
+
elsif arg.is_a?(String) || ! arg.respond_to?(:to_a)
|
|
65
|
+
raise TypeError,
|
|
66
|
+
"CArray.string: expected an Array of Strings, an object array or " \
|
|
67
|
+
"a String array (got #{arg.class})"
|
|
60
68
|
else
|
|
61
69
|
values = arg.to_a
|
|
62
70
|
end
|
|
@@ -31,11 +31,14 @@ class CArray
|
|
|
31
31
|
# (integer / boolean results). Shape and mask are carried.
|
|
32
32
|
def string_map (data_type = nil) # :nodoc:
|
|
33
33
|
out = data_type ? template(data_type) : CArray.object(*shape)
|
|
34
|
+
# A typed result has no nil: a cell the String method answers nil for
|
|
35
|
+
# (index finds nothing) is UNDEF.
|
|
36
|
+
cell = data_type ? ->(s) { v = yield(s); v.nil? ? UNDEF : v } : ->(s) { yield(s) }
|
|
34
37
|
if has_mask?
|
|
35
38
|
m = is_masked
|
|
36
|
-
elements.times { |i| out[i] = (m[i] ? UNDEF :
|
|
39
|
+
elements.times { |i| out[i] = (m[i] ? UNDEF : cell.(self[i])) }
|
|
37
40
|
else
|
|
38
|
-
elements.times { |i| out[i] =
|
|
41
|
+
elements.times { |i| out[i] = cell.(self[i]) }
|
|
39
42
|
end
|
|
40
43
|
data_type ? out : CAString.wrap(out)
|
|
41
44
|
end
|
|
@@ -124,10 +127,12 @@ class CArray
|
|
|
124
127
|
# @return [CArray] `:int`, shape and mask of `self` preserved.
|
|
125
128
|
def bytesize () ; string_map(:int) { |s| s.bytesize } ; end
|
|
126
129
|
# Per-cell `String#index`.
|
|
127
|
-
# @return [CArray] `:int`, shape and mask of `self` preserved
|
|
130
|
+
# @return [CArray] `:int`, shape and mask of `self` preserved; UNDEF
|
|
131
|
+
# where the cell does not hold the substring.
|
|
128
132
|
def str_index (*a) ; string_map(:int) { |s| s.index(*a) } ; end
|
|
129
133
|
# Per-cell `String#rindex`.
|
|
130
|
-
# @return [CArray] `:int`, shape and mask of `self` preserved
|
|
134
|
+
# @return [CArray] `:int`, shape and mask of `self` preserved; UNDEF
|
|
135
|
+
# where the cell does not hold the substring.
|
|
131
136
|
def rindex (*a) ; string_map(:int) { |s| s.rindex(*a) } ; end
|
|
132
137
|
|
|
133
138
|
# ---- numeric parse (Integer / Float result) --------------------------
|
|
@@ -191,6 +196,9 @@ class CArray
|
|
|
191
196
|
unless [:error, :silent].include?(truncate)
|
|
192
197
|
raise ArgumentError, "truncate: must be :error or :silent (got #{truncate.inspect})"
|
|
193
198
|
end
|
|
199
|
+
unless bytes.nil? || (bytes.is_a?(Integer) && bytes >= 1)
|
|
200
|
+
raise ArgumentError, "to_fixlen_string: invalid bytes: #{bytes.inspect} (a cell needs bytes: of 1 or more)"
|
|
201
|
+
end
|
|
194
202
|
return self if is_a?(CAFixlenString) && (bytes.nil? || bytes == self.bytes)
|
|
195
203
|
|
|
196
204
|
if has_mask?
|
|
@@ -200,7 +208,7 @@ class CArray
|
|
|
200
208
|
values = Array.new(elements) { |i| self[i].to_s }
|
|
201
209
|
end
|
|
202
210
|
width = bytes || values.compact.map(&:bytesize).max || 1
|
|
203
|
-
width = 1 if width < 1
|
|
211
|
+
width = 1 if width < 1 # every string empty: the narrowest cell
|
|
204
212
|
if truncate == :error
|
|
205
213
|
values.each_with_index do |s, i|
|
|
206
214
|
next if s.nil?
|
|
@@ -224,11 +224,13 @@ class CAStruct::Builder # :nodoc:
|
|
|
224
224
|
end
|
|
225
225
|
vtype = {1 => :uint8, 2 => :uint16, 4 => :uint32, 8 => :uint64}[view_bytes]
|
|
226
226
|
range = bit_in_byte..(bit_in_byte + bits - 1)
|
|
227
|
+
# The word is read little-endian on every host: bits are numbered
|
|
228
|
+
# byte by byte from the record's start, as the layout places them.
|
|
227
229
|
reader = ->(data) {
|
|
228
|
-
data.field(start_byte, vtype).bitfield(range)[0]
|
|
230
|
+
data.field(start_byte, vtype).endian(:little).bitfield(range)[0]
|
|
229
231
|
}
|
|
230
232
|
writer = ->(data, val) {
|
|
231
|
-
data.field(start_byte, vtype).bitfield(range)[0] = val
|
|
233
|
+
data.field(start_byte, vtype).endian(:little).bitfield(range)[0] = val
|
|
232
234
|
}
|
|
233
235
|
[reader, writer].freeze
|
|
234
236
|
end
|
|
@@ -241,7 +243,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
241
243
|
def self.build_nested_struct_dispatcher (offset, nested_class)
|
|
242
244
|
reader = ->(data) {
|
|
243
245
|
element = nested_class.allocate
|
|
244
|
-
element.instance_variable_set(:@data, data.field(offset, nested_class).
|
|
246
|
+
element.instance_variable_set(:@data, data.field(offset, nested_class).send(:face_storage))
|
|
245
247
|
element.freeze if data.frozen?
|
|
246
248
|
element
|
|
247
249
|
}
|