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.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +438 -0
  3. data/README.md +1 -1
  4. data/carray.gemspec +3 -2
  5. data/ext/ca_array_pool.c +25 -1
  6. data/ext/ca_axis_dispatch.c +19 -20
  7. data/ext/ca_axis_group.c +1 -1
  8. data/ext/ca_categorical_iterator.c +34 -27
  9. data/ext/ca_compare.c +4 -4
  10. data/ext/ca_composite_dispatch.c +9 -9
  11. data/ext/ca_kernel_iterator.c +122 -237
  12. data/ext/ca_kernel_iterator.h +522 -337
  13. data/ext/ca_obj_array.c +69 -19
  14. data/ext/ca_obj_bincmp.c +10 -1
  15. data/ext/ca_obj_binop.c +11 -1
  16. data/ext/ca_obj_bitarray.c +5 -1
  17. data/ext/ca_obj_bitfield.c +60 -43
  18. data/ext/ca_obj_byte_swap.c +4 -1
  19. data/ext/ca_obj_const_string.c +15 -3
  20. data/ext/ca_obj_face.c +44 -1
  21. data/ext/ca_obj_fake.c +3 -1
  22. data/ext/ca_obj_field.c +13 -3
  23. data/ext/ca_obj_grid.c +219 -17
  24. data/ext/ca_obj_meld.c +69 -113
  25. data/ext/ca_obj_moncmp.c +9 -1
  26. data/ext/ca_obj_monop.c +86 -12
  27. data/ext/ca_obj_object.c +118 -32
  28. data/ext/ca_obj_record.c +3 -0
  29. data/ext/ca_obj_reduce.c +19 -1
  30. data/ext/ca_obj_refer.c +80 -26
  31. data/ext/ca_obj_remap.c +69 -10
  32. data/ext/ca_obj_roll.c +27 -6
  33. data/ext/ca_obj_select.c +38 -6
  34. data/ext/ca_obj_select_axis.c +1 -0
  35. data/ext/ca_obj_shift.c +32 -7
  36. data/ext/ca_obj_stack.c +311 -111
  37. data/ext/ca_obj_stride.c +159 -64
  38. data/ext/ca_obj_string.c +27 -2
  39. data/ext/ca_obj_tile.c +28 -7
  40. data/ext/ca_obj_transpose.c +8 -4
  41. data/ext/ca_obj_triop.c +12 -1
  42. data/ext/ca_obj_window.c +80 -50
  43. data/ext/ca_op_ipower.c +12 -12
  44. data/ext/ca_sort_kernels.h +6 -3
  45. data/ext/caframe_csv_reader.c +1 -1
  46. data/ext/carray.h +35 -1
  47. data/ext/carray_access.c +43 -67
  48. data/ext/carray_address_basis.c +134 -0
  49. data/ext/carray_attribute.c +32 -0
  50. data/ext/carray_broadcast.c +3 -4
  51. data/ext/carray_build_flags.h +1 -1
  52. data/ext/carray_cast.c +251 -93
  53. data/ext/carray_cast_func.rb +56 -26
  54. data/ext/carray_conversion.c +11 -7
  55. data/ext/carray_core.c +814 -72
  56. data/ext/carray_count.c +6 -0
  57. data/ext/carray_element.c +22 -13
  58. data/ext/carray_factorize.c +20 -20
  59. data/ext/carray_generate.c +8 -2
  60. data/ext/carray_histogram.c +26 -4
  61. data/ext/carray_index_classifier.c +4 -4
  62. data/ext/carray_internal.h +159 -12
  63. data/ext/carray_kernels_bincmp.c +2174 -170
  64. data/ext/carray_kernels_binop.c +25653 -5487
  65. data/ext/carray_kernels_init.c +8 -8
  66. data/ext/carray_kernels_moncmp.c +96 -96
  67. data/ext/carray_kernels_monop.c +12253 -5235
  68. data/ext/carray_kernels_reduce_aggregate.c +6150 -2423
  69. data/ext/carray_kernels_reduce_boolean.c +1533 -15
  70. data/ext/carray_kernels_reduce_cumulative.c +2442 -882
  71. data/ext/carray_kernels_reduce_extreme.c +5658 -1661
  72. data/ext/carray_kernels_reduce_variance.c +2534 -402
  73. data/ext/carray_kernels_scan.c +368 -368
  74. data/ext/carray_kernels_search.c +4551 -604
  75. data/ext/carray_kernels_sort.c +659 -751
  76. data/ext/carray_kernels_triop.c +114 -114
  77. data/ext/carray_key_windows.c +4 -0
  78. data/ext/carray_lazy.c +47 -0
  79. data/ext/carray_loop.c +1 -1
  80. data/ext/carray_mask.c +175 -161
  81. data/ext/carray_median_percentile.c +4 -4
  82. data/ext/carray_memory_view.c +15 -9
  83. data/ext/carray_operator.c +152 -49
  84. data/ext/carray_order.c +230 -10
  85. data/ext/carray_random.c +22 -19
  86. data/ext/carray_scatter.c +24 -7
  87. data/ext/carray_slab.c +128 -29
  88. data/ext/carray_sort.c +224 -16
  89. data/ext/carray_sort_kernel.c +5 -7
  90. data/ext/carray_struct.c +35 -43
  91. data/ext/carray_test.c +74 -19
  92. data/ext/carray_time_text.c +6 -4
  93. data/ext/carray_utils.c +63 -21
  94. data/ext/extconf.rb +11 -0
  95. data/ext/mkkernel.rb +546 -199
  96. data/ext/ruby_carray.c +4 -1
  97. data/ext/version.h +4 -4
  98. data/lib/carray/attribute.rb +42 -5
  99. data/lib/carray/autoload_carray.rb +3 -3
  100. data/lib/carray/autoload_method_extension.rb +5 -3
  101. data/lib/carray/axis_group.rb +13 -7
  102. data/lib/carray/basics.rb +28 -8
  103. data/lib/carray/block_iterator.rb +17 -3
  104. data/lib/carray/categorical.rb +35 -23
  105. data/lib/carray/categorical_iterator.rb +8 -8
  106. data/lib/carray/complex.rb +2 -2
  107. data/lib/carray/conditional.rb +3 -2
  108. data/lib/carray/const_string.rb +10 -0
  109. data/lib/carray/data_type_extension.rb +8 -1
  110. data/lib/carray/fixlen_string.rb +41 -2
  111. data/lib/carray/frame/column_verbs.rb +48 -16
  112. data/lib/carray/frame/concat.rb +17 -3
  113. data/lib/carray/frame/convert.rb +19 -0
  114. data/lib/carray/frame/csv_parser.rb +32 -32
  115. data/lib/carray/frame/describe.rb +4 -2
  116. data/lib/carray/frame/frame.rb +405 -85
  117. data/lib/carray/frame/group.rb +777 -64
  118. data/lib/carray/frame/io.rb +212 -72
  119. data/lib/carray/frame/join.rb +146 -37
  120. data/lib/carray/frame/records.rb +3 -3
  121. data/lib/carray/frame/reshape.rb +89 -11
  122. data/lib/carray/frame/sort.rb +57 -9
  123. data/lib/carray/frame/verbs.rb +384 -49
  124. data/lib/carray/frame/windows.rb +44 -6
  125. data/lib/carray/fuse_source.rb +31 -2
  126. data/lib/carray/inspect.rb +7 -2
  127. data/lib/carray/iterator.rb +32 -0
  128. data/lib/carray/lazy.rb +52 -8
  129. data/lib/carray/mask_gap_fill.rb +37 -17
  130. data/lib/carray/meld_reduce.rb +1 -1
  131. data/lib/carray/methods/choose.rb +3 -2
  132. data/lib/carray/methods/is_in.rb +8 -1
  133. data/lib/carray/methods/meshgrid.rb +3 -1
  134. data/lib/carray/runtime.rb +47 -8
  135. data/lib/carray/segment_iterator.rb +35 -16
  136. data/lib/carray/serialize.rb +53 -19
  137. data/lib/carray/stack.rb +33 -18
  138. data/lib/carray/string.rb +8 -0
  139. data/lib/carray/string_operation_extension.rb +13 -5
  140. data/lib/carray/struct_builder.rb +5 -3
  141. data/lib/carray/time.rb +112 -68
  142. data/lib/carray/window_iterator.rb +16 -4
  143. data/yard-stubs/ca_obj_array.rb +26 -1
  144. data/yard-stubs/ca_obj_transpose.rb +5 -4
  145. data/yard-stubs/carray_attribute.rb +3 -1
  146. data/yard-stubs/carray_cast.rb +10 -5
  147. data/yard-stubs/carray_conversion.rb +13 -3
  148. data/yard-stubs/carray_element.rb +7 -6
  149. data/yard-stubs/carray_mask.rb +28 -4
  150. data/yard-stubs/carray_math.rb +45 -20
  151. data/yard-stubs/carray_memory_view.rb +7 -0
  152. data/yard-stubs/carray_order.rb +100 -4
  153. data/yard-stubs/carray_scatter.rb +7 -5
  154. data/yard-stubs/carray_test.rb +7 -4
  155. metadata +18 -4
@@ -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 = endian || CArray.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
- builder.call(:pack => 1, :size => record_bytes) do
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, loaded
672
- # back by the class method rather than into an empty instance. A view comes
673
- # back as a plain array (a copy owns its memory, as #copy gives); a scalar
674
- # comes back a scalar. A wrap is refused below.
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
- class CAView
730
+ [CAView, CArrayMask, CAObjectMask].each do |klass|
695
731
  # @return [CArray] the plain array a {#_dump} string describes.
696
- def self._load (str)
697
- CArray.send(:array_from_marshal_payload, Marshal.load(str))
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 process;
703
- # Ruby would also try to write the objects that keep the borrowed memory
704
- # alive. Its copy owns its memory and marshals.
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`, `data_type`, byte width, and every axis
73
- # length except `axis` (the "meld axis"). Mismatched `data_type` raises:
74
- # cast the pieces yourself (`.to_type(:float64)`) or use
75
- # {CArray.concatenate} (eager, auto-casts).
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
- # data_type mismatch, or non-axis dim mismatch across pieces
91
- # (surfaced by CAMeld.new).
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
- if arrays.any? { |a| a.is_a?(CAMeld) && a.meld_axis == axis_norm }
109
- arrays = arrays.flat_map { |a|
110
- a.is_a?(CAMeld) && a.meld_axis == axis_norm ? a.parents : [a]
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
- CArray.stack(self.parents + others, axis: self.k_axis)
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 : yield(self[i])) }
39
+ elements.times { |i| out[i] = (m[i] ? UNDEF : cell.(self[i])) }
37
40
  else
38
- elements.times { |i| out[i] = yield(self[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).parent)
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
  }