carray 3.0.1 → 3.0.3
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 +824 -0
- data/README.md +5 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +258 -4
- data/ext/ca_axis_group.c +368 -184
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +90 -41
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +960 -245
- data/ext/ca_kernel_iterator.h +352 -181
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +194 -42
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +5 -4
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +180 -50
- data/ext/ca_obj_face.c +428 -62
- data/ext/ca_obj_face.h +107 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +39 -26
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +235 -55
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +551 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +88 -75
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -123
- data/ext/ca_obj_stride.c +267 -35
- data/ext/ca_obj_string.c +25 -22
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +157 -29
- data/ext/ca_op_ipower.c +25 -13
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_rng_normal.h +42 -0
- data/ext/ca_rng_xoshiro256pp.h +105 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +385 -198
- data/ext/ca_sweep_engine.h +31 -23
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +463 -0
- data/ext/carray.h +90 -28
- data/ext/carray_access.c +487 -231
- data/ext/carray_address_basis.c +731 -0
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +49 -17
- data/ext/carray_call_cfunc.c +667 -511
- data/ext/carray_cast.c +1556 -247
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +84 -38
- data/ext/carray_core.c +591 -119
- data/ext/carray_count.c +148 -96
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +604 -291
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +400 -1
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +88 -66
- data/ext/carray_mask.c +705 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +263 -63
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1093 -363
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +582 -204
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +183 -70
- data/ext/carray_sort.c +235 -240
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +103 -118
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1349 -851
- data/ext/ruby_carray.c +17 -2
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +24 -3
- data/lib/carray/autoload_method_extension.rb +18 -0
- data/lib/carray/axis_group.rb +245 -20
- data/lib/carray/basics.rb +95 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +200 -54
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +244 -59
- data/lib/carray/categorical_iterator.rb +391 -757
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +177 -27
- data/lib/carray/construct.rb +40 -0
- data/lib/carray/data_type_extension.rb +3 -0
- data/lib/carray/data_type_limits.rb +91 -0
- data/lib/carray/fixlen_string.rb +1 -1
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +590 -76
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +205 -35
- data/lib/carray/frame/group.rb +199 -40
- data/lib/carray/frame/io.rb +452 -60
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +54 -19
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +726 -56
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +71 -20
- data/lib/carray/iterator.rb +157 -2
- data/lib/carray/lazy.rb +54 -67
- data/lib/carray/mask_gap_fill.rb +66 -50
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/discovery_along.rb +74 -0
- data/lib/carray/methods/factorize.rb +50 -0
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +130 -6
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +235 -21
- data/lib/carray/methods/mask_duplicates.rb +35 -1
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +22 -1
- data/lib/carray/methods/repeat.rb +91 -0
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +52 -3
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +86 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +109 -40
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +9 -4
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +535 -105
- data/lib/carray/window_iterator.rb +259 -82
- data/lib/carray.rb +4 -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_block.rb +2 -7
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +11 -4
- data/yard-stubs/carray_access.rb +6 -5
- data/yard-stubs/carray_broadcast.rb +2 -2
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -83
- data/yard-stubs/carray_count.rb +16 -2
- data/yard-stubs/carray_lazy.rb +210 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +1499 -4
- data/yard-stubs/carray_median_percentile.rb +26 -8
- data/yard-stubs/carray_order.rb +43 -82
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +11 -8
- data/yard-stubs/carray_sort.rb +50 -8
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -25837
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14593
- data/ext/carray_kernels_reduce_extreme.c +0 -16948
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32138
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
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,6 +326,20 @@ class CAStruct
|
|
|
318
326
|
end
|
|
319
327
|
end
|
|
320
328
|
|
|
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
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Freezing an element also stops writes to its bytes.
|
|
337
|
+
# @return [self]
|
|
338
|
+
def freeze
|
|
339
|
+
@data.freeze
|
|
340
|
+
super
|
|
341
|
+
end
|
|
342
|
+
|
|
321
343
|
protected
|
|
322
344
|
|
|
323
345
|
def __data__
|
|
@@ -326,56 +348,69 @@ class CAStruct
|
|
|
326
348
|
|
|
327
349
|
public
|
|
328
350
|
|
|
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.
|
|
351
|
+
# Member access. A member is named by a String (`s["lat"]`) or its
|
|
352
|
+
# Integer position (`s[0]`); its accessor (`s.lat`) reads it too. A
|
|
353
|
+
# Symbol is not a member name here, and a name that is not a member
|
|
354
|
+
# raises. These Ruby bodies state the rule; the methods that run are
|
|
355
|
+
# the C ones installed at the bottom of this file.
|
|
336
356
|
# @overload [](name)
|
|
337
|
-
# Returns the value of member `name
|
|
338
|
-
#
|
|
339
|
-
# @param name [Symbol, String, Integer]
|
|
357
|
+
# Returns the value of member `name`.
|
|
358
|
+
# @param name [String, Integer]
|
|
340
359
|
# @return [Object]
|
|
360
|
+
# @raise [TypeError] for a Symbol or another type of name.
|
|
361
|
+
# @raise [ArgumentError] when `name` is not a member.
|
|
341
362
|
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
|
|
363
|
+
pair = self.class::DISPATCH_TABLE[member_name(name)]
|
|
364
|
+
pair[0].call(@data)
|
|
351
365
|
end
|
|
352
366
|
|
|
353
367
|
# @overload []=(name, val)
|
|
354
|
-
# Sets member `name` to `val`.
|
|
355
|
-
#
|
|
356
|
-
# @param name [Symbol, String, Integer]
|
|
368
|
+
# Sets member `name` to `val`.
|
|
369
|
+
# @param name [String, Integer]
|
|
357
370
|
# @param val [Object]
|
|
358
371
|
# @return [Object] `val`.
|
|
372
|
+
# @raise [TypeError] for a Symbol or another type of name.
|
|
373
|
+
# @raise [ArgumentError] when `name` is not a member.
|
|
359
374
|
def []= (name, val)
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
375
|
+
pair = self.class::DISPATCH_TABLE[member_name(name)]
|
|
376
|
+
pair[1].call(@data, val)
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
private def member_name (name)
|
|
380
|
+
case name
|
|
381
|
+
when Symbol
|
|
382
|
+
if self.class::MEMBER_TABLE.key?(name.to_s)
|
|
383
|
+
raise TypeError, "a struct member is named by a String (s[\"#{name}\"]) " \
|
|
384
|
+
"or read by its accessor (s.#{name}), not by a Symbol"
|
|
385
|
+
end
|
|
386
|
+
raise TypeError, "a struct member is named by a String, not by a Symbol " \
|
|
387
|
+
"(#{self.class.inspect} has no member #{name.to_s.inspect} either)"
|
|
388
|
+
when Integer
|
|
389
|
+
key = members[name]
|
|
390
|
+
when String
|
|
391
|
+
key = name
|
|
366
392
|
else
|
|
367
|
-
|
|
393
|
+
raise TypeError, "a struct member is named by a String or an Integer " \
|
|
394
|
+
"position (got #{name.class})"
|
|
368
395
|
end
|
|
396
|
+
unless key && self.class::MEMBER_TABLE.key?(key)
|
|
397
|
+
raise ArgumentError, "#{self.class.inspect} has no member #{name.inspect} " \
|
|
398
|
+
"(members: #{members.inspect})"
|
|
399
|
+
end
|
|
400
|
+
key
|
|
369
401
|
end
|
|
370
402
|
|
|
371
403
|
# @overload each { |value| ... }
|
|
372
|
-
# Yields each member value in declaration order
|
|
404
|
+
# Yields each member value in declaration order (an Enumerator
|
|
405
|
+
# without a block).
|
|
373
406
|
# @yieldparam value [Object]
|
|
374
407
|
# @return [self]
|
|
375
408
|
def each
|
|
409
|
+
return to_enum(:each) { length } unless block_given?
|
|
376
410
|
members.each do |name|
|
|
377
411
|
yield(self[name])
|
|
378
412
|
end
|
|
413
|
+
self
|
|
379
414
|
end
|
|
380
415
|
|
|
381
416
|
# @overload each_pair { |name, value| ... }
|
|
@@ -385,9 +420,19 @@ class CAStruct
|
|
|
385
420
|
# @yieldparam value [Object]
|
|
386
421
|
# @return [self]
|
|
387
422
|
def each_pair
|
|
423
|
+
return to_enum(:each_pair) { length } unless block_given?
|
|
388
424
|
members.each do |name|
|
|
389
425
|
yield(name.intern, self[name])
|
|
390
426
|
end
|
|
427
|
+
self
|
|
428
|
+
end
|
|
429
|
+
|
|
430
|
+
# @overload to_h
|
|
431
|
+
# Returns the members as a Hash of member name (Symbol) to value, in
|
|
432
|
+
# declaration order.
|
|
433
|
+
# @return [Hash{Symbol => Object}]
|
|
434
|
+
def to_h
|
|
435
|
+
each_pair.to_h
|
|
391
436
|
end
|
|
392
437
|
|
|
393
438
|
# @overload length
|
|
@@ -432,18 +477,60 @@ class CAStruct
|
|
|
432
477
|
return ["<", self.class.inspect, " ", table.inspect[1..-2], ">"].join
|
|
433
478
|
end
|
|
434
479
|
|
|
435
|
-
|
|
436
|
-
|
|
480
|
+
alias to_s inspect
|
|
481
|
+
|
|
482
|
+
# Equality with another record of the same class, by the record's bytes
|
|
483
|
+
# (the same as {#eql?}): a struct is its bytes, so a NaN member equals
|
|
484
|
+
# itself, 0.0 and -0.0 differ, and so do records whose padding bytes
|
|
485
|
+
# differ. A record array's `eq` compares records the same way.
|
|
437
486
|
# @return [Boolean]
|
|
438
487
|
def == (other)
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
488
|
+
other.is_a?(self.class) && encode == other.encode
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Order against a record of the same class, by the members the struct
|
|
492
|
+
# was declared with in `order_by:`, compared in that order. NaN sorts
|
|
493
|
+
# after every number. nil when the other is not a record of this class,
|
|
494
|
+
# or when the struct declares no order.
|
|
495
|
+
# @return [Integer, nil]
|
|
496
|
+
def <=> (other)
|
|
497
|
+
keys = self.class.order_by
|
|
498
|
+
return nil unless keys && other.class.equal?(self.class)
|
|
499
|
+
keys.each do |key|
|
|
500
|
+
c = CAStruct.compare_key(self[key], other[key])
|
|
501
|
+
return c unless c == 0
|
|
502
|
+
end
|
|
503
|
+
0
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# @!method <(other)
|
|
507
|
+
# @raise [ArgumentError] when the struct declares no `order_by:`, or
|
|
508
|
+
# `other` is not a record of the same class.
|
|
509
|
+
# @!method <=(other)
|
|
510
|
+
# @!method >(other)
|
|
511
|
+
# @!method >=(other)
|
|
512
|
+
{ :< => ->(c) { c < 0 }, :<= => ->(c) { c <= 0 },
|
|
513
|
+
:> => ->(c) { c > 0 }, :>= => ->(c) { c >= 0 } }.each do |op, test|
|
|
514
|
+
define_method(op) do |other|
|
|
515
|
+
c = (self <=> other)
|
|
516
|
+
if c.nil?
|
|
517
|
+
raise ArgumentError,
|
|
518
|
+
"comparison of #{self.class.inspect} with #{other.class.inspect} failed" +
|
|
519
|
+
(self.class.order_by ? "" : " (the struct declares no order_by:)")
|
|
520
|
+
end
|
|
521
|
+
test.call(c)
|
|
444
522
|
end
|
|
445
523
|
end
|
|
446
524
|
|
|
525
|
+
# One member of {#<=>}: NaN after every number, as a numeric array sorts.
|
|
526
|
+
def self.compare_key (a, b)
|
|
527
|
+
a_nan = a.is_a?(Float) && a.nan?
|
|
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
|
|
532
|
+
end
|
|
533
|
+
|
|
447
534
|
# Byte-level identity. Two CAStruct instances are eql? iff they are
|
|
448
535
|
# of exactly the same class and their binary representations match.
|
|
449
536
|
# Lets struct records work as Hash keys / Set members.
|
|
@@ -465,6 +552,7 @@ class CAStruct
|
|
|
465
552
|
# @return [self]
|
|
466
553
|
# @raise [CAStruct::DecodeError] on an unsupported input.
|
|
467
554
|
def decode (data)
|
|
555
|
+
raise FrozenError.new("can't modify frozen #{self.class}", receiver: self) if frozen?
|
|
468
556
|
case data
|
|
469
557
|
when String
|
|
470
558
|
@data.load_binary(data)
|
|
@@ -486,25 +574,18 @@ class CAStruct
|
|
|
486
574
|
end
|
|
487
575
|
|
|
488
576
|
# @overload swap_bytes!
|
|
489
|
-
# Byte-swaps `self` in place (endian flip).
|
|
577
|
+
# Byte-swaps each member of `self` in place (endian flip).
|
|
490
578
|
# @return [self]
|
|
491
579
|
def swap_bytes!
|
|
492
|
-
|
|
493
|
-
return self
|
|
580
|
+
decode(swap_bytes.encode)
|
|
494
581
|
end
|
|
495
582
|
|
|
496
583
|
# @overload swap_bytes
|
|
497
|
-
# Returns a fresh
|
|
584
|
+
# Returns a fresh copy of `self` with each member byte-swapped, as
|
|
585
|
+
# a record array's swap_bytes swaps its records.
|
|
498
586
|
# @return [CAStruct]
|
|
499
587
|
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
|
|
588
|
+
return CARecord.wrap(@data, self.class).swap_bytes[0]
|
|
508
589
|
end
|
|
509
590
|
|
|
510
591
|
end
|
|
@@ -527,9 +608,19 @@ class CArray
|
|
|
527
608
|
# Returns a new {CAStruct} subclass defined by the block via
|
|
528
609
|
# {CAStruct::Builder}. Options control alignment, packing, and
|
|
529
610
|
# endianness.
|
|
530
|
-
#
|
|
611
|
+
#
|
|
612
|
+
# `order_by:` names the members records are ordered by, compared in
|
|
613
|
+
# that order (`order_by: [:lat, :id]` sorts by `lat`, then `id` among
|
|
614
|
+
# equal `lat`). It gives the records `<=>` and the comparison
|
|
615
|
+
# operators, and gives a {CARecord} of them `sort`, `min`, `max`,
|
|
616
|
+
# `partition_copy` and their index forms. A struct without it has no
|
|
617
|
+
# order, and those raise. NaN sorts after every number, as in a
|
|
618
|
+
# numeric array.
|
|
619
|
+
# @param opt [Hash] `pack:`, `size:`, `order_by:`.
|
|
531
620
|
# @yield DSL calls in {CAStruct::Builder}.
|
|
532
621
|
# @return [Class] anonymous {CAStruct} subclass.
|
|
622
|
+
# @raise [CAStruct::DefinitionError] when `order_by:` names something
|
|
623
|
+
# that is not a member, or a nested record.
|
|
533
624
|
def self.struct (opt={}, &block)
|
|
534
625
|
return CAStruct::Builder.new(:struct, opt).define(&block)
|
|
535
626
|
end
|
|
@@ -641,4 +732,4 @@ if CArray.respond_to?(:__install_castruct_methods__)
|
|
|
641
732
|
CArray.__install_castruct_methods__(CAStruct)
|
|
642
733
|
end
|
|
643
734
|
|
|
644
|
-
|
|
735
|
+
require 'carray/record_order'
|
|
@@ -234,8 +234,17 @@ class CAStruct::Builder # :nodoc:
|
|
|
234
234
|
end
|
|
235
235
|
|
|
236
236
|
# @!visibility private
|
|
237
|
+
# A nested member reads as an element of the nested struct over the
|
|
238
|
+
# parent's own bytes (a field view), so writing to it writes to the
|
|
239
|
+
# parent, as writing to an array member does. A frozen parent gives a
|
|
240
|
+
# frozen nested element.
|
|
237
241
|
def self.build_nested_struct_dispatcher (offset, nested_class)
|
|
238
|
-
reader = ->(data) {
|
|
242
|
+
reader = ->(data) {
|
|
243
|
+
element = nested_class.allocate
|
|
244
|
+
element.instance_variable_set(:@data, data.field(offset, nested_class).parent)
|
|
245
|
+
element.freeze if data.frozen?
|
|
246
|
+
element
|
|
247
|
+
}
|
|
239
248
|
writer = ->(data, val) { data.field(offset, nested_class)[0] = val }
|
|
240
249
|
[reader, writer].freeze
|
|
241
250
|
end
|
|
@@ -296,7 +305,22 @@ class CAStruct::Builder # :nodoc:
|
|
|
296
305
|
CA_CMPLX128 => CA_ALIGN_FLOAT64,
|
|
297
306
|
}
|
|
298
307
|
|
|
308
|
+
# Raises ArgumentError when `opt` names a key outside `allowed`. The
|
|
309
|
+
# options are read one key at a time, so a misspelt key would otherwise
|
|
310
|
+
# be ignored and the layout would change without a word.
|
|
311
|
+
def self.check_keys! (opt, allowed, where)
|
|
312
|
+
unknown = opt.keys - allowed
|
|
313
|
+
return if unknown.empty?
|
|
314
|
+
raise ArgumentError, "#{where}: unknown keyword#{unknown.size > 1 ? "s" : ""}: " +
|
|
315
|
+
unknown.map(&:inspect).join(", ")
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
BUILDER_KEYS = [:pack, :size, :order_by].freeze
|
|
319
|
+
MEMBER_KEYS = [:offset, :bytes, :endian].freeze
|
|
320
|
+
NESTED_KEYS = (BUILDER_KEYS + [:type, :offset]).freeze
|
|
321
|
+
|
|
299
322
|
def initialize (type, opt = {})
|
|
323
|
+
CAStruct::Builder.check_keys!(opt, BUILDER_KEYS, type.to_s)
|
|
300
324
|
if not opt[:pack].nil? and not opt[:pack].is_a?(Integer)
|
|
301
325
|
raise CAStruct::DefinitionError,
|
|
302
326
|
"invalid :pack value #{opt[:pack].inspect} (expected nil or Integer)"
|
|
@@ -305,10 +329,42 @@ class CAStruct::Builder # :nodoc:
|
|
|
305
329
|
@align = opt[:pack] ### nil for alignment, int for pack(n)
|
|
306
330
|
@members = [] ### array of CArray::Struct::Builder::Member
|
|
307
331
|
@offset = 0 ### offset of each member and size of struct
|
|
332
|
+
@extent = 0 ### furthest byte any member reaches
|
|
308
333
|
@align_max = 1 ### maximum of alignment among members
|
|
309
334
|
@size = opt[:size] ### user defined struct size
|
|
310
335
|
@bit_offset = 0 ### 0..7, sub-byte position for bitfield accumulator
|
|
336
|
+
@order_by = opt[:order_by] ### member names the records are ordered by
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# The `order_by:` members, as member names. Each has to be a member of
|
|
340
|
+
# this struct that holds one ordered value: not a nested record, an array
|
|
341
|
+
# member or a complex number.
|
|
342
|
+
def order_keys (table)
|
|
343
|
+
keys = Array(@order_by).map(&:to_s)
|
|
344
|
+
if keys.empty?
|
|
345
|
+
raise CAStruct::DefinitionError, "order_by: names no member"
|
|
346
|
+
end
|
|
347
|
+
keys.each do |key|
|
|
348
|
+
unless table.key?(key)
|
|
349
|
+
raise CAStruct::DefinitionError, "order_by: #{key.inspect} is not a member"
|
|
350
|
+
end
|
|
351
|
+
type = table[key][1]
|
|
352
|
+
if type.is_a?(Class) && type <= CAStruct
|
|
353
|
+
raise CAStruct::DefinitionError,
|
|
354
|
+
"order_by: #{key.inspect} is a nested record; order by its members instead"
|
|
355
|
+
end
|
|
356
|
+
if type.is_a?(CArray)
|
|
357
|
+
raise CAStruct::DefinitionError,
|
|
358
|
+
"order_by: #{key.inspect} is an array member, which has no order"
|
|
359
|
+
end
|
|
360
|
+
if [:cmplx64, :cmplx128].include?(type)
|
|
361
|
+
raise CAStruct::DefinitionError,
|
|
362
|
+
"order_by: #{key.inspect} is complex, which has no order"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
keys.freeze
|
|
311
366
|
end
|
|
367
|
+
private :order_keys
|
|
312
368
|
|
|
313
369
|
# Round up the byte cursor if the bitfield accumulator is mid-byte.
|
|
314
370
|
# Called before placing a non-bit member so the byte member starts
|
|
@@ -364,6 +420,9 @@ class CAStruct::Builder # :nodoc:
|
|
|
364
420
|
klass = Class.new(CAUnion)
|
|
365
421
|
end
|
|
366
422
|
# ---
|
|
423
|
+
# A member placed by an explicit offset can end before an earlier one
|
|
424
|
+
# does, so the body runs to the furthest member, not the last one.
|
|
425
|
+
@offset = @extent if @extent > @offset
|
|
367
426
|
if @align.nil?
|
|
368
427
|
@offset = alignment(@offset, :align_max)
|
|
369
428
|
end
|
|
@@ -406,6 +465,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
406
465
|
names.freeze
|
|
407
466
|
klass.const_set(:MEMBER_TABLE, table) ### required element as data class
|
|
408
467
|
klass.const_set(:MEMBERS, names)
|
|
468
|
+
klass.const_set(:ORDER_BY, order_keys(table)) if @order_by
|
|
409
469
|
# ---
|
|
410
470
|
# Step 3 dispatch table: precompute a per-member [reader, writer]
|
|
411
471
|
# Proc pair so `record[name]` is one Hash lookup + one Proc call,
|
|
@@ -424,14 +484,23 @@ class CAStruct::Builder # :nodoc:
|
|
|
424
484
|
klass.const_set(:FAST_PRIMITIVES,
|
|
425
485
|
self.class.build_fast_primitives(table, @offset).freeze)
|
|
426
486
|
# ---
|
|
487
|
+
# An accessor for each member, unless the name is already a method of
|
|
488
|
+
# the element (hash, size, class, initialize, ...): the accessor would
|
|
489
|
+
# replace it. Such a member is read as s["name"] only, as a record
|
|
490
|
+
# array's members always are.
|
|
427
491
|
klass.module_eval {
|
|
492
|
+
taken = ->(m) { method_defined?(m) || private_method_defined?(m) }
|
|
428
493
|
names.each do |name|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
494
|
+
unless taken.(name)
|
|
495
|
+
define_method(name) {
|
|
496
|
+
return self[name]
|
|
497
|
+
}
|
|
498
|
+
end
|
|
499
|
+
unless taken.("#{name}=")
|
|
500
|
+
define_method("#{name}=") { |val|
|
|
501
|
+
return self[name] = val
|
|
502
|
+
}
|
|
503
|
+
end
|
|
435
504
|
end
|
|
436
505
|
}
|
|
437
506
|
# ---
|
|
@@ -471,6 +540,9 @@ class CAStruct::Builder # :nodoc:
|
|
|
471
540
|
end
|
|
472
541
|
|
|
473
542
|
def pack (addr, align, opt={})
|
|
543
|
+
if addr < 0
|
|
544
|
+
raise CAStruct::DefinitionError, format("negative member offset: %d", addr)
|
|
545
|
+
end
|
|
474
546
|
if ( addr % align ) != 0
|
|
475
547
|
raise CAStruct::DefinitionError,
|
|
476
548
|
format("invalid offset for packing: %d not aligned to %d", addr, align)
|
|
@@ -491,6 +563,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
491
563
|
# @param opt [Hash] member options (`:offset`, `:bytes`, `:endian`, ...).
|
|
492
564
|
# @return [Member]
|
|
493
565
|
def member (data_type, id = nil, opt = {})
|
|
566
|
+
CAStruct::Builder.check_keys!(opt, MEMBER_KEYS, "member")
|
|
494
567
|
opt = opt.clone
|
|
495
568
|
# If a bit member sequence was in flight, round up to a clean byte
|
|
496
569
|
# boundary before placing this byte-typed member.
|
|
@@ -504,6 +577,10 @@ class CAStruct::Builder # :nodoc:
|
|
|
504
577
|
when :struct ### struct
|
|
505
578
|
case @align
|
|
506
579
|
when nil ### -- aligned
|
|
580
|
+
if opt[:offset]
|
|
581
|
+
raise CAStruct::DefinitionError,
|
|
582
|
+
"offset: places a member only in a packed struct (pass pack:)"
|
|
583
|
+
end
|
|
507
584
|
@offset = alignment(@offset, data_type, opt)
|
|
508
585
|
opt[:offset] = @offset
|
|
509
586
|
else ### -- packed
|
|
@@ -516,7 +593,12 @@ class CAStruct::Builder # :nodoc:
|
|
|
516
593
|
mem = Member.new(id, data_type, opt)
|
|
517
594
|
@members.push(mem)
|
|
518
595
|
@offset += mem.byte_length
|
|
596
|
+
@extent = @offset if @offset > @extent
|
|
519
597
|
when :union ### union
|
|
598
|
+
if opt[:offset] && opt[:offset] != 0
|
|
599
|
+
raise CAStruct::DefinitionError,
|
|
600
|
+
"a union member is always at offset 0 (got offset: #{opt[:offset]})"
|
|
601
|
+
end
|
|
520
602
|
alignment(0, data_type, opt)
|
|
521
603
|
opt[:offset] = 0
|
|
522
604
|
mem = Member.new(id, data_type, opt)
|
|
@@ -537,17 +619,19 @@ class CAStruct::Builder # :nodoc:
|
|
|
537
619
|
# `type:` is provided.
|
|
538
620
|
def struct (*args, &block)
|
|
539
621
|
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
622
|
+
CAStruct::Builder.check_keys!(opt, NESTED_KEYS, "struct")
|
|
540
623
|
if block
|
|
541
|
-
|
|
542
|
-
st = self.class.new(:struct,
|
|
624
|
+
inner = {:pack => @align}.update(opt.slice(*BUILDER_KEYS))
|
|
625
|
+
st = self.class.new(:struct, inner).define(&block)
|
|
543
626
|
elsif opt[:type] and opt[:type] <= CAStruct
|
|
544
627
|
st = opt[:type]
|
|
545
628
|
else
|
|
546
629
|
raise CAStruct::DefinitionError,
|
|
547
630
|
"no type given for nested struct member (pass a block or :type)"
|
|
548
631
|
end
|
|
632
|
+
place = opt.key?(:offset) ? {:offset => opt[:offset]} : {}
|
|
549
633
|
args.each do |arg|
|
|
550
|
-
member(st, arg)
|
|
634
|
+
member(st, arg, place)
|
|
551
635
|
end
|
|
552
636
|
return st
|
|
553
637
|
end
|
|
@@ -562,17 +646,19 @@ class CAStruct::Builder # :nodoc:
|
|
|
562
646
|
# `type:` is provided.
|
|
563
647
|
def union (*args, &block)
|
|
564
648
|
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
649
|
+
CAStruct::Builder.check_keys!(opt, NESTED_KEYS, "union")
|
|
565
650
|
if block
|
|
566
|
-
|
|
567
|
-
st = self.class.new(:union,
|
|
651
|
+
inner = {:pack => @align}.update(opt.slice(*BUILDER_KEYS))
|
|
652
|
+
st = self.class.new(:union, inner).define(&block)
|
|
568
653
|
elsif opt[:type] and opt[:type] <= CAStruct
|
|
569
654
|
st = opt[:type]
|
|
570
655
|
else
|
|
571
656
|
raise CAStruct::DefinitionError,
|
|
572
657
|
"no type given for nested union member (pass a block or :type)"
|
|
573
658
|
end
|
|
659
|
+
place = opt.key?(:offset) ? {:offset => opt[:offset]} : {}
|
|
574
660
|
args.each do |arg|
|
|
575
|
-
member(st, arg)
|
|
661
|
+
member(st, arg, place)
|
|
576
662
|
end
|
|
577
663
|
return st
|
|
578
664
|
end
|
|
@@ -585,12 +671,14 @@ class CAStruct::Builder # :nodoc:
|
|
|
585
671
|
# CArray template.
|
|
586
672
|
def array (*args)
|
|
587
673
|
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
674
|
+
CAStruct::Builder.check_keys!(opt, [:type, :offset], "array")
|
|
588
675
|
if not opt[:type] or not opt[:type].kind_of?(CArray)
|
|
589
676
|
raise CAStruct::DefinitionError,
|
|
590
677
|
"no :type given for array member (expected a CArray template)"
|
|
591
678
|
end
|
|
679
|
+
place = opt.key?(:offset) ? {:offset => opt[:offset]} : {}
|
|
592
680
|
args.each do |arg|
|
|
593
|
-
member(opt[:type], arg)
|
|
681
|
+
member(opt[:type], arg, place)
|
|
594
682
|
end
|
|
595
683
|
end
|
|
596
684
|
|
|
@@ -618,6 +706,13 @@ class CAStruct::Builder # :nodoc:
|
|
|
618
706
|
"bit member requires bits: as an Integer in 1..64 (got #{bits.inspect})"
|
|
619
707
|
end
|
|
620
708
|
total_bit_offset = @offset * 8 + @bit_offset
|
|
709
|
+
# A bit member is read from one word of up to 8 bytes starting at the
|
|
710
|
+
# byte it begins in, so it may not reach past the 64th bit of that word.
|
|
711
|
+
if (total_bit_offset % 8) + bits > 64
|
|
712
|
+
raise CAStruct::DefinitionError,
|
|
713
|
+
"bit member #{name.to_s.inspect} (bits: #{bits}) starts at bit " \
|
|
714
|
+
"#{total_bit_offset % 8} of a byte and would span more than 8 bytes"
|
|
715
|
+
end
|
|
621
716
|
mem = Member.new(name.to_s, :bitfield,
|
|
622
717
|
{:bits => bits,
|
|
623
718
|
:bit_offset => total_bit_offset,
|
|
@@ -676,6 +771,7 @@ class CAStruct::Builder # :nodoc:
|
|
|
676
771
|
"#{typename} requires at least one member name " \
|
|
677
772
|
"(use nil for anonymous padding)"
|
|
678
773
|
end
|
|
774
|
+
CAStruct::Builder.check_keys!(opt, MEMBER_KEYS, "#{typename}")
|
|
679
775
|
CAStruct::Builder.validate_endian!(opt, "#{typename}", #{allow_endian})
|
|
680
776
|
args.each do |arg|
|
|
681
777
|
member(:#{typename}, arg, opt)
|
data/lib/carray/table.rb
CHANGED
|
@@ -16,7 +16,7 @@ class CArray
|
|
|
16
16
|
# are silently ignored.
|
|
17
17
|
# @param list [Array<Symbol, String>, nil] column names.
|
|
18
18
|
# @return [Array<Symbol, String>, nil] the assigned list.
|
|
19
|
-
# @raise [
|
|
19
|
+
# @raise [ArgumentError] when `list.size != dim1`.
|
|
20
20
|
def column_names= (list)
|
|
21
21
|
if self.empty?
|
|
22
22
|
return
|
|
@@ -25,7 +25,7 @@ class CArray
|
|
|
25
25
|
elsif list.empty?
|
|
26
26
|
return
|
|
27
27
|
elsif list and list.size != dim1
|
|
28
|
-
raise "column_names list size is invalid (#{list.size} <=> #{dim1} )"
|
|
28
|
+
raise ArgumentError, "column_names list size is invalid (#{list.size} <=> #{dim1} )"
|
|
29
29
|
else
|
|
30
30
|
@column_names = list
|
|
31
31
|
end
|