carray 2.0.0 → 3.0.0
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/.yardopts +5 -25
- data/CHANGELOG.md +16 -0
- data/LICENSE +1 -1
- data/NEWS.md +3 -0
- data/README.md +128 -44
- data/carray.gemspec +22 -24
- data/ext/ca_array_pool.c +91 -0
- data/ext/ca_axis_descriptor.h +186 -0
- data/ext/ca_axis_dispatch.c +924 -0
- data/ext/ca_axis_group.c +1208 -0
- data/ext/ca_bincmp_dispatch.c +76 -0
- data/ext/ca_bincmp_dispatch.h +85 -0
- data/ext/ca_binop_dispatch.c +125 -0
- data/ext/ca_binop_dispatch.h +159 -0
- data/ext/ca_categorical_iterator.c +1375 -0
- data/ext/ca_compare.c +94 -0
- data/ext/ca_compare.h +26 -0
- data/ext/ca_composite_dispatch.c +414 -0
- data/ext/ca_composite_dispatch.h +116 -0
- data/ext/ca_for_buffer.h +96 -0
- data/ext/ca_for_each_element.h +241 -0
- data/ext/ca_group_iter.c +304 -0
- data/ext/ca_iter_substrate.h +325 -0
- data/ext/ca_kernel_iterator.c +4321 -0
- data/ext/ca_kernel_iterator.h +2603 -0
- data/ext/ca_moncmp_dispatch.c +37 -0
- data/ext/ca_moncmp_dispatch.h +62 -0
- data/ext/ca_monop_dispatch.c +200 -0
- data/ext/ca_monop_dispatch.h +235 -0
- data/ext/ca_obj_array.c +355 -359
- data/ext/ca_obj_bincmp.c +809 -0
- data/ext/ca_obj_binop.c +892 -0
- data/ext/ca_obj_bitarray.c +369 -164
- data/ext/ca_obj_bitfield.c +294 -234
- data/ext/ca_obj_block.c +189 -711
- data/ext/ca_obj_byte_swap.c +766 -0
- data/ext/ca_obj_const_string.c +965 -0
- data/ext/ca_obj_face.c +670 -0
- data/ext/ca_obj_face.h +247 -0
- data/ext/ca_obj_fake.c +228 -100
- data/ext/ca_obj_farray.c +54 -441
- data/ext/ca_obj_field.c +82 -529
- data/ext/ca_obj_fixlen_string.c +306 -0
- data/ext/ca_obj_grid.c +858 -440
- data/ext/ca_obj_meld.c +1034 -0
- data/ext/ca_obj_moncmp.c +569 -0
- data/ext/ca_obj_monop.c +1111 -0
- data/ext/ca_obj_object.c +774 -298
- data/ext/ca_obj_record.c +468 -0
- data/ext/ca_obj_reduce.c +97 -82
- data/ext/ca_obj_refer.c +569 -459
- data/ext/ca_obj_remap.c +475 -0
- data/ext/ca_obj_repeat.c +92 -477
- data/ext/ca_obj_roll.c +616 -0
- data/ext/ca_obj_select.c +344 -296
- data/ext/ca_obj_select_axis.c +1296 -0
- data/ext/ca_obj_shift.c +230 -792
- data/ext/ca_obj_source.c +78 -0
- data/ext/ca_obj_stack.c +1173 -0
- data/ext/ca_obj_stride.c +2501 -0
- data/ext/ca_obj_string.c +268 -0
- data/ext/ca_obj_tile.c +614 -0
- data/ext/ca_obj_time.c +546 -0
- data/ext/ca_obj_timedelta.c +435 -0
- data/ext/ca_obj_transpose.c +62 -516
- data/ext/ca_obj_triop.c +746 -0
- data/ext/ca_obj_unbound_repeat.c +208 -241
- data/ext/ca_obj_window.c +1131 -563
- data/ext/ca_op_byte_swap.c +175 -0
- data/ext/ca_op_ipower.c +319 -0
- data/ext/ca_op_powi.h +88 -0
- data/ext/ca_sort_kernels.h +132 -0
- data/ext/ca_sweep_engine.c +430 -0
- data/ext/ca_sweep_engine.h +157 -0
- data/ext/ca_transform_common.c +228 -0
- data/ext/ca_triop_dispatch.c +55 -0
- data/ext/ca_triop_dispatch.h +62 -0
- data/ext/carray.h +795 -402
- data/ext/carray_access.c +831 -711
- data/ext/carray_attribute.c +98 -330
- data/ext/carray_bincount.c +255 -0
- data/ext/carray_broadcast.c +283 -0
- data/ext/carray_call_cfunc.c +1360 -828
- data/ext/carray_call_cfunc.h +160 -0
- data/ext/carray_cast.c +1212 -301
- data/ext/carray_cast_func.rb +81 -40
- data/ext/carray_class.c +53 -63
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +350 -346
- data/ext/carray_copy.c +156 -268
- data/ext/carray_core.c +1342 -199
- data/ext/carray_count.c +312 -0
- data/ext/carray_data_type.c +43 -19
- data/ext/carray_element.c +585 -213
- data/ext/carray_factorize.c +2542 -0
- data/ext/carray_generate.c +230 -559
- data/ext/carray_histogram.c +490 -0
- data/ext/carray_hold.c +228 -0
- data/ext/carray_index_classifier.c +1035 -0
- data/ext/carray_index_classifier.h +27 -0
- data/ext/carray_internal.h +120 -0
- data/ext/carray_kernels_bincmp.c +4445 -0
- data/ext/carray_kernels_binop.c +10979 -0
- data/ext/carray_kernels_init.c +36 -0
- data/ext/carray_kernels_map.c +3466 -0
- data/ext/carray_kernels_moncmp.c +2096 -0
- data/ext/carray_kernels_monop.c +18312 -0
- data/ext/carray_kernels_reduce_aggregate.c +25836 -0
- data/ext/carray_kernels_reduce_boolean.c +329 -0
- data/ext/carray_kernels_reduce_cumulative.c +14592 -0
- data/ext/carray_kernels_reduce_extreme.c +16947 -0
- data/ext/carray_kernels_reduce_variance.c +3909 -0
- data/ext/carray_kernels_scan.c +3692 -0
- data/ext/carray_kernels_search.c +32137 -0
- data/ext/carray_kernels_sort.c +10625 -0
- data/ext/carray_kernels_triop.c +1391 -0
- data/ext/carray_lazy.c +567 -0
- data/ext/carray_loop.c +88 -200
- data/ext/carray_mask.c +848 -154
- data/ext/carray_math_kernel.h +120 -0
- data/ext/carray_mathfunc.c +10 -241
- data/ext/carray_median_percentile.c +1257 -0
- data/ext/carray_memory_view.c +1625 -0
- data/ext/carray_operator.c +1526 -318
- data/ext/carray_order.c +664 -1394
- data/ext/carray_partition.c +416 -0
- data/ext/carray_random.c +518 -0
- data/ext/carray_scatter.c +357 -0
- data/ext/carray_slab.c +1219 -0
- data/ext/carray_slab.h +84 -0
- data/ext/carray_sort.c +829 -0
- data/ext/carray_sort_kernel.c +620 -0
- data/ext/carray_struct.c +695 -0
- data/ext/carray_test.c +343 -229
- data/ext/carray_undef.c +34 -17
- data/ext/carray_utils.c +175 -74
- data/ext/extconf.rb +216 -55
- data/ext/mk_call_cfunc.rb +480 -0
- data/ext/mkkernel.rb +8842 -0
- data/ext/ruby_carray.c +202 -101
- data/ext/version.h +4 -14
- data/ext/version.rb +5 -13
- data/lib/carray/arrow_tensor.rb +401 -0
- data/lib/carray/attribute.rb +166 -0
- data/lib/carray/autoload_carray.rb +220 -0
- data/lib/carray/autoload_method_extension.rb +44 -0
- data/lib/carray/axis_group.rb +711 -0
- data/lib/carray/basics.rb +481 -0
- data/lib/carray/bincount_nd.rb +358 -0
- data/lib/carray/block_iterator.rb +604 -0
- data/lib/carray/boolean_reduce.rb +109 -0
- data/lib/carray/categorical.rb +561 -0
- data/lib/carray/categorical_iterator.rb +1062 -0
- data/lib/carray/complex.rb +150 -0
- data/lib/carray/conditional.rb +216 -0
- data/lib/carray/const_string.rb +228 -0
- data/lib/carray/construct.rb +139 -328
- data/lib/carray/core_extensions.rb +240 -0
- data/lib/carray/data_type_extension.rb +233 -0
- data/lib/carray/fixlen_string.rb +95 -0
- data/lib/carray/frame/concat.rb +132 -0
- data/lib/carray/frame/convert.rb +95 -0
- data/lib/carray/frame/csv_parser.rb +211 -0
- data/lib/carray/frame/frame.rb +649 -0
- data/lib/carray/frame/group.rb +186 -0
- data/lib/carray/frame/io.rb +164 -0
- data/lib/carray/frame/join.rb +248 -0
- data/lib/carray/frame/records.rb +99 -0
- data/lib/carray/frame/sort.rb +113 -0
- data/lib/carray/frame/verbs.rb +299 -0
- data/lib/carray/frame.rb +16 -0
- data/lib/carray/histogram.rb +512 -0
- data/lib/carray/inspect.rb +37 -20
- data/lib/carray/iterator.rb +57 -349
- data/lib/carray/lazy.rb +889 -0
- data/lib/carray/mask_gap_fill.rb +200 -0
- data/lib/carray/math.rb +78 -342
- data/lib/carray/meld_reduce.rb +289 -0
- data/lib/carray/methods/align_addr.rb +116 -0
- data/lib/carray/methods/bin.rb +128 -0
- data/lib/carray/methods/bincount.rb +87 -0
- data/lib/carray/methods/bit_string.rb +92 -0
- data/lib/carray/methods/broadcast.rb +63 -0
- data/lib/carray/methods/choose.rb +39 -0
- data/lib/carray/methods/composition.rb +280 -0
- data/lib/carray/methods/gather_nd.rb +206 -0
- data/lib/carray/methods/index.rb +39 -0
- data/lib/carray/methods/insert_block.rb +99 -0
- data/lib/carray/methods/is_in.rb +141 -0
- data/lib/carray/methods/join.rb +90 -0
- data/lib/carray/methods/locate_addr.rb +47 -0
- data/lib/carray/methods/mask_duplicates.rb +41 -0
- data/lib/carray/methods/meshgrid.rb +91 -0
- data/lib/carray/methods/mode.rb +126 -0
- data/lib/carray/methods/nunique.rb +46 -0
- data/lib/carray/methods/resize.rb +56 -0
- data/lib/carray/methods/snap.rb +156 -0
- data/lib/carray/methods/string_format.rb +57 -0
- data/lib/carray/methods/unique.rb +47 -0
- data/lib/carray/methods/value_counts.rb +71 -0
- data/lib/carray/mkmf.rb +124 -101
- data/lib/carray/runtime.rb +108 -0
- data/lib/carray/serialize.rb +478 -167
- data/lib/carray/slab_iterator.rb +292 -0
- data/lib/carray/stack.rb +291 -0
- data/lib/carray/string.rb +56 -180
- data/lib/carray/string_operation_extension.rb +289 -0
- data/lib/carray/struct.rb +335 -323
- data/lib/carray/struct_builder.rb +697 -0
- data/lib/carray/table.rb +41 -2
- data/lib/carray/time.rb +2255 -38
- data/lib/carray/window_iterator.rb +655 -0
- data/lib/carray.rb +55 -57
- metadata +163 -130
- data/Rakefile +0 -51
- data/TODO.md +0 -18
- data/ext/ca_iter_block.c +0 -257
- data/ext/ca_iter_dimension.c +0 -299
- data/ext/ca_iter_window.c +0 -214
- data/ext/ca_obj_mapping.c +0 -644
- data/ext/carray_iterator.c +0 -641
- data/ext/carray_math.rb +0 -850
- data/ext/carray_numeric.c +0 -259
- data/ext/carray_sort_addr.c +0 -254
- data/ext/carray_stat.c +0 -2100
- data/ext/carray_stat_proc.rb +0 -1999
- data/ext/mkmath.rb +0 -741
- data/ext/ruby_ccomplex.c +0 -509
- data/ext/ruby_float_func.c +0 -86
- data/lib/carray/array.rb +0 -8
- data/lib/carray/autoload/autoload_base.rb +0 -19
- data/lib/carray/autoload/autoload_gem_cairo.rb +0 -9
- data/lib/carray/autoload/autoload_gem_ffi.rb +0 -9
- data/lib/carray/autoload/autoload_gem_gnuplot.rb +0 -2
- data/lib/carray/autoload/autoload_gem_io_csv.rb +0 -14
- data/lib/carray/autoload/autoload_gem_io_pg.rb +0 -6
- data/lib/carray/autoload/autoload_gem_io_sqlite3.rb +0 -12
- data/lib/carray/autoload/autoload_gem_narray.rb +0 -10
- data/lib/carray/autoload/autoload_gem_numo_narray.rb +0 -15
- data/lib/carray/autoload/autoload_gem_opencv.rb +0 -16
- data/lib/carray/autoload/autoload_gem_random.rb +0 -8
- data/lib/carray/autoload/autoload_gem_rmagick.rb +0 -23
- data/lib/carray/autoload/autoload_gem_zimg.rb +0 -3
- data/lib/carray/autoload/autoload_io_imagemagick.rb +0 -6
- data/lib/carray/autoload/autoload_math_histogram.rb +0 -5
- data/lib/carray/autoload/autoload_math_recurrence.rb +0 -6
- data/lib/carray/autoload/autoload_object_iterator.rb +0 -1
- data/lib/carray/autoload/autoload_object_link.rb +0 -1
- data/lib/carray/autoload/autoload_object_pack.rb +0 -2
- data/lib/carray/autoload.rb +0 -141
- data/lib/carray/basic.rb +0 -191
- data/lib/carray/broadcast.rb +0 -101
- data/lib/carray/compose.rb +0 -315
- data/lib/carray/convert.rb +0 -115
- data/lib/carray/info.rb +0 -110
- data/lib/carray/io/imagemagick.rb +0 -235
- data/lib/carray/mask.rb +0 -102
- data/lib/carray/math/histogram.rb +0 -177
- data/lib/carray/math/recurrence.rb +0 -93
- data/lib/carray/object/ca_obj_iterator.rb +0 -50
- data/lib/carray/object/ca_obj_link.rb +0 -50
- data/lib/carray/object/ca_obj_pack.rb +0 -99
- data/lib/carray/obsolete.rb +0 -256
- data/lib/carray/ordering.rb +0 -181
- data/lib/carray/testing.rb +0 -51
- data/lib/carray/transform.rb +0 -109
- data/misc/Methods.ja.md +0 -182
- data/misc/NOTE +0 -51
- data/spec/Classes/CABitfield_spec.rb +0 -58
- data/spec/Classes/CABlockIterator_spec.rb +0 -114
- data/spec/Classes/CABlock_spec.rb +0 -205
- data/spec/Classes/CAField_spec.rb +0 -39
- data/spec/Classes/CAGrid_spec.rb +0 -75
- data/spec/Classes/CAMap_spec.rb +0 -0
- data/spec/Classes/CAMapping_spec.rb +0 -105
- data/spec/Classes/CAObject_attribute_spec.rb +0 -33
- data/spec/Classes/CAObject_spec.rb +0 -33
- data/spec/Classes/CARefer_spec.rb +0 -93
- data/spec/Classes/CARepeat_spec.rb +0 -65
- data/spec/Classes/CASelect_spec.rb +0 -22
- data/spec/Classes/CAShift_spec.rb +0 -16
- data/spec/Classes/CAStruct_spec.rb +0 -71
- data/spec/Classes/CATranspose_spec.rb +0 -60
- data/spec/Classes/CAUnboudRepeat_spec.rb +0 -102
- data/spec/Classes/CAWindow_spec.rb +0 -54
- data/spec/Classes/CAWrap_spec.rb +0 -8
- data/spec/Classes/CArray_spec.rb +0 -184
- data/spec/Classes/CScalar_spec.rb +0 -55
- data/spec/Classes/ex1.rb +0 -46
- data/spec/Features/feature_130_spec.rb +0 -19
- data/spec/Features/feature_attributes_spec.rb +0 -280
- data/spec/Features/feature_boolean_spec.rb +0 -98
- data/spec/Features/feature_broadcast.rb +0 -116
- data/spec/Features/feature_cast_function.rb +0 -19
- data/spec/Features/feature_cast_spec.rb +0 -33
- data/spec/Features/feature_class_spec.rb +0 -84
- data/spec/Features/feature_complex_spec.rb +0 -42
- data/spec/Features/feature_composite_spec.rb +0 -124
- data/spec/Features/feature_convert_spec.rb +0 -46
- data/spec/Features/feature_copy_spec.rb +0 -123
- data/spec/Features/feature_creation_spec.rb +0 -84
- data/spec/Features/feature_element_spec.rb +0 -144
- data/spec/Features/feature_extream_spec.rb +0 -54
- data/spec/Features/feature_generate_spec.rb +0 -74
- data/spec/Features/feature_index_spec.rb +0 -69
- data/spec/Features/feature_mask_spec.rb +0 -580
- data/spec/Features/feature_math_spec.rb +0 -97
- data/spec/Features/feature_order_spec.rb +0 -146
- data/spec/Features/feature_ref_store_spec.rb +0 -209
- data/spec/Features/feature_serialization_spec.rb +0 -125
- data/spec/Features/feature_stat_spec.rb +0 -397
- data/spec/Features/feature_virtual_spec.rb +0 -48
- data/spec/Features/method_eq_spec.rb +0 -81
- data/spec/Features/method_is_nan_spec.rb +0 -12
- data/spec/Features/method_map_spec.rb +0 -54
- data/spec/Features/method_max_with.rb +0 -20
- data/spec/Features/method_min_with.rb +0 -19
- data/spec/Features/method_ne_spec.rb +0 -18
- data/spec/Features/method_project_spec.rb +0 -188
- data/spec/Features/method_ref_spec.rb +0 -27
- data/spec/Features/method_round_spec.rb +0 -11
- data/spec/Features/method_s_linspace_spec.rb +0 -48
- data/spec/Features/method_s_span_spec.rb +0 -14
- data/spec/Features/method_seq_spec.rb +0 -47
- data/spec/Features/method_sort_with.rb +0 -43
- data/spec/Features/method_sorted_with.rb +0 -29
- data/spec/Features/method_span_spec.rb +0 -42
- data/spec/Features/method_wrap_readonly_spec.rb +0 -43
- data/spec/UnitTest/test_CAVirtual.rb +0 -214
- data/spec/spec_all.rb +0 -10
- data/utils/ca_ase.rb +0 -21
- data/utils/ca_methods.rb +0 -15
- data/utils/cast_checker.rb +0 -30
- data/utils/convert_test.rb +0 -73
- data/utils/extract_yard.rb +0 -22
- data/utils/guess_shape.rb +0 -76
- data/utils/monkey_patch_methods.rb +0 -62
- data/utils/remove_resource_fork.sh +0 -5
data/lib/carray/struct.rb
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
# ----------------------------------------------------------------------------
|
|
2
|
-
#
|
|
3
|
-
# carray/struct.rb
|
|
4
|
-
#
|
|
5
|
-
# This file is part of Ruby/CArray extension library.
|
|
6
|
-
#
|
|
7
|
-
# Copyright (C) 2005-2020 Hiroki Motoyoshi
|
|
8
|
-
#
|
|
9
|
-
# ----------------------------------------------------------------------------
|
|
10
|
-
#
|
|
11
1
|
# The data class for fixed length carray are required to satisfy only
|
|
12
2
|
# five conditions.
|
|
13
3
|
#
|
|
@@ -27,7 +17,7 @@
|
|
|
27
17
|
# :size => 1024 # user defined size (with padding)
|
|
28
18
|
# }
|
|
29
19
|
#
|
|
30
|
-
#
|
|
20
|
+
# CArray.struct(option) { |s|
|
|
31
21
|
#
|
|
32
22
|
# # numeric types
|
|
33
23
|
#
|
|
@@ -49,11 +39,11 @@
|
|
|
49
39
|
#
|
|
50
40
|
# # struct type
|
|
51
41
|
# struct(:st1, :st2) { uint8 :a, :b, :c }
|
|
52
|
-
# struct :st3, :st4, :type =>
|
|
42
|
+
# struct :st3, :st4, :type => CArray.struct { uint8 :a, :b, :c }
|
|
53
43
|
#
|
|
54
44
|
# # union type
|
|
55
45
|
# union(:un1, :un2) { uint8 :a; int16 :b; float32 :c }
|
|
56
|
-
# union :un3, :un4, :type =>
|
|
46
|
+
# union :un3, :un4, :type => CArray.union { uint8 :a, :b, :c }
|
|
57
47
|
#
|
|
58
48
|
# # anonymous
|
|
59
49
|
#
|
|
@@ -70,13 +60,112 @@
|
|
|
70
60
|
# member union{ int8 :a; int16 :b; float :c }, :st5, :st6
|
|
71
61
|
#
|
|
72
62
|
# }
|
|
73
|
-
|
|
63
|
+
|
|
64
|
+
class CAStruct
|
|
65
|
+
# CAStruct::Builder is the DSL backend behind CArray.struct / CArray.union.
|
|
66
|
+
# It is only touched at struct-definition time (= the body of a
|
|
67
|
+
# `CArray.struct { ... }` block); programs that only consume pre-defined
|
|
68
|
+
# struct classes never reference it. Lazy-load on first lookup.
|
|
69
|
+
autoload :Builder, "carray/struct_builder"
|
|
70
|
+
|
|
71
|
+
# Generic base for all CAStruct-related errors. Callers can rescue
|
|
72
|
+
# this class to catch any struct-side failure regardless of which
|
|
73
|
+
# specific sub-error fired.
|
|
74
|
+
class Error < StandardError; end
|
|
75
|
+
|
|
76
|
+
# Raised by CAStruct::Builder when the struct/union definition is
|
|
77
|
+
# malformed (duplicate member name, empty typed-method call, etc.).
|
|
78
|
+
class DefinitionError < Error; end
|
|
79
|
+
|
|
80
|
+
# Raised by CAStruct#decode / #encode when the input cannot be
|
|
81
|
+
# interpreted as the struct's binary representation.
|
|
82
|
+
class DecodeError < Error; end
|
|
83
|
+
|
|
84
|
+
# Reflection record for a single struct member. Frozen by default
|
|
85
|
+
# so callers can treat it as a value object. Lifted from
|
|
86
|
+
# `MEMBER_TABLE`'s `[offset, type, opts]` tuple to give first-class
|
|
87
|
+
# access to member metadata without exposing the opaque hash shape.
|
|
88
|
+
#
|
|
89
|
+
# Two flavours:
|
|
90
|
+
#
|
|
91
|
+
# - Regular byte-typed member: `name` / `offset` / `type` / `bytes`
|
|
92
|
+
# describe the placement. For typed primitives, `type` is the
|
|
93
|
+
# Symbol (`:int32` etc.); for nested CAStruct subclass members,
|
|
94
|
+
# `type` is the class; for CArray-template members, `type` is the
|
|
95
|
+
# template CArray.
|
|
96
|
+
# - Bit member: `bitfield?` returns true; `bits` and
|
|
97
|
+
# `bit_offset` hold the bit-level placement. `offset` is the
|
|
98
|
+
# byte containing the LSB of the field (for raw-record indexing),
|
|
99
|
+
# `bytes` is nil.
|
|
100
|
+
class Field
|
|
101
|
+
attr_reader :name, :offset, :type, :bytes, :bits, :bit_offset, :endian
|
|
102
|
+
|
|
103
|
+
def initialize (name:, offset:, type:, bytes: nil,
|
|
104
|
+
bits: nil, bit_offset: nil, endian: nil)
|
|
105
|
+
@name = name
|
|
106
|
+
@offset = offset
|
|
107
|
+
@type = type
|
|
108
|
+
@bytes = bytes
|
|
109
|
+
@bits = bits
|
|
110
|
+
@bit_offset = bit_offset
|
|
111
|
+
@endian = endian
|
|
112
|
+
freeze
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def bitfield?
|
|
116
|
+
@type == :bitfield
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @return [String]
|
|
120
|
+
def inspect
|
|
121
|
+
if bitfield?
|
|
122
|
+
format("#<%s %p bitfield bits=%d bit_offset=%d>",
|
|
123
|
+
self.class, @name, @bits, @bit_offset)
|
|
124
|
+
else
|
|
125
|
+
endian_part = @endian ? format(" endian=%p", @endian) : ""
|
|
126
|
+
format("#<%s %p type=%p offset=%d bytes=%s%s>",
|
|
127
|
+
self.class, @name, @type, @offset, @bytes.inspect,
|
|
128
|
+
endian_part)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
alias to_s inspect
|
|
133
|
+
|
|
134
|
+
# Field-wise comparison of the member's declaration (name, offset, type,
|
|
135
|
+
# width, bit placement, endianness). Aliased as `eql?`.
|
|
136
|
+
# @return [Boolean]
|
|
137
|
+
def == (other)
|
|
138
|
+
other.is_a?(Field) &&
|
|
139
|
+
@name == other.name &&
|
|
140
|
+
@offset == other.offset &&
|
|
141
|
+
@type == other.type &&
|
|
142
|
+
@bytes == other.bytes &&
|
|
143
|
+
@bits == other.bits &&
|
|
144
|
+
@bit_offset == other.bit_offset &&
|
|
145
|
+
@endian == other.endian
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
alias eql? ==
|
|
149
|
+
|
|
150
|
+
# @return [Integer] a hash consistent with #==.
|
|
151
|
+
def hash
|
|
152
|
+
[@name, @offset, @type, @bytes, @bits, @bit_offset, @endian].hash
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
end
|
|
74
157
|
|
|
75
158
|
class CArray
|
|
76
159
|
|
|
160
|
+
# @overload st
|
|
161
|
+
# Returns a Ruby `Struct` view exposing every {CAStruct} member
|
|
162
|
+
# of `self` as a `Struct` attribute holding the corresponding
|
|
163
|
+
# member column. Cached per receiver.
|
|
164
|
+
# @return [Struct]
|
|
165
|
+
# @raise [CAStruct::Error] when `self` has no `data_class`.
|
|
77
166
|
def st
|
|
78
167
|
unless has_data_class?
|
|
79
|
-
raise "
|
|
168
|
+
raise CAStruct::Error, "carray does not have a data_class"
|
|
80
169
|
end
|
|
81
170
|
unless @struct
|
|
82
171
|
struct_class = Struct.new(nil, *data_class::MEMBERS)
|
|
@@ -94,37 +183,128 @@ class CAStruct
|
|
|
94
183
|
|
|
95
184
|
class << self
|
|
96
185
|
|
|
186
|
+
# @return [String] the struct's name, or `"AnonStruct"` when it was
|
|
187
|
+
# defined anonymously.
|
|
97
188
|
def inspect
|
|
98
189
|
return name.nil? ? "AnonStruct" : name
|
|
99
190
|
end
|
|
100
191
|
|
|
192
|
+
# @overload [](*values)
|
|
193
|
+
# Returns a new struct record whose members are set from
|
|
194
|
+
# `values` in declaration order. Missing values leave the
|
|
195
|
+
# corresponding member at its default; extra values raise.
|
|
196
|
+
# @param values [Array<Object>]
|
|
197
|
+
# @return [CAStruct]
|
|
198
|
+
# @raise [ArgumentError] when too many values are given.
|
|
101
199
|
def [] (*argv)
|
|
200
|
+
if argv.size > self::MEMBERS.size
|
|
201
|
+
raise ArgumentError,
|
|
202
|
+
format("too many arguments for %s.[] (<%i> for <%i>)",
|
|
203
|
+
inspect, argv.size, self::MEMBERS.size)
|
|
204
|
+
end
|
|
102
205
|
obj = new()
|
|
103
206
|
members.each do |name|
|
|
207
|
+
break if argv.empty?
|
|
104
208
|
obj[name] = argv.shift
|
|
105
209
|
end
|
|
106
210
|
return obj
|
|
107
211
|
end
|
|
108
212
|
|
|
213
|
+
# @overload members
|
|
214
|
+
# Returns the member name list in declaration order.
|
|
215
|
+
# @return [Array<String>]
|
|
109
216
|
def members
|
|
110
|
-
return self::MEMBERS
|
|
111
|
-
end
|
|
112
|
-
|
|
217
|
+
return self::MEMBERS
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# @overload fields
|
|
221
|
+
# Returns the struct's members as {CAStruct::Field} objects
|
|
222
|
+
# in declaration order. Cached on the class.
|
|
223
|
+
# @return [Array<CAStruct::Field>]
|
|
224
|
+
def fields
|
|
225
|
+
@__fields__ ||= self::MEMBERS.map { |name|
|
|
226
|
+
offset, type, opts = *self::MEMBER_TABLE[name]
|
|
227
|
+
opts ||= {}
|
|
228
|
+
if type == :bitfield
|
|
229
|
+
CAStruct::Field.new(name: name,
|
|
230
|
+
offset: offset,
|
|
231
|
+
type: :bitfield,
|
|
232
|
+
bits: opts[:bits],
|
|
233
|
+
bit_offset: opts[:bit_offset])
|
|
234
|
+
else
|
|
235
|
+
CAStruct::Field.new(name: name,
|
|
236
|
+
offset: offset,
|
|
237
|
+
type: type,
|
|
238
|
+
bytes: opts[:bytes],
|
|
239
|
+
endian: opts[:endian])
|
|
240
|
+
end
|
|
241
|
+
}.freeze
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# @overload field_info(name)
|
|
245
|
+
# Returns the {CAStruct::Field} for `name`, or `nil` when the
|
|
246
|
+
# member is unknown.
|
|
247
|
+
# @param name [Symbol, String]
|
|
248
|
+
# @return [CAStruct::Field, nil]
|
|
249
|
+
def field_info (name)
|
|
250
|
+
key = name.to_s
|
|
251
|
+
fields.find { |f| f.name == key }
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
# @overload offset_of(name)
|
|
255
|
+
# Returns the offset of member `name`: bytes for regular
|
|
256
|
+
# members, bits for bit members. `nil` when the member is
|
|
257
|
+
# unknown.
|
|
258
|
+
# @param name [Symbol, String]
|
|
259
|
+
# @return [Integer, nil]
|
|
260
|
+
def offset_of (name)
|
|
261
|
+
f = field_info(name)
|
|
262
|
+
return nil unless f
|
|
263
|
+
f.bitfield? ? f.bit_offset : f.offset
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# @overload decode(data)
|
|
267
|
+
# Returns a new struct record built from the binary `data`
|
|
268
|
+
# (String or CArray).
|
|
269
|
+
# @param data [String, CArray]
|
|
270
|
+
# @return [CAStruct]
|
|
113
271
|
def decode (data) ### required element as data class
|
|
114
272
|
return new.decode(data)
|
|
115
273
|
end
|
|
116
|
-
|
|
274
|
+
|
|
275
|
+
# @overload size
|
|
276
|
+
# Returns the byte size of the struct's layout.
|
|
277
|
+
# @return [Integer]
|
|
117
278
|
def size
|
|
118
279
|
return self::DATA_SIZE
|
|
119
280
|
end
|
|
120
281
|
|
|
121
282
|
end
|
|
122
283
|
|
|
284
|
+
# @overload initialize(*values)
|
|
285
|
+
# Allocates a new struct record. Members are set from the
|
|
286
|
+
# trailing positional `values` in declaration order, or from a
|
|
287
|
+
# single Hash argument keyed by member name.
|
|
288
|
+
# @param values [Array<Object>, Array<Hash>]
|
|
289
|
+
# @raise [ArgumentError] on unknown Hash keys or an excess of
|
|
290
|
+
# positional values.
|
|
123
291
|
def initialize (*argv)
|
|
124
292
|
@data = CScalar.new(self.class)
|
|
125
293
|
mems = members
|
|
126
294
|
if argv.size == 1 and argv.first.is_a?(Hash)
|
|
127
|
-
|
|
295
|
+
# Validate keys up-front so the caller gets one clear error
|
|
296
|
+
# listing all unknown keys, rather than a NoMethodError from the
|
|
297
|
+
# first one and silent partial-init for the rest.
|
|
298
|
+
known = self.class::MEMBER_TABLE
|
|
299
|
+
unknown = argv.first.keys.reject { |k| known.key?(k.to_s) }
|
|
300
|
+
unless unknown.empty?
|
|
301
|
+
raise ArgumentError,
|
|
302
|
+
format("unknown member(s) for %s: %s (known: %s)",
|
|
303
|
+
self.class.inspect,
|
|
304
|
+
unknown.map(&:inspect).join(", "),
|
|
305
|
+
mems.map(&:inspect).join(", "))
|
|
306
|
+
end
|
|
307
|
+
argv.first.each do |k, v|
|
|
128
308
|
self[k] = v
|
|
129
309
|
end
|
|
130
310
|
elsif argv.size <= mems.size
|
|
@@ -132,9 +312,9 @@ class CAStruct
|
|
|
132
312
|
self[mems[i]] = v
|
|
133
313
|
end
|
|
134
314
|
else
|
|
135
|
-
raise ArgumentError,
|
|
136
|
-
format("too many arguments for %s.new (<%i> for <%i>)",
|
|
137
|
-
self.class.inspect, argv.size,
|
|
315
|
+
raise ArgumentError,
|
|
316
|
+
format("too many arguments for %s.new (<%i> for <%i>)",
|
|
317
|
+
self.class.inspect, argv.size, mems.size)
|
|
138
318
|
end
|
|
139
319
|
end
|
|
140
320
|
|
|
@@ -146,72 +326,104 @@ class CAStruct
|
|
|
146
326
|
|
|
147
327
|
public
|
|
148
328
|
|
|
329
|
+
# Member access (`record[:name]` / `record["name"]` / `record[i]`).
|
|
330
|
+
# Step 3 (3.0): the per-type `case` was replaced by a class-level
|
|
331
|
+
# DISPATCH_TABLE built once at struct-definition time. Each known
|
|
332
|
+
# member has a frozen `[reader_proc, writer_proc]` pair that closes
|
|
333
|
+
# over its offset / type / opts, so the per-call work is one Hash
|
|
334
|
+
# lookup + one Proc#call. Unknown names fall back to `send(name)`
|
|
335
|
+
# so subclasses can define computed members via plain Ruby methods.
|
|
336
|
+
# @overload [](name)
|
|
337
|
+
# Returns the value of member `name` (Integer indexes the
|
|
338
|
+
# member list). Unknown names fall through to `send(name)`.
|
|
339
|
+
# @param name [Symbol, String, Integer]
|
|
340
|
+
# @return [Object]
|
|
149
341
|
def [] (name)
|
|
150
342
|
if name.kind_of?(Integer)
|
|
151
343
|
name = members[name]
|
|
152
344
|
end
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
return send(name)
|
|
157
|
-
when Class
|
|
158
|
-
return type.decode(@data.field(offset, type))
|
|
159
|
-
when CArray
|
|
160
|
-
return @data.field(offset,type)[0,false]
|
|
345
|
+
pair = self.class::DISPATCH_TABLE[name.to_s]
|
|
346
|
+
if pair
|
|
347
|
+
pair[0].call(@data)
|
|
161
348
|
else
|
|
162
|
-
|
|
349
|
+
send(name)
|
|
163
350
|
end
|
|
164
351
|
end
|
|
165
352
|
|
|
353
|
+
# @overload []=(name, val)
|
|
354
|
+
# Sets member `name` to `val`. Unknown names fall through to
|
|
355
|
+
# `send("#{name}=", val)`.
|
|
356
|
+
# @param name [Symbol, String, Integer]
|
|
357
|
+
# @param val [Object]
|
|
358
|
+
# @return [Object] `val`.
|
|
166
359
|
def []= (name, val)
|
|
167
360
|
if name.kind_of?(Integer)
|
|
168
361
|
name = members[name]
|
|
169
362
|
end
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
send(name.to_s + "=", val)
|
|
174
|
-
when Class
|
|
175
|
-
@data.field(offset, type)[0] = val
|
|
176
|
-
when CArray
|
|
177
|
-
@data.field(offset, type)[0,false] = val
|
|
363
|
+
pair = self.class::DISPATCH_TABLE[name.to_s]
|
|
364
|
+
if pair
|
|
365
|
+
pair[1].call(@data, val)
|
|
178
366
|
else
|
|
179
|
-
|
|
367
|
+
send(name.to_s + "=", val)
|
|
180
368
|
end
|
|
181
369
|
end
|
|
182
370
|
|
|
371
|
+
# @overload each { |value| ... }
|
|
372
|
+
# Yields each member value in declaration order.
|
|
373
|
+
# @yieldparam value [Object]
|
|
374
|
+
# @return [self]
|
|
183
375
|
def each
|
|
184
376
|
members.each do |name|
|
|
185
377
|
yield(self[name])
|
|
186
378
|
end
|
|
187
379
|
end
|
|
188
380
|
|
|
381
|
+
# @overload each_pair { |name, value| ... }
|
|
382
|
+
# Yields each member as a `(name, value)` pair in declaration
|
|
383
|
+
# order.
|
|
384
|
+
# @yieldparam name [Symbol]
|
|
385
|
+
# @yieldparam value [Object]
|
|
386
|
+
# @return [self]
|
|
189
387
|
def each_pair
|
|
190
388
|
members.each do |name|
|
|
191
389
|
yield(name.intern, self[name])
|
|
192
390
|
end
|
|
193
391
|
end
|
|
194
392
|
|
|
393
|
+
# @overload length
|
|
394
|
+
# Returns the number of members.
|
|
395
|
+
# @return [Integer]
|
|
195
396
|
def length
|
|
196
397
|
return self.class::MEMBERS.length
|
|
197
398
|
end
|
|
198
|
-
|
|
399
|
+
|
|
199
400
|
alias size length
|
|
200
401
|
|
|
402
|
+
# @overload members
|
|
403
|
+
# Returns the member name list in declaration order.
|
|
404
|
+
# @return [Array<String>]
|
|
201
405
|
def members
|
|
202
|
-
return self.class::MEMBERS
|
|
406
|
+
return self.class::MEMBERS
|
|
203
407
|
end
|
|
204
408
|
|
|
409
|
+
# @overload values
|
|
410
|
+
# Returns the member values in declaration order.
|
|
411
|
+
# @return [Array<Object>]
|
|
205
412
|
def values
|
|
206
413
|
return members.map{|name| self[name] }
|
|
207
414
|
end
|
|
208
415
|
|
|
209
416
|
alias to_a values
|
|
210
417
|
|
|
418
|
+
# @overload values_at(*names)
|
|
419
|
+
# Returns the values of the named members in the given order.
|
|
420
|
+
# @param names [Array<Symbol, String, Integer>]
|
|
421
|
+
# @return [Array<Object>]
|
|
211
422
|
def values_at (*names)
|
|
212
423
|
return names.map{|name| self[name] }
|
|
213
424
|
end
|
|
214
425
|
|
|
426
|
+
# @return [String] the class name followed by every member and its value.
|
|
215
427
|
def inspect
|
|
216
428
|
table = {}
|
|
217
429
|
members.each do |key|
|
|
@@ -220,6 +432,9 @@ class CAStruct
|
|
|
220
432
|
return ["<", self.class.inspect, " ", table.inspect[1..-2], ">"].join
|
|
221
433
|
end
|
|
222
434
|
|
|
435
|
+
# Value comparison against another record of the same class. Unlike
|
|
436
|
+
# `eql?`, which compares the raw bytes, this compares the decoded members.
|
|
437
|
+
# @return [Boolean]
|
|
223
438
|
def == (other)
|
|
224
439
|
case other
|
|
225
440
|
when self.class
|
|
@@ -229,333 +444,115 @@ class CAStruct
|
|
|
229
444
|
end
|
|
230
445
|
end
|
|
231
446
|
|
|
447
|
+
# Byte-level identity. Two CAStruct instances are eql? iff they are
|
|
448
|
+
# of exactly the same class and their binary representations match.
|
|
449
|
+
# Lets struct records work as Hash keys / Set members.
|
|
450
|
+
def eql? (other)
|
|
451
|
+
other.is_a?(self.class) && encode == other.encode
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# @return [Integer] a hash consistent with #eql? (byte-level identity), so
|
|
455
|
+
# records work as Hash keys and Set members.
|
|
456
|
+
def hash
|
|
457
|
+
encode.hash
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
# @overload decode(data)
|
|
461
|
+
# Loads `data`'s binary representation into `self`. A String
|
|
462
|
+
# is loaded directly; a CArray is copied through
|
|
463
|
+
# `dump_binary` (no aliasing).
|
|
464
|
+
# @param data [String, CArray]
|
|
465
|
+
# @return [self]
|
|
466
|
+
# @raise [CAStruct::DecodeError] on an unsupported input.
|
|
232
467
|
def decode (data)
|
|
233
468
|
case data
|
|
234
469
|
when String
|
|
235
470
|
@data.load_binary(data)
|
|
236
471
|
when CArray
|
|
237
|
-
@data
|
|
472
|
+
@data.load_binary(data.dump_binary)
|
|
238
473
|
else
|
|
239
|
-
raise
|
|
474
|
+
raise CAStruct::DecodeError,
|
|
475
|
+
format("unknown data to decode: %s", data.class)
|
|
240
476
|
end
|
|
241
477
|
return self
|
|
242
478
|
end
|
|
243
479
|
|
|
480
|
+
# @overload encode
|
|
481
|
+
# Returns the binary representation of `self` matching this
|
|
482
|
+
# struct's layout.
|
|
483
|
+
# @return [String]
|
|
244
484
|
def encode ### required element as data class
|
|
245
485
|
return @data.dump_binary
|
|
246
486
|
end
|
|
247
487
|
|
|
248
|
-
|
|
249
|
-
|
|
488
|
+
# @overload swap_bytes!
|
|
489
|
+
# Byte-swaps `self` in place (endian flip).
|
|
490
|
+
# @return [self]
|
|
250
491
|
def swap_bytes!
|
|
251
|
-
@data.swap_bytes
|
|
492
|
+
@data[] = @data.swap_bytes
|
|
252
493
|
return self
|
|
253
494
|
end
|
|
254
495
|
|
|
496
|
+
# @overload swap_bytes
|
|
497
|
+
# Returns a fresh byte-swapped copy of `self`.
|
|
498
|
+
# @return [CAStruct]
|
|
255
499
|
def swap_bytes
|
|
256
500
|
return self.class.decode(@data.swap_bytes.dump_binary)
|
|
257
501
|
end
|
|
258
502
|
|
|
503
|
+
# @overload to_ptr
|
|
504
|
+
# Returns a `Fiddle::Pointer` to the backing storage.
|
|
505
|
+
# @return [Fiddle::Pointer]
|
|
259
506
|
def to_ptr
|
|
260
507
|
return @data.to_ptr
|
|
261
508
|
end
|
|
262
509
|
|
|
263
510
|
end
|
|
264
511
|
|
|
512
|
+
# Struct whose members all start at offset 0, so they are alternative
|
|
513
|
+
# readings of the same bytes. Otherwise behaves as a {CAStruct}.
|
|
265
514
|
class CAUnion < CAStruct
|
|
266
515
|
class << self
|
|
516
|
+
# @return [String] the union's name, or `"AnonUnion"` when it was
|
|
517
|
+
# defined anonymously.
|
|
267
518
|
def inspect
|
|
268
519
|
return name.nil? ? "AnonUnion" : name
|
|
269
520
|
end
|
|
270
521
|
end
|
|
271
522
|
end
|
|
272
523
|
|
|
273
|
-
|
|
524
|
+
class CArray
|
|
274
525
|
|
|
526
|
+
# @overload struct(opt = {}, &block)
|
|
527
|
+
# Returns a new {CAStruct} subclass defined by the block via
|
|
528
|
+
# {CAStruct::Builder}. Options control alignment, packing, and
|
|
529
|
+
# endianness.
|
|
530
|
+
# @param opt [Hash]
|
|
531
|
+
# @yield DSL calls in {CAStruct::Builder}.
|
|
532
|
+
# @return [Class] anonymous {CAStruct} subclass.
|
|
275
533
|
def self.struct (opt={}, &block)
|
|
276
534
|
return CAStruct::Builder.new(:struct, opt).define(&block)
|
|
277
535
|
end
|
|
278
536
|
|
|
537
|
+
# @overload union(opt = {}, &block)
|
|
538
|
+
# Returns a new {CAUnion} subclass defined by the block. Same
|
|
539
|
+
# options and DSL as {.struct} but every member occupies the
|
|
540
|
+
# same offset.
|
|
541
|
+
# @param opt [Hash]
|
|
542
|
+
# @yield DSL calls in {CAStruct::Builder}.
|
|
543
|
+
# @return [Class] anonymous {CAUnion} subclass.
|
|
279
544
|
def self.union (opt={}, &block)
|
|
280
545
|
return CAStruct::Builder.new(:union, opt).define(&block)
|
|
281
546
|
end
|
|
282
547
|
|
|
283
548
|
end
|
|
284
549
|
|
|
285
|
-
# ---------------------------------------------------------------------
|
|
286
|
-
#
|
|
287
|
-
# Struct Builder Class
|
|
288
|
-
#
|
|
289
|
-
# ---------------------------------------------------------------------
|
|
290
|
-
|
|
291
|
-
class CAStruct::Builder # :nodoc:
|
|
292
|
-
|
|
293
|
-
class Member # :nodoc:
|
|
294
|
-
|
|
295
|
-
def initialize (name, type, opt={})
|
|
296
|
-
@name, @type, @opt = name, type, opt
|
|
297
|
-
if @type.kind_of?(CArray)
|
|
298
|
-
@type = @type.to_ca
|
|
299
|
-
@byte_length = @type.bytes * @type.elements
|
|
300
|
-
@bytes = 0
|
|
301
|
-
else
|
|
302
|
-
data_type, @bytes = CArray.guess_type_and_bytes(@type, @opt[:bytes])
|
|
303
|
-
if data_type == CA_OBJECT
|
|
304
|
-
raise RuntimeError, "CA_OBJECT type can't be a member of struct or union"
|
|
305
|
-
end
|
|
306
|
-
@byte_length = @bytes
|
|
307
|
-
end
|
|
308
|
-
@offset = @opt[:offset]
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
attr_reader :name, :type, :offset, :bytes, :byte_length
|
|
312
|
-
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
ALIGN_TABLE = {
|
|
316
|
-
CA_FIXLEN => CA_ALIGN_FIXLEN,
|
|
317
|
-
CA_BOOLEAN => CA_ALIGN_INT8,
|
|
318
|
-
CA_INT8 => CA_ALIGN_INT8,
|
|
319
|
-
CA_UINT8 => CA_ALIGN_INT8,
|
|
320
|
-
CA_INT16 => CA_ALIGN_INT16,
|
|
321
|
-
CA_UINT16 => CA_ALIGN_INT16,
|
|
322
|
-
CA_INT32 => CA_ALIGN_INT32,
|
|
323
|
-
CA_UINT32 => CA_ALIGN_INT32,
|
|
324
|
-
CA_INT64 => CA_ALIGN_INT64,
|
|
325
|
-
CA_UINT64 => CA_ALIGN_INT64,
|
|
326
|
-
CA_FLOAT32 => CA_ALIGN_FLOAT32,
|
|
327
|
-
CA_FLOAT64 => CA_ALIGN_FLOAT64,
|
|
328
|
-
CA_CMPLX64 => CA_ALIGN_FLOAT32,
|
|
329
|
-
CA_CMPLX128 => CA_ALIGN_FLOAT64,
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
CARRAY_METHODS = CArray.instance_methods.map{|m| m.intern }
|
|
333
|
-
|
|
334
|
-
def initialize (type, opt = {})
|
|
335
|
-
if not opt[:pack].nil? and not opt[:pack].is_a?(Integer)
|
|
336
|
-
raise "invalid alignment given '#{align}'"
|
|
337
|
-
end
|
|
338
|
-
@type = type ### :struct or :union
|
|
339
|
-
@align = opt[:pack] ### nil for alignment, int for pack(n)
|
|
340
|
-
@members = [] ### array of CArray::Struct::Builder::Member
|
|
341
|
-
@offset = 0 ### offset of each member and size of struct
|
|
342
|
-
@align_max = 1 ### maximum of alignment among members
|
|
343
|
-
@size = opt[:size] ### user defined struct size
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
def define (&block)
|
|
347
|
-
# ---
|
|
348
|
-
case block.arity
|
|
349
|
-
when 1
|
|
350
|
-
block.call(self) ### struct/union definition block
|
|
351
|
-
when -1, 0
|
|
352
|
-
instance_exec(&block) ### struct/union definition block
|
|
353
|
-
else
|
|
354
|
-
raise "invalid # of block parameters"
|
|
355
|
-
end
|
|
356
|
-
# ---
|
|
357
|
-
case @type
|
|
358
|
-
when :struct
|
|
359
|
-
klass = Class.new(CAStruct)
|
|
360
|
-
when :union
|
|
361
|
-
klass = Class.new(CAUnion)
|
|
362
|
-
end
|
|
363
|
-
# ---
|
|
364
|
-
if @align.nil?
|
|
365
|
-
@offset = alignment(@offset, :align_max)
|
|
366
|
-
end
|
|
367
|
-
if @size
|
|
368
|
-
if @size < @offset
|
|
369
|
-
raise RuntimeError, "struct size exceeds the fixlen size"
|
|
370
|
-
end
|
|
371
|
-
@offset = @size
|
|
372
|
-
end
|
|
373
|
-
klass.const_set(:DATA_SIZE, @offset) ### required element as data class
|
|
374
|
-
# ---
|
|
375
|
-
table = Hash.new
|
|
376
|
-
names = []
|
|
377
|
-
@members.each do |mem|
|
|
378
|
-
name = mem.name
|
|
379
|
-
type = mem.type
|
|
380
|
-
offset = mem.offset
|
|
381
|
-
bytes = mem.bytes
|
|
382
|
-
if bytes
|
|
383
|
-
table[name] = [offset, type, {:bytes=>bytes}]
|
|
384
|
-
else
|
|
385
|
-
table[name] = [offset, type]
|
|
386
|
-
end
|
|
387
|
-
names.push(name)
|
|
388
|
-
end
|
|
389
|
-
klass.const_set(:MEMBER_TABLE, table) ### required element as data class
|
|
390
|
-
klass.const_set(:MEMBERS, names)
|
|
391
|
-
# ---
|
|
392
|
-
klass.module_eval {
|
|
393
|
-
names.each do |name|
|
|
394
|
-
define_method(name) {
|
|
395
|
-
return self[name]
|
|
396
|
-
}
|
|
397
|
-
define_method("#{name}=") { |val|
|
|
398
|
-
return self[name] = val
|
|
399
|
-
}
|
|
400
|
-
end
|
|
401
|
-
}
|
|
402
|
-
# ---
|
|
403
|
-
return klass
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
private
|
|
407
|
-
|
|
408
|
-
def alignment (addr, data_type, opt={})
|
|
409
|
-
case data_type
|
|
410
|
-
when Integer
|
|
411
|
-
align = ALIGN_TABLE[data_type]
|
|
412
|
-
when CArray, Class
|
|
413
|
-
align = CA_ALIGN_VOIDP
|
|
414
|
-
when :align_max
|
|
415
|
-
align = @align_max
|
|
416
|
-
else
|
|
417
|
-
data_type, bytes = CArray.guess_type_and_bytes(data_type, opt[:bytes])
|
|
418
|
-
return alignment(addr, data_type, :bytes=>bytes)
|
|
419
|
-
end
|
|
420
|
-
if align > @align_max
|
|
421
|
-
@align_max = align
|
|
422
|
-
end
|
|
423
|
-
if ( d = addr % align ) != 0
|
|
424
|
-
addr += (align - d)
|
|
425
|
-
end
|
|
426
|
-
return addr
|
|
427
|
-
end
|
|
428
|
-
|
|
429
|
-
def pack (addr, align, opt={})
|
|
430
|
-
if ( addr % align ) != 0
|
|
431
|
-
raise "invalid offset for packing"
|
|
432
|
-
end
|
|
433
|
-
return addr
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
public
|
|
437
|
-
|
|
438
|
-
def member (data_type, id = nil, opt = {})
|
|
439
|
-
opt = opt.clone
|
|
440
|
-
if id
|
|
441
|
-
id = id.to_s
|
|
442
|
-
else
|
|
443
|
-
id = "#{@members.size}"
|
|
444
|
-
end
|
|
445
|
-
case @type
|
|
446
|
-
when :struct ### struct
|
|
447
|
-
case @align
|
|
448
|
-
when nil ### -- aligned
|
|
449
|
-
@offset = alignment(@offset, data_type, opt)
|
|
450
|
-
opt[:offset] = @offset
|
|
451
|
-
else ### -- packed
|
|
452
|
-
if opt[:offset] ### ---- explicit offset
|
|
453
|
-
@offset = pack(opt[:offset], @align, opt)
|
|
454
|
-
else
|
|
455
|
-
opt[:offset] = @offset ### ---- auto offset
|
|
456
|
-
end
|
|
457
|
-
end
|
|
458
|
-
mem = Member.new(id, data_type, opt)
|
|
459
|
-
@members.push(mem)
|
|
460
|
-
@offset += mem.byte_length
|
|
461
|
-
when :union ### union
|
|
462
|
-
alignment(0, data_type, opt)
|
|
463
|
-
opt[:offset] = 0
|
|
464
|
-
mem = Member.new(id, data_type, opt)
|
|
465
|
-
@members.push(mem)
|
|
466
|
-
if mem.byte_length > @offset
|
|
467
|
-
@offset = mem.byte_length
|
|
468
|
-
end
|
|
469
|
-
end
|
|
470
|
-
end
|
|
471
|
-
|
|
472
|
-
# method for nested struct
|
|
473
|
-
def struct (*args, &block)
|
|
474
|
-
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
475
|
-
if block
|
|
476
|
-
opt = {:pack => @align}.update(opt)
|
|
477
|
-
st = self.class.new(:struct, opt).define(&block)
|
|
478
|
-
elsif opt[:type] and opt[:type] <= CAStruct
|
|
479
|
-
st = opt[:type]
|
|
480
|
-
else
|
|
481
|
-
raise "type is not given for struct member of struct"
|
|
482
|
-
end
|
|
483
|
-
args.each do |arg|
|
|
484
|
-
member(st, arg)
|
|
485
|
-
end
|
|
486
|
-
return st
|
|
487
|
-
end
|
|
488
|
-
|
|
489
|
-
# method for nested union
|
|
490
|
-
def union (*args, &block)
|
|
491
|
-
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
492
|
-
if block
|
|
493
|
-
opt = {:pack => @align}.update(opt)
|
|
494
|
-
st = self.class.new(:union, opt).define(&block)
|
|
495
|
-
elsif opt[:type] and opt[:type] <= CAStruct
|
|
496
|
-
st = opt[:type]
|
|
497
|
-
else
|
|
498
|
-
raise "type is not given for union member of struct"
|
|
499
|
-
end
|
|
500
|
-
args.each do |arg|
|
|
501
|
-
member(st, arg)
|
|
502
|
-
end
|
|
503
|
-
return st
|
|
504
|
-
end
|
|
505
|
-
|
|
506
|
-
def array (*args)
|
|
507
|
-
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
508
|
-
if not opt[:type] or not opt[:type].kind_of?(CArray)
|
|
509
|
-
raise "type is not given for array member of struct"
|
|
510
|
-
end
|
|
511
|
-
args.each do |arg|
|
|
512
|
-
member(opt[:type], arg)
|
|
513
|
-
end
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
[
|
|
517
|
-
"fixlen",
|
|
518
|
-
"boolean",
|
|
519
|
-
"int8",
|
|
520
|
-
"uint8",
|
|
521
|
-
"int16",
|
|
522
|
-
"uint16",
|
|
523
|
-
"int32",
|
|
524
|
-
"uint32",
|
|
525
|
-
"int64",
|
|
526
|
-
"uint64",
|
|
527
|
-
"float32",
|
|
528
|
-
"float64",
|
|
529
|
-
"float128",
|
|
530
|
-
"cmplx64",
|
|
531
|
-
"cmplx128",
|
|
532
|
-
"cmplx256",
|
|
533
|
-
].each do |typename|
|
|
534
|
-
class_eval %{
|
|
535
|
-
def #{typename} (*args)
|
|
536
|
-
opt = args.last.is_a?(Hash) ? args.pop : {}
|
|
537
|
-
args.each do |arg|
|
|
538
|
-
member(:#{typename}, arg, opt)
|
|
539
|
-
end
|
|
540
|
-
end
|
|
541
|
-
}
|
|
542
|
-
end
|
|
543
|
-
|
|
544
|
-
alias char_p fixlen
|
|
545
|
-
alias byte uint8
|
|
546
|
-
alias short int16
|
|
547
|
-
alias int int32
|
|
548
|
-
alias float float32
|
|
549
|
-
alias double float64
|
|
550
|
-
alias complex cmplx64
|
|
551
|
-
alias dcomplex cmplx128
|
|
552
|
-
|
|
553
|
-
end
|
|
554
|
-
|
|
555
|
-
|
|
556
550
|
|
|
557
551
|
class CAStruct
|
|
558
552
|
|
|
553
|
+
# Renders the struct's layout as the definition source that would rebuild
|
|
554
|
+
# it — member names, types, offsets and any nested struct.
|
|
555
|
+
# @return [String]
|
|
559
556
|
def self.spec
|
|
560
557
|
output = ""
|
|
561
558
|
table = self::MEMBER_TABLE
|
|
@@ -572,9 +569,9 @@ class CAStruct
|
|
|
572
569
|
output << sprintf("%s = ", self.name)
|
|
573
570
|
end
|
|
574
571
|
if self < CAUnion
|
|
575
|
-
output << sprintf("
|
|
572
|
+
output << sprintf("CArray.union(:size=>%i) {\n", self::DATA_SIZE)
|
|
576
573
|
else
|
|
577
|
-
output << sprintf("
|
|
574
|
+
output << sprintf("CArray.struct(:size=>%i) {\n", self::DATA_SIZE)
|
|
578
575
|
end
|
|
579
576
|
members.each do |member|
|
|
580
577
|
offset, type, option = *table[member]
|
|
@@ -623,10 +620,25 @@ class CAStruct
|
|
|
623
620
|
end
|
|
624
621
|
end
|
|
625
622
|
|
|
623
|
+
# @return [String] the layout of this record's class; see {CAStruct.spec}.
|
|
626
624
|
def spec
|
|
627
625
|
return self.class.spec
|
|
628
626
|
end
|
|
629
|
-
|
|
627
|
+
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
# Hand the C extension a reference to CAStruct so it can override
|
|
631
|
+
# `[]` / `[]=` with the fast primitive-aware C path defined in
|
|
632
|
+
# ext/carray_struct.c.
|
|
633
|
+
#
|
|
634
|
+
# CAREFUL: this must run AFTER CAStruct's Ruby [] / []= are defined
|
|
635
|
+
# above (so the C versions can replace them) and AFTER FAST_PRIMITIVES /
|
|
636
|
+
# DISPATCH_TABLE keys are agreed upon (so the per-class struct definition
|
|
637
|
+
# in Builder sets them up before any instance is created). Subclasses
|
|
638
|
+
# created by Builder later don't need re-installation -- they inherit the
|
|
639
|
+
# C methods from CAStruct.
|
|
640
|
+
if CArray.respond_to?(:__install_castruct_methods__)
|
|
641
|
+
CArray.__install_castruct_methods__(CAStruct)
|
|
630
642
|
end
|
|
631
643
|
|
|
632
644
|
|