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/serialize.rb
CHANGED
|
@@ -1,80 +1,103 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Portable binary serialization ("_CARRAY3" format).
|
|
2
2
|
#
|
|
3
|
-
#
|
|
3
|
+
# User-facing spec (C struct / FORTRAN / NumPy readers):
|
|
4
|
+
# docs/topics/Serialization.md.
|
|
4
5
|
#
|
|
5
|
-
#
|
|
6
|
+
# Public surface:
|
|
7
|
+
# CArray.save(ca, path_or_io, endian: CArray.endian)
|
|
8
|
+
# CArray.load(path_or_io_or_string)
|
|
9
|
+
# CArray.dump(ca, endian: CArray.endian) # -> String
|
|
10
|
+
# CArray#marshal_dump / CArray#marshal_load
|
|
6
11
|
#
|
|
7
|
-
#
|
|
12
|
+
# Core promise: numeric payload lives at a fixed offset (data_offset,
|
|
13
|
+
# 256 in v1.x) as raw contiguous bytes in a single file-wide endian.
|
|
14
|
+
# A C / FORTRAN / Python reader reaches the data with one seek and
|
|
15
|
+
# never parses any Ruby-specific metadata. CArray-native concepts
|
|
16
|
+
# (attribute Hash, data_class Face schema, mask) ride in a YAML
|
|
17
|
+
# flow-style trailer at the file tail that other languages skip.
|
|
8
18
|
#
|
|
9
|
-
#
|
|
19
|
+
# CA_OBJECT (arbitrary Ruby objects) is *not* serialisable by this
|
|
20
|
+
# portable format: CArray.save raises on it. Object arrays persist
|
|
21
|
+
# through the separate Ruby-only Marshal path (CArray#marshal_dump).
|
|
10
22
|
#
|
|
23
|
+
# On-disk layout (all header integers in file endian; the byte order is
|
|
24
|
+
# self-describing via endian_marker at offset 8):
|
|
11
25
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
26
|
+
# 0-7 magic char*8 "_CARRAY3"
|
|
27
|
+
# 8-11 endian_marker uint32 0x01020304 in file endian
|
|
28
|
+
# 12 version_major uint8 1
|
|
29
|
+
# 13 version_minor uint8 0
|
|
30
|
+
# 14-15 header_bytes uint16 256
|
|
31
|
+
# 16 has_mask uint8
|
|
32
|
+
# 17 has_trailer uint8
|
|
33
|
+
# 18 data_type_code uint8 CArray CA_* code
|
|
34
|
+
# 19 ndim uint8
|
|
35
|
+
# 20-23 reserved0 uint32 zero (align shape to 8 bytes)
|
|
36
|
+
# 24-151 shape int64*16 front ndim slots valid
|
|
37
|
+
# 152-155 element_bytes uint32
|
|
38
|
+
# 156-159 flags int32 raw ca->flags snapshot (provenance)
|
|
39
|
+
# 160-167 elements uint64
|
|
40
|
+
# 168-175 data_offset uint64 = 256
|
|
41
|
+
# 176-183 data_bytes uint64 = elements*element_bytes
|
|
42
|
+
# 184-191 mask_offset uint64 0 if !has_mask
|
|
43
|
+
# 192-199 mask_bytes uint64 0 if !has_mask
|
|
44
|
+
# 200-207 trailer_offset uint64 0 if !has_trailer
|
|
45
|
+
# 208-215 trailer_bytes uint64 0 if !has_trailer
|
|
46
|
+
# 216-223 data_checksum uint64 reserved (0)
|
|
47
|
+
# 224 checksum_algo uint8 reserved (0=none)
|
|
48
|
+
# 225-255 reserved zero
|
|
14
49
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
50
|
+
# 256-... Data (raw contiguous, file endian ordering)
|
|
51
|
+
# ... Mask (raw int8*elements, if has_mask)
|
|
52
|
+
# ... Trailer (UTF-8 YAML flow, trailer_bytes long, if present)
|
|
17
53
|
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
|
|
24
|
-
#
|
|
25
|
-
# CArray's Binary Format
|
|
26
|
-
#
|
|
27
|
-
# offset 0 bytes
|
|
28
|
-
# magic_string : char*8 : "_CARRAY_"
|
|
29
|
-
# data_type_name : char*8 : e.g. "int8", "cmplx256" ...
|
|
30
|
-
# endian : char*4 : "_LE_":LITTLE_ENIDAN or "_BE_":BIG_ENDIAN
|
|
31
|
-
# offset 20 bytes
|
|
32
|
-
# data_type : int32 : data type specifier
|
|
33
|
-
# bytes : int32 : byte size of each data
|
|
34
|
-
# ndim : int32 : ndim of array
|
|
35
|
-
# elements : int32 : number of all elements
|
|
36
|
-
# has_mask : int32 : 0 (not masked) or 1 (masked)
|
|
37
|
-
# offset 40 bytes
|
|
38
|
-
# dim[CA_RANK_MAX] : int32 : size for 0-th dimension
|
|
39
|
-
# has_attribute : int32
|
|
40
|
-
#
|
|
41
|
-
# offset 256 bytes
|
|
42
|
-
# data : bytes*elements : value data
|
|
43
|
-
# mask : int8*elements : mask data if has_mask == 1
|
|
44
|
-
# attribute : object marshal : attribute
|
|
45
|
-
# data_class_name : string marshal : data_class_name
|
|
54
|
+
# flags is a verbatim snapshot of the source array's internal
|
|
55
|
+
# ca->flags at write time, kept purely as a provenance record of what
|
|
56
|
+
# the array was (scalar / Face / view / read-only / ...). It is NOT
|
|
57
|
+
# read back on load and carries no reconstruction meaning.
|
|
46
58
|
|
|
47
59
|
require "stringio"
|
|
60
|
+
require "psych"
|
|
48
61
|
|
|
49
|
-
class CArray::Serializer # :nodoc:
|
|
50
|
-
|
|
51
|
-
Header = CA.struct(:pack=>1, :size=>256) {
|
|
52
|
-
char_p :magic_string, :bytes=>8
|
|
53
|
-
char_p :data_type_name, :bytes=>8
|
|
54
|
-
char_p :endian, :bytes=>4
|
|
55
|
-
int32 :data_type
|
|
56
|
-
int64 :bytes
|
|
57
|
-
int32 :ndim
|
|
58
|
-
int64 :elements
|
|
59
|
-
int32 :has_mask
|
|
60
|
-
array :dim, :type => CArray.int64(CA_RANK_MAX)
|
|
61
|
-
int32 :has_attr
|
|
62
|
-
int32 :has_data_class
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
Header_Legacy = CA.struct(:pack=>1, :size=>256) {
|
|
66
|
-
char_p :magic_string, :bytes=>8
|
|
67
|
-
char_p :data_type_name, :bytes=>8
|
|
68
|
-
char_p :endian, :bytes=>4
|
|
69
|
-
int32 :data_type
|
|
70
|
-
int32 :bytes
|
|
71
|
-
int32 :ndim
|
|
72
|
-
int32 :elements
|
|
73
|
-
int32 :has_mask
|
|
74
|
-
array :dim, :type => CArray.int32(CA_RANK_MAX)
|
|
75
|
-
int32 :has_attr
|
|
76
|
-
}
|
|
62
|
+
class CArray::Serializer # :nodoc:
|
|
77
63
|
|
|
64
|
+
# @!visibility private
|
|
65
|
+
MAGIC = "_CARRAY3"
|
|
66
|
+
# @!visibility private
|
|
67
|
+
HEADER_BYTES = 256
|
|
68
|
+
# @!visibility private
|
|
69
|
+
VERSION_MAJOR = 1
|
|
70
|
+
# @!visibility private
|
|
71
|
+
VERSION_MINOR = 0
|
|
72
|
+
# @!visibility private
|
|
73
|
+
ENDIAN_MARKER = 0x01020304
|
|
74
|
+
|
|
75
|
+
# Canonical primitive-type notation for the trailer data_class
|
|
76
|
+
# schema. The .ca format *owns* this mapping: it is the same bare
|
|
77
|
+
# PEP 3118 notation CArray's MemoryView layer emits via
|
|
78
|
+
# ca_mv_format_for, frozen here as the format's own copy so a future
|
|
79
|
+
# MemoryView producer flip does not silently change the on-disk spec.
|
|
80
|
+
# Non-primitive member types (:fixlen / :object / nested class /
|
|
81
|
+
# CArray template / :bitfield) are absent, so SYMBOL_TO_PEP[type] is
|
|
82
|
+
# nil for them, which enforces the flat-primitive-only rule for free
|
|
83
|
+
# (a non-expressible member raises on save).
|
|
84
|
+
PEP_TO_SYMBOL = {
|
|
85
|
+
"?" => :boolean,
|
|
86
|
+
"b" => :int8, "B" => :uint8,
|
|
87
|
+
"h" => :int16, "H" => :uint16,
|
|
88
|
+
"i" => :int32, "I" => :uint32,
|
|
89
|
+
"q" => :int64, "Q" => :uint64,
|
|
90
|
+
"f" => :float32, "d" => :float64,
|
|
91
|
+
"Zf" => :cmplx64, "Zd" => :cmplx128,
|
|
92
|
+
}.freeze
|
|
93
|
+
|
|
94
|
+
# @!visibility private
|
|
95
|
+
SYMBOL_TO_PEP = PEP_TO_SYMBOL.invert.freeze
|
|
96
|
+
|
|
97
|
+
# @overload initialize(io)
|
|
98
|
+
# Allocates a Serializer around `io`. A String is wrapped in a
|
|
99
|
+
# `StringIO`; anything else is used directly.
|
|
100
|
+
# @param io [IO, String] destination or source.
|
|
78
101
|
def initialize (io)
|
|
79
102
|
case io
|
|
80
103
|
when String
|
|
@@ -84,128 +107,377 @@ class CArray::Serializer # :nodoc:
|
|
|
84
107
|
end
|
|
85
108
|
end
|
|
86
109
|
|
|
110
|
+
# @overload save(ca, endian: CArray.endian)
|
|
111
|
+
# Writes `ca` to the wrapped IO in the _CARRAY3 portable format.
|
|
112
|
+
# The data region is raw contiguous bytes; attribute Hash and
|
|
113
|
+
# data_class schema (if any) ride in a YAML trailer at the tail.
|
|
114
|
+
# @param ca [CArray] array to write.
|
|
115
|
+
# @param opt [Hash] `:endian` forces the output byte order
|
|
116
|
+
# (defaults to the host's; a differing value swaps data + header).
|
|
117
|
+
# @return [CArray] `ca`.
|
|
118
|
+
# @raise [ArgumentError] when `ca` is a CA_OBJECT array (use
|
|
119
|
+
# `Marshal.dump(ca)` instead), or carries a data_class the
|
|
120
|
+
# v1.0 flat-primitive schema cannot express.
|
|
87
121
|
def save (ca, **opt)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
header[:endian] = ( endian == CA_LITTLE_ENDIAN ) ? "_LE_" : "_BE_"
|
|
94
|
-
header[:data_type] = ca.data_type
|
|
95
|
-
header[:bytes] = ca.bytes
|
|
96
|
-
header[:ndim] = ca.ndim
|
|
97
|
-
header[:elements] = ca.elements
|
|
98
|
-
header[:has_mask] = ca.has_mask? ? 1 : 0
|
|
99
|
-
header[:has_data_class] = ca.has_data_class? ? 1 : 0
|
|
100
|
-
header[:dim][[0,ca.ndim]] = ca.dim
|
|
101
|
-
attributes = nil
|
|
102
|
-
if ca.attribute
|
|
103
|
-
attributes = ca.attribute.clone
|
|
104
|
-
end
|
|
105
|
-
if opt[:attribute]
|
|
106
|
-
(attributes ||= {}).update(opt[:attribute])
|
|
107
|
-
end
|
|
108
|
-
header[:has_attr] = attributes.empty? ? 0 : 1
|
|
109
|
-
unless CArray.endian == endian
|
|
110
|
-
header.swap_bytes!
|
|
111
|
-
end
|
|
112
|
-
@io.write(header.encode)
|
|
113
|
-
# ---
|
|
114
|
-
if ca.data_type == CA_OBJECT
|
|
115
|
-
Marshal.dump(ca.value.to_a, @io)
|
|
116
|
-
else
|
|
117
|
-
unless CArray.endian == endian
|
|
118
|
-
ca = ca.swap_bytes
|
|
119
|
-
end
|
|
120
|
-
ca.dump_binary(@io)
|
|
121
|
-
end
|
|
122
|
-
# ---
|
|
123
|
-
if ca.has_mask?
|
|
124
|
-
ca.mask.dump_binary(@io)
|
|
122
|
+
if ca.data_type == :object
|
|
123
|
+
raise ArgumentError,
|
|
124
|
+
"CArray.save cannot serialise a CA_OBJECT array " \
|
|
125
|
+
"(arbitrary Ruby objects have no portable representation); " \
|
|
126
|
+
"use Marshal.dump(ca) for a Ruby-only round-trip"
|
|
125
127
|
end
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
|
|
129
|
+
file_endian = opt[:endian] || CArray.endian
|
|
130
|
+
swap = (file_endian != CArray.endian)
|
|
131
|
+
|
|
132
|
+
elements = ca.elements
|
|
133
|
+
element_bytes = ca.bytes
|
|
134
|
+
data_bytes = elements * element_bytes
|
|
135
|
+
has_mask = ca.has_mask?
|
|
136
|
+
|
|
137
|
+
# Trailer is present only when there is semantic content: an
|
|
138
|
+
# attribute Hash or a data_class schema. A plain numeric array
|
|
139
|
+
# writes no trailer (both trailer fields zero), keeping the
|
|
140
|
+
# C / FORTRAN reader path a header + raw data.
|
|
141
|
+
trailer = build_trailer(ca)
|
|
142
|
+
trailer_str = trailer.empty? ? nil : encode_trailer(trailer)
|
|
143
|
+
|
|
144
|
+
data_offset = HEADER_BYTES
|
|
145
|
+
mask_offset = has_mask ? data_offset + data_bytes : 0
|
|
146
|
+
mask_bytes = has_mask ? elements : 0
|
|
147
|
+
tail_offset = has_mask ? mask_offset + mask_bytes : data_offset + data_bytes
|
|
148
|
+
trailer_offset = trailer_str ? tail_offset : 0
|
|
149
|
+
trailer_bytes = trailer_str ? trailer_str.bytesize : 0
|
|
150
|
+
|
|
151
|
+
dim = ca.shape
|
|
152
|
+
dim = dim + Array.new(CA_RANK_MAX - dim.size, 0)
|
|
153
|
+
|
|
154
|
+
header = {
|
|
155
|
+
has_mask: has_mask ? 1 : 0,
|
|
156
|
+
has_trailer: trailer_str ? 1 : 0,
|
|
157
|
+
data_type_code: CArray.data_type_code(ca.data_type),
|
|
158
|
+
ndim: ca.ndim,
|
|
159
|
+
shape: dim,
|
|
160
|
+
element_bytes: element_bytes,
|
|
161
|
+
flags: ca.flags,
|
|
162
|
+
elements: elements,
|
|
163
|
+
data_offset: data_offset,
|
|
164
|
+
data_bytes: data_bytes,
|
|
165
|
+
mask_offset: mask_offset,
|
|
166
|
+
mask_bytes: mask_bytes,
|
|
167
|
+
trailer_offset: trailer_offset,
|
|
168
|
+
trailer_bytes: trailer_bytes,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
@io.write(self.class.pack_header(header, file_endian))
|
|
172
|
+
|
|
173
|
+
data_ca = swap ? ca.swap_bytes : ca
|
|
174
|
+
data_ca.dump_binary(@io)
|
|
175
|
+
|
|
176
|
+
if has_mask
|
|
177
|
+
ca.mask.dump_binary(@io) # int8, endian-neutral
|
|
131
178
|
end
|
|
179
|
+
|
|
180
|
+
@io.write(trailer_str) if trailer_str
|
|
181
|
+
|
|
132
182
|
return ca
|
|
133
183
|
end
|
|
134
|
-
|
|
184
|
+
|
|
185
|
+
# @overload load(data_type: nil)
|
|
186
|
+
# Reads a _CARRAY3 payload from the wrapped IO and returns the
|
|
187
|
+
# reconstructed array, re-wrapping it through its recorded
|
|
188
|
+
# data_class when the trailer carries one.
|
|
189
|
+
# @param opt [Hash] `:data_type` overrides the element type for a
|
|
190
|
+
# bare `CA_FIXLEN` payload.
|
|
191
|
+
# @return [CArray]
|
|
192
|
+
# @raise [RuntimeError] on a bad magic string, corrupt header, or
|
|
193
|
+
# unsupported version.
|
|
135
194
|
def load (**opt)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
raise "not a CArray binary data"
|
|
143
|
-
end
|
|
144
|
-
case header[:endian]
|
|
145
|
-
when "_LE_"
|
|
146
|
-
endian = CA_LITTLE_ENDIAN
|
|
147
|
-
when "_BE_"
|
|
148
|
-
endian = CA_BIG_ENDIAN
|
|
149
|
-
end
|
|
150
|
-
unless CArray.endian == endian
|
|
151
|
-
header.swap_bytes!
|
|
152
|
-
end
|
|
153
|
-
data_type = header[:data_type]
|
|
154
|
-
bytes = header[:bytes]
|
|
155
|
-
ndim = header[:ndim]
|
|
156
|
-
elements = header[:elements]
|
|
157
|
-
has_mask = header[:has_mask] != 0 ? true : false
|
|
158
|
-
dim = header[:dim][[0, ndim]].to_a
|
|
159
|
-
has_attr = header[:has_attr] != 0 ? true : false
|
|
160
|
-
has_data_class = header[:has_data_class] != 0 ? true : false
|
|
161
|
-
if data_type == 255
|
|
162
|
-
data_type = header[:data_type_name].strip.to_sym
|
|
163
|
-
end
|
|
164
|
-
data_type, bytes = CArray.guess_type_and_bytes(data_type, bytes)
|
|
165
|
-
if opt[:data_type] and data_type == CA_FIXLEN
|
|
166
|
-
data_type = opt[:data_type]
|
|
195
|
+
buf = @io.read(HEADER_BYTES)
|
|
196
|
+
unless buf && buf.bytesize == HEADER_BYTES
|
|
197
|
+
raise "not a CArray binary data (truncated header)"
|
|
198
|
+
end
|
|
199
|
+
if buf[0, 8] != MAGIC
|
|
200
|
+
raise "not a CArray binary data (bad magic)"
|
|
167
201
|
end
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
202
|
+
|
|
203
|
+
# The byte order is self-describing via endian_marker at offset 8:
|
|
204
|
+
# the writer stored 0x01020304 in file order, so the raw bytes are
|
|
205
|
+
# 01 02 03 04 on a big-endian file and 04 03 02 01 on a little-endian
|
|
206
|
+
# one. The leading byte alone decides; anything else is corrupt.
|
|
207
|
+
case buf[8].ord
|
|
208
|
+
when 0x01 then file_endian = CA_BIG_ENDIAN
|
|
209
|
+
when 0x04 then file_endian = CA_LITTLE_ENDIAN
|
|
171
210
|
else
|
|
172
|
-
|
|
173
|
-
unless CArray.endian == endian
|
|
174
|
-
ca.swap_bytes!
|
|
175
|
-
end
|
|
211
|
+
raise "corrupt CArray binary data (endian marker mismatch)"
|
|
176
212
|
end
|
|
177
|
-
|
|
213
|
+
h = self.class.unpack_header(buf, file_endian)
|
|
214
|
+
|
|
215
|
+
if h[:endian_marker] != ENDIAN_MARKER
|
|
216
|
+
raise "corrupt CArray binary data (endian marker mismatch)"
|
|
217
|
+
end
|
|
218
|
+
if h[:version_major] != VERSION_MAJOR
|
|
219
|
+
raise "unsupported CArray binary version #{h[:version_major]}.#{h[:version_minor]}"
|
|
220
|
+
end
|
|
221
|
+
if h[:header_bytes] != HEADER_BYTES
|
|
222
|
+
raise "unsupported CArray binary header size #{h[:header_bytes]}"
|
|
223
|
+
end
|
|
224
|
+
if h[:data_bytes] != h[:elements] * h[:element_bytes]
|
|
225
|
+
raise "corrupt CArray binary data (data_bytes cross-check failed)"
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
swap = (file_endian != CArray.endian)
|
|
229
|
+
data_type = h[:data_type_code]
|
|
230
|
+
ndim = h[:ndim]
|
|
231
|
+
dim = h[:shape][0, ndim]
|
|
232
|
+
bytes = h[:element_bytes]
|
|
233
|
+
|
|
234
|
+
if opt[:data_type] and data_type == CArray.data_type_code(CA_FIXLEN)
|
|
235
|
+
data_type = opt[:data_type]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
ca = CArray.new(data_type, dim, :bytes => bytes)
|
|
239
|
+
ca.load_binary(@io)
|
|
240
|
+
ca[] = ca.swap_bytes if swap
|
|
241
|
+
|
|
242
|
+
if h[:has_mask] != 0
|
|
178
243
|
ca.mask = 0
|
|
179
244
|
ca.mask.load_binary(@io)
|
|
180
245
|
end
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
ca
|
|
246
|
+
|
|
247
|
+
if h[:trailer_bytes] > 0
|
|
248
|
+
trailer_raw = @io.read(h[:trailer_bytes])
|
|
249
|
+
trailer = decode_trailer(trailer_raw)
|
|
250
|
+
ca = apply_trailer(ca, trailer)
|
|
186
251
|
end
|
|
252
|
+
|
|
187
253
|
return ca
|
|
188
254
|
end
|
|
189
|
-
|
|
255
|
+
|
|
256
|
+
private
|
|
257
|
+
|
|
258
|
+
# Build the trailer mapping (String-keyed) from the array's
|
|
259
|
+
# attributes and data_class. Returns {} when there is nothing to
|
|
260
|
+
# serialise.
|
|
261
|
+
def build_trailer (ca)
|
|
262
|
+
trailer = {}
|
|
263
|
+
attrs = ca.attrs # frozen, merged along parent chain
|
|
264
|
+
trailer["attrs"] = attrs unless attrs.empty?
|
|
265
|
+
if ca.has_data_class? and ca.data_class
|
|
266
|
+
trailer["data_class"] = build_data_class_schema(ca.data_class)
|
|
267
|
+
end
|
|
268
|
+
trailer
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Layer 1 (always) + Layer 2 `name` (when the class is named).
|
|
272
|
+
# ruby_marshal_b64 (full-identity round-trip of anonymous / dynamic
|
|
273
|
+
# classes) is deferred past v1.0 — anonymous classes round-trip via
|
|
274
|
+
# Layer 1 synthesis on load (duck-typed, identity not preserved),
|
|
275
|
+
# matching the "data_class identity out of scope for 3.0" pin.
|
|
276
|
+
def build_data_class_schema (klass)
|
|
277
|
+
members = klass.fields.map do |f|
|
|
278
|
+
if f.bitfield?
|
|
279
|
+
raise ArgumentError,
|
|
280
|
+
"CArray.save cannot serialise data_class #{klass.inspect}: " \
|
|
281
|
+
"member #{f.name.inspect} is a bitfield (the v1.0 Layer 1 " \
|
|
282
|
+
"schema is flat-primitive only); use Marshal.dump(ca)"
|
|
283
|
+
end
|
|
284
|
+
pep = SYMBOL_TO_PEP[f.type]
|
|
285
|
+
unless pep
|
|
286
|
+
raise ArgumentError,
|
|
287
|
+
"CArray.save cannot serialise data_class #{klass.inspect}: " \
|
|
288
|
+
"member #{f.name.inspect} has non-primitive type #{f.type.inspect} " \
|
|
289
|
+
"(nested struct / template / fixlen are not expressible in the " \
|
|
290
|
+
"v1.0 Layer 1 schema); use Marshal.dump(ca)"
|
|
291
|
+
end
|
|
292
|
+
{ "name" => f.name, "type" => pep, "offset" => f.offset, "bytes" => f.bytes }
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
schema = {
|
|
296
|
+
"kind" => (klass < CAUnion ? "union" : "struct"),
|
|
297
|
+
"record_bytes" => klass::DATA_SIZE,
|
|
298
|
+
"members" => members,
|
|
299
|
+
}
|
|
300
|
+
schema["name"] = klass.name if klass.name
|
|
301
|
+
schema
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Reconstruct attribute Hash + data_class wrapping from a parsed
|
|
305
|
+
# trailer. Returns the (possibly re-wrapped) array.
|
|
306
|
+
def apply_trailer (ca, trailer)
|
|
307
|
+
if trailer.key?("data_class")
|
|
308
|
+
ca = wrap_data_class(ca, trailer["data_class"])
|
|
309
|
+
end
|
|
310
|
+
if trailer.key?("attrs")
|
|
311
|
+
attrs = trailer["attrs"]
|
|
312
|
+
if attrs.is_a?(Hash) and not attrs.empty?
|
|
313
|
+
ca.instance_variable_set(:@attr, attrs)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
ca
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Load fall-through: prefer the named Ruby class (const_get), else
|
|
320
|
+
# synthesise an anonymous struct from the Layer 1 members[], else
|
|
321
|
+
# leave the raw CA_FIXLEN array untouched.
|
|
322
|
+
def wrap_data_class (ca, schema)
|
|
323
|
+
name = schema["name"]
|
|
324
|
+
if name
|
|
325
|
+
begin
|
|
326
|
+
klass = Kernel.const_get(name)
|
|
327
|
+
rescue NameError
|
|
328
|
+
klass = nil
|
|
329
|
+
end
|
|
330
|
+
if klass and CArray.data_class?(klass)
|
|
331
|
+
return CARecord.wrap(ca, klass)
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
members = schema["members"]
|
|
336
|
+
if members.is_a?(Array) and not members.empty?
|
|
337
|
+
klass = synthesize_data_class(schema)
|
|
338
|
+
return CARecord.wrap(ca, klass)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
ca
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# Build a runtime-defined CArray.struct / .union that reproduces the
|
|
345
|
+
# exact byte layout described by the Layer 1 schema (explicit offsets
|
|
346
|
+
# + total size so trailing padding is preserved).
|
|
347
|
+
def synthesize_data_class (schema)
|
|
348
|
+
members = schema["members"]
|
|
349
|
+
record_bytes = schema["record_bytes"]
|
|
350
|
+
builder = (schema["kind"] == "union") ? CArray.method(:union) : CArray.method(:struct)
|
|
351
|
+
builder.call(:pack => 1, :size => record_bytes) do
|
|
352
|
+
members.each do |m|
|
|
353
|
+
sym = PEP_TO_SYMBOL[m["type"]]
|
|
354
|
+
unless sym
|
|
355
|
+
raise "unknown data_class member type #{m["type"].inspect} in CArray binary data"
|
|
356
|
+
end
|
|
357
|
+
member sym, m["name"], :offset => m["offset"]
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# YAML flow-style emission. Reads like a JSON one-liner but with
|
|
363
|
+
# native non-finite Float literals (.inf / .nan).
|
|
364
|
+
def encode_trailer (trailer)
|
|
365
|
+
visitor = Psych::Visitors::YAMLTree.create
|
|
366
|
+
visitor << trailer
|
|
367
|
+
ast = visitor.tree
|
|
368
|
+
ast.grep(Psych::Nodes::Mapping).each { |n| n.style = Psych::Nodes::Mapping::FLOW }
|
|
369
|
+
ast.grep(Psych::Nodes::Sequence).each { |n| n.style = Psych::Nodes::Sequence::FLOW }
|
|
370
|
+
# line_width: -1 disables the emitter's line folding so the flow
|
|
371
|
+
# trailer stays a single line regardless of length.
|
|
372
|
+
ast.yaml(nil, :line_width => -1).sub(/\A---\s*/, "").strip.b
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
# safe_load is the symmetric guardrail: it restores the
|
|
376
|
+
# JSON-compatible value set + non-finite Floats and refuses any
|
|
377
|
+
# `!ruby/object:` tag, so a hostile trailer cannot execute code.
|
|
378
|
+
def decode_trailer (raw)
|
|
379
|
+
Psych.safe_load(raw, :permitted_classes => [], :aliases => false) || {}
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
class << self
|
|
383
|
+
|
|
384
|
+
# Pack a header field Hash into a HEADER_BYTES buffer, integers in
|
|
385
|
+
# `file_endian` byte order.
|
|
386
|
+
def pack_header (h, file_endian)
|
|
387
|
+
e = (file_endian == CA_LITTLE_ENDIAN) ? "<" : ">"
|
|
388
|
+
shape = h[:shape]
|
|
389
|
+
body = [
|
|
390
|
+
MAGIC, # a8 0
|
|
391
|
+
ENDIAN_MARKER, # L 8
|
|
392
|
+
VERSION_MAJOR, # C 12
|
|
393
|
+
VERSION_MINOR, # C 13
|
|
394
|
+
HEADER_BYTES, # S 14
|
|
395
|
+
h[:has_mask], # C 16
|
|
396
|
+
h[:has_trailer], # C 17
|
|
397
|
+
h[:data_type_code], # C 18
|
|
398
|
+
h[:ndim], # C 19
|
|
399
|
+
0, # L 20 reserved0
|
|
400
|
+
*shape, # q16 24
|
|
401
|
+
h[:element_bytes], # L 152
|
|
402
|
+
h[:flags], # l 156
|
|
403
|
+
h[:elements], # Q 160
|
|
404
|
+
h[:data_offset], # Q 168
|
|
405
|
+
h[:data_bytes], # Q 176
|
|
406
|
+
h[:mask_offset], # Q 184
|
|
407
|
+
h[:mask_bytes], # Q 192
|
|
408
|
+
h[:trailer_offset], # Q 200
|
|
409
|
+
h[:trailer_bytes], # Q 208
|
|
410
|
+
0, # Q 216 data_checksum
|
|
411
|
+
0, # C 224 checksum_algo
|
|
412
|
+
].pack("a8L#{e}CCS#{e}CCCCL#{e}q#{e}16L#{e}l#{e}Q#{e}Q#{e}Q#{e}Q#{e}Q#{e}Q#{e}Q#{e}Q#{e}C")
|
|
413
|
+
body.ljust(HEADER_BYTES, "\x00")
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Parse a HEADER_BYTES buffer (integers in `file_endian`) into a
|
|
417
|
+
# field Hash. Byte-slice unpack mirrors the C struct offsets.
|
|
418
|
+
def unpack_header (buf, file_endian)
|
|
419
|
+
e = (file_endian == CA_LITTLE_ENDIAN) ? "<" : ">"
|
|
420
|
+
{
|
|
421
|
+
endian_marker: buf[8, 4].unpack1("L#{e}"),
|
|
422
|
+
version_major: buf[12].ord,
|
|
423
|
+
version_minor: buf[13].ord,
|
|
424
|
+
header_bytes: buf[14, 2].unpack1("S#{e}"),
|
|
425
|
+
has_mask: buf[16].ord,
|
|
426
|
+
has_trailer: buf[17].ord,
|
|
427
|
+
data_type_code: buf[18].ord,
|
|
428
|
+
ndim: buf[19].ord,
|
|
429
|
+
shape: buf[24, 128].unpack("q#{e}16"),
|
|
430
|
+
element_bytes: buf[152, 4].unpack1("L#{e}"),
|
|
431
|
+
flags: buf[156, 4].unpack1("l#{e}"),
|
|
432
|
+
elements: buf[160, 8].unpack1("Q#{e}"),
|
|
433
|
+
data_offset: buf[168, 8].unpack1("Q#{e}"),
|
|
434
|
+
data_bytes: buf[176, 8].unpack1("Q#{e}"),
|
|
435
|
+
mask_offset: buf[184, 8].unpack1("Q#{e}"),
|
|
436
|
+
mask_bytes: buf[192, 8].unpack1("Q#{e}"),
|
|
437
|
+
trailer_offset: buf[200, 8].unpack1("Q#{e}"),
|
|
438
|
+
trailer_bytes: buf[208, 8].unpack1("Q#{e}"),
|
|
439
|
+
data_checksum: buf[216, 8].unpack1("Q#{e}"),
|
|
440
|
+
checksum_algo: buf[224].ord,
|
|
441
|
+
}
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
end
|
|
445
|
+
|
|
190
446
|
end
|
|
191
447
|
|
|
192
448
|
class CArray
|
|
193
449
|
|
|
194
|
-
|
|
450
|
+
# @overload save(ca, output, **opt)
|
|
451
|
+
# Writes `ca` to `output` in the _CARRAY3 portable format. A
|
|
452
|
+
# String `output` is opened as a binary file; anything else is
|
|
453
|
+
# treated as an IO-like object.
|
|
454
|
+
# @param ca [CArray] array to write.
|
|
455
|
+
# @param output [String, IO] destination path or IO.
|
|
456
|
+
# @param opt [Hash] serializer options (`:endian`).
|
|
457
|
+
# @return [CArray] `ca`.
|
|
458
|
+
def self.save (ca, output, **opt)
|
|
195
459
|
case output
|
|
196
460
|
when String
|
|
197
461
|
open(output, "wb:ASCII-8BIT") { |io|
|
|
198
462
|
return Serializer.new(io).save(ca, **opt)
|
|
199
463
|
}
|
|
200
464
|
else
|
|
201
|
-
return Serializer.new(output).save(ca, **opt)
|
|
465
|
+
return Serializer.new(output).save(ca, **opt)
|
|
202
466
|
end
|
|
203
467
|
end
|
|
204
468
|
|
|
469
|
+
# @overload load(input, **opt)
|
|
470
|
+
# Reads a _CARRAY3 payload from `input`. A String starting with
|
|
471
|
+
# the CArray magic is decoded in place; other Strings are treated
|
|
472
|
+
# as file paths; anything else is used as IO.
|
|
473
|
+
# @param input [String, IO] source path, in-memory payload, or IO.
|
|
474
|
+
# @param opt [Hash] loader options (`:data_type`).
|
|
475
|
+
# @return [CArray]
|
|
205
476
|
def self.load (input, **opt)
|
|
206
477
|
case input
|
|
207
478
|
when String
|
|
208
|
-
if input.
|
|
479
|
+
if input.bytesize >= Serializer::HEADER_BYTES and
|
|
480
|
+
input.byteslice(0, 8) == Serializer::MAGIC
|
|
209
481
|
io = StringIO.new(input)
|
|
210
482
|
return Serializer.new(io).load(**opt)
|
|
211
483
|
else
|
|
@@ -214,31 +486,70 @@ class CArray
|
|
|
214
486
|
}
|
|
215
487
|
end
|
|
216
488
|
else
|
|
217
|
-
return Serializer.new(input).load(**opt)
|
|
489
|
+
return Serializer.new(input).load(**opt)
|
|
218
490
|
end
|
|
219
491
|
end
|
|
220
492
|
|
|
493
|
+
# @overload dump(ca, **opt)
|
|
494
|
+
# Returns `ca` serialized to a String in the _CARRAY3 format.
|
|
495
|
+
# @param ca [CArray] array to serialize.
|
|
496
|
+
# @param opt [Hash] serializer options (`:endian`).
|
|
497
|
+
# @return [String]
|
|
221
498
|
def self.dump (ca, **opt)
|
|
222
|
-
io = StringIO.new("")
|
|
223
|
-
Serializer.new(io).save(ca, **opt)
|
|
499
|
+
io = StringIO.new("".b)
|
|
500
|
+
Serializer.new(io).save(ca, **opt)
|
|
224
501
|
return io.string
|
|
225
502
|
end
|
|
226
503
|
|
|
227
504
|
# for Marshal
|
|
505
|
+
#
|
|
506
|
+
# Decision 4: the Marshal path and the portable format are
|
|
507
|
+
# separated. A CA_OBJECT array Marshal-dumps its element VALUEs
|
|
508
|
+
# directly (Ruby-only, no portability promise); every other array
|
|
509
|
+
# reuses the portable format as a numeric container. So
|
|
510
|
+
# Marshal.dump works for object arrays even though CArray.save
|
|
511
|
+
# refuses them.
|
|
228
512
|
|
|
513
|
+
# @overload marshal_dump
|
|
514
|
+
# Returns the Marshal payload for `self`. Virtual / wrapped
|
|
515
|
+
# arrays are copied first so the payload always describes an
|
|
516
|
+
# owning array.
|
|
517
|
+
# @return [Array]
|
|
229
518
|
def marshal_dump ()
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
519
|
+
target = (self.class != CArray and self.class != CScalar) ? self.copy : self
|
|
520
|
+
if target.data_type == :object
|
|
521
|
+
["object",
|
|
522
|
+
target.shape,
|
|
523
|
+
target.value.to_a,
|
|
524
|
+
(target.has_mask? ? target.mask.to_a : nil)]
|
|
525
|
+
else
|
|
526
|
+
["portable", CArray.dump(target)]
|
|
233
527
|
end
|
|
234
|
-
return CArray.dump(self)
|
|
235
528
|
end
|
|
236
529
|
|
|
530
|
+
# @overload marshal_load(data)
|
|
531
|
+
# Reconstitutes `self` from a Marshal payload produced by
|
|
532
|
+
# {#marshal_dump}.
|
|
533
|
+
# @param data [Array] Marshal payload.
|
|
534
|
+
# @return [void]
|
|
237
535
|
def marshal_load (data)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
536
|
+
tag, *rest = data
|
|
537
|
+
case tag
|
|
538
|
+
when "object"
|
|
539
|
+
shape, values, mask = rest
|
|
540
|
+
ca = CArray.object(*shape)
|
|
541
|
+
ca[] = values
|
|
542
|
+
if mask
|
|
543
|
+
ca.mask = 0
|
|
544
|
+
ca.mask[] = mask
|
|
545
|
+
end
|
|
546
|
+
initialize_copy(ca)
|
|
547
|
+
when "portable"
|
|
548
|
+
ca = CArray.load(StringIO.new(rest[0]))
|
|
549
|
+
initialize_copy(ca)
|
|
550
|
+
else
|
|
551
|
+
raise TypeError, "unrecognised CArray Marshal payload"
|
|
552
|
+
end
|
|
241
553
|
end
|
|
242
554
|
|
|
243
555
|
end
|
|
244
|
-
|