carray-netcdf 1.0.13 → 2.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.
data/lib/io/netcdf.rb DELETED
@@ -1,848 +0,0 @@
1
- require "carray"
2
- require "carray/netcdflib.so"
3
-
4
- module NC
5
-
6
- module_function
7
-
8
- def nc_decode (fd, varid, data)
9
- if fill_value = nc_get_att(fd, varid, "_FillValue")
10
- data[:eq, fill_value] = UNDEF
11
- end
12
- if scale_factor = nc_get_att(fd, varid, "scale_factor")
13
- data *= scale_factor
14
- end
15
- if add_offset = nc_get_att(fd, varid, "add_offset")
16
- data += add_offset
17
- end
18
- return data
19
- end
20
-
21
- def nc_put_att_simple (fd, varid, name, val)
22
- case val
23
- when Float
24
- nc_put_att(fd, varid, name, CA_DOUBLE(val))
25
- when Integer
26
- nc_put_att(fd, varid, name, CA_INT(val))
27
- else
28
- nc_put_att(fd, varid, name, val)
29
- end
30
- end
31
-
32
- def nc_put_var_all (fd, varid, val)
33
- data_type = NC.ca_type(nc_inq_vartype(fd, varid))
34
- dim = (0...nc_inq_varndims(fd, varid)).collect do |dimid|
35
- nc_inq_dimlen(fd, dimid)
36
- end
37
- data = CArray.new(data_type, dim) { val }
38
- nc_put_var(fd, varid, data)
39
- end
40
-
41
- end
42
-
43
- class NCObject
44
-
45
- include NC
46
-
47
- def get_attributes (file_id, var_id)
48
- attrs = {}
49
- varnatts = nc_inq_varnatts(file_id, var_id)
50
- varnatts.times do |i|
51
- attname = nc_inq_attname(file_id, var_id, i)
52
- value = nc_get_att(file_id, var_id, attname)
53
- attrs[attname] = value
54
- end
55
- return attrs.freeze
56
- end
57
-
58
- def get_attributes! (file_id, var_id)
59
- attrs = {}
60
- varnatts = nc_inq_varnatts(file_id, var_id)
61
- varnatts.times do |i|
62
- attname = nc_inq_attname(file_id, var_id, i)
63
- type = nc_inq_atttype(file_id, var_id, attname)
64
- case type
65
- when NC_CHAR
66
- value = nc_get_att(file_id, var_id, attname)
67
- else
68
- len = nc_inq_attlen(file_id, var_id, attname)
69
- value = CArray.new(NC.ca_type(type), [len])
70
- nc_get_att(file_id, var_id, attname, value)
71
- end
72
- attrs[attname] = value
73
- end
74
- return attrs.freeze
75
- end
76
-
77
- def attribute (name)
78
- return @attributes[name]
79
- end
80
-
81
- attr_reader :attributes
82
-
83
- end
84
-
85
- class NCVar < NCObject
86
-
87
- include NC
88
-
89
- TYPENAME = {
90
- NC_CHAR => "char",
91
- NC_BYTE => "byte",
92
- NC_SHORT => "short",
93
- NC_INT => "int",
94
- NC_FLOAT => "float",
95
- NC_DOUBLE => "double",
96
- }
97
-
98
- def initialize (ncfile, var_id)
99
- @ncfile = ncfile
100
- @file_id = ncfile.file_id
101
- @var_id = var_id
102
- @name = nc_inq_varname(@file_id, var_id)
103
- @vartype = nc_inq_vartype(@file_id, var_id)
104
- @dims = ncfile.dims.values_at(*nc_inq_vardimid(@file_id, var_id))
105
- @shape = @dims.map{|d| d.len}
106
- @attributes = get_attributes(@file_id, var_id)
107
- @dims.freeze
108
- @shape.freeze
109
- @attributes.freeze
110
- end
111
-
112
- attr_reader :name, :dims, :attributes
113
-
114
- def definition
115
- defs = { "type" => TYPENAME[@vartype] }
116
- if not dims.empty?
117
- defs["dim"] = dims.map{|x| x.name }
118
- end
119
- atts = {}
120
- get_attributes!(@file_id, @var_id).each do |k,v|
121
- atts[k] = NCFile.convert_attribute_value(v)
122
- end
123
- defs.update(atts)
124
- return defs
125
- end
126
-
127
- def inspect
128
- return "#{@name}#{@dims}"
129
- end
130
-
131
- def is_dim?
132
- begin
133
- nc_inq_dimlen(@file_id, @var_id)
134
- return true
135
- rescue RuntimeError
136
- return false
137
- end
138
- end
139
-
140
- def decode (value)
141
- if @attributes.has_key?("_FillValue")
142
- fill_value = @attributes["_FillValue"]
143
- case value
144
- when CArray
145
- value[:eq, fill_value] = UNDEF
146
- else
147
- value = UNDEF if value == fill_value
148
- end
149
- end
150
- if @attributes.has_key?("missing_value")
151
- missing_values = [@attributes["missing_value"]].flatten
152
- missing_values.each do |mv|
153
- case value
154
- when CArray
155
- value[:eq, mv] = UNDEF
156
- else
157
- if value == mv
158
- value = UNDEF
159
- break
160
- end
161
- end
162
- end
163
- end
164
- if @attributes.has_key?("scale_factor")
165
- value *= @attributes["scale_factor"]
166
- end
167
- if @attributes.has_key?("add_offset")
168
- value += @attributes["add_offset"]
169
- end
170
- return value
171
- end
172
-
173
- def to_ca
174
- return self[]
175
- end
176
-
177
- TIME_UNITS = {
178
- "day" => 86400,
179
- "days" => 86400,
180
- "d" => 86400,
181
- "hour" => 3600,
182
- "hours" => 3600,
183
- "hr" => 3600,
184
- "h" => 3600,
185
- "minute" => 60,
186
- "minutes" => 60,
187
- "min" => 60,
188
- "second" => 1,
189
- "seconds" => 1,
190
- "sec" => 1,
191
- "s" => 1,
192
- }
193
-
194
- def to_time
195
-
196
- if not @attributes.has_key?("units")
197
- raise "variable #{@name} has no attribute 'units'"
198
- elsif @attributes["units"] =~ /\A(.+?)\s+since\s+(.+)\Z/
199
- interval = TIME_UNITS[$1]
200
- basetime = Time.parse($2)
201
- if interval
202
- return self[].convert(:object) {|x| basetime + interval*x }
203
- else
204
- raise "#{$1} is invalid for time units"
205
- end
206
- else
207
- raise "format of #{@name}:units is not valid for time"
208
- end
209
- end
210
-
211
- def [] (*argv)
212
- return get!(*argv)
213
- end
214
-
215
- def get (*argv)
216
- if argv.size > 0 and argv[0].is_a?(Struct::CAIndexInfo)
217
- info = argv.shift
218
- else
219
- info = CArray.scan_index(@shape, argv)
220
- end
221
- out = nil
222
- case info.type
223
- when CA_REG_ADDRESS
224
- addr = info.index[0]
225
- index = []
226
- (0..@shape.size-1).reverse_each do |i|
227
- index[i] = addr % @shape[i]
228
- addr /= @shape[i]
229
- end
230
- out = get_var1(*index)
231
- when CA_REG_FLATTEN
232
- out = get_var[nil]
233
- when CA_REG_POINT
234
- out = get_var1(*info.index)
235
- when CA_REG_ALL
236
- out = get_var()
237
- when CA_REG_BLOCK
238
- use_compact = false
239
- start = []
240
- count = []
241
- stride = []
242
- info.index.each do |idx|
243
- case idx
244
- when Array
245
- start << idx[0]
246
- count << idx[1]
247
- stride << idx[2]
248
- else
249
- use_compact = true
250
- start << idx
251
- count << 1
252
- stride << 1
253
- end
254
- end
255
- if stride.all?{|x| x == 1 }
256
- out = get_vara(start, count)
257
- else
258
- out = get_vars(start, count, stride)
259
- end
260
- if use_compact and out.is_a?(CArray)
261
- out = out.compact
262
- end
263
- when CA_REG_SELECT, CA_REG_GRID
264
- out = get_var[*argv]
265
- else
266
- raise "invalid index"
267
- end
268
- return out
269
- end
270
-
271
- def get! (*argv)
272
- info = CArray.scan_index(@shape, argv)
273
- case info.type
274
- when CA_REG_METHOD_CALL
275
- return decode(get_var)[*argv]
276
- else
277
- return decode(get(info, *argv))
278
- end
279
- end
280
-
281
- def get_var1 (*index)
282
- return nc_get_var1(@file_id, @var_id, index)
283
- end
284
-
285
- def get_var1! (*index)
286
- return decode(get_var1(*index))
287
- end
288
-
289
- def get_var ()
290
- return nc_get_var(@file_id, @var_id)
291
- end
292
-
293
- def get_var! ()
294
- return decode(nc_get_var(@file_id, @var_id))
295
- end
296
-
297
- def get_vara (start, count)
298
- return nc_get_vara(@file_id, @var_id, start, count)
299
- end
300
-
301
- def get_vara! (start, count)
302
- return decode(nc_get_vara(@file_id, @var_id, start, count))
303
- end
304
-
305
- def get_vars (start, count, stride)
306
- return nc_get_vars(@file_id, @var_id, start, count, stride)
307
- end
308
-
309
- def get_vars! (start, count, stride)
310
- return decode(nc_get_vars(@file_id, @var_id, start, count, stride))
311
- end
312
-
313
- def get_varm (start, count, stride, imap)
314
- return nc_get_varm(@file_id, @var_id, start, count, stride, imap)
315
- end
316
-
317
- def get_varm! (start, count, stride, imap)
318
- return decode(nc_get_varm(@file_id, @var_id, start, count, stride, imap))
319
- end
320
-
321
- def method_missing (id, *argv)
322
- to_ca.send(id, *argv)
323
- end
324
-
325
- end
326
-
327
- class NCDim < NCObject
328
-
329
- include NC
330
-
331
- def initialize (ncfile, dim_id)
332
- @ncfile = ncfile
333
- @file_id = ncfile.file_id
334
- @dim_id = dim_id
335
- @name = nc_inq_dimname(@file_id, @dim_id)
336
- @len = nc_inq_dimlen(@file_id, @dim_id)
337
- end
338
-
339
- attr_reader :name, :len
340
-
341
- def definition
342
- return @len
343
- end
344
-
345
- def inspect
346
- return "#{@name}=#{@len}"
347
- end
348
-
349
- def to_i
350
- return @dim_id
351
- end
352
-
353
- def to_ca
354
- return self[]
355
- end
356
-
357
- def [] (*argv)
358
- return @ncfile[name][*argv]
359
- end
360
-
361
- end
362
-
363
- class NCFile < NCObject
364
-
365
- include NC
366
-
367
- def self.open (filename)
368
- file_id = NC.open(filename)
369
- if block_given?
370
- begin
371
- nc = NCFile.new(file_id)
372
- yield(nc)
373
- ensure
374
- nc.close
375
- end
376
- else
377
- return NCFile.new(file_id)
378
- end
379
- end
380
-
381
- def initialize (file_id)
382
- @file_id = file_id
383
- @dims = []
384
- @vars = []
385
- @name2dim = {}
386
- @name2var = {}
387
- @attributes = get_attributes(@file_id, NC_GLOBAL)
388
- @attributes.freeze
389
- parse_metadata()
390
- end
391
-
392
- attr_reader :file_id, :dims, :vars, :attributes
393
-
394
- def close ()
395
- nc_close(@file_id)
396
- end
397
-
398
- def parse_metadata ()
399
- ndims = nc_inq_ndims(@file_id)
400
- ndims.times do |i|
401
- dim = NCDim.new(self, i)
402
- @dims[i] = dim
403
- @name2dim[dim.name] = dim
404
- end
405
- @dims.freeze
406
- @name2dim.freeze
407
- nvars = nc_inq_nvars(@file_id)
408
- nvars.times do |i|
409
- var = NCVar.new(self, i)
410
- @vars[i] = var
411
- @name2var[var.name] = var
412
- end
413
- @vars.freeze
414
- @name2var.freeze
415
- end
416
-
417
- def self.convert_attribute_value (value)
418
- case value
419
- when CArray
420
- if value.length == 1
421
- case value.data_type
422
- when CA_UINT8
423
- value = { "char" => value[0] }
424
- when CA_INT8
425
- value = { "byte" => value[0] }
426
- when CA_SHORT
427
- value = { "short" => value[0] }
428
- when CA_INT
429
- value = { "int" => value[0] }
430
- when CA_FLOAT
431
- value = { "float" => value[0] }
432
- when CA_DOUBLE
433
- value = { "double" => value[0] }
434
- end
435
- else
436
- case value.data_type
437
- when CA_UINT8
438
- value = { "char" => value.to_a }
439
- when CA_INT8
440
- value = { "byte" => value.to_a }
441
- when CA_SHORT
442
- value = { "short" => value.to_a }
443
- when CA_INT
444
- value = { "int" => value.to_a }
445
- when CA_FLOAT
446
- value = { "float" => value.to_a }
447
- when CA_DOUBLE
448
- value = { "double" => value.to_a }
449
- end
450
- end
451
- end
452
- return value
453
- end
454
-
455
- def definition
456
- atts = {}
457
- get_attributes!(@file_id, NC_GLOBAL).each do |k,v|
458
- atts[k] = NCFile.convert_attribute_value(v)
459
- end
460
- {
461
- "dimensions" => @dims.map{|x| [x.name, x.definition] }.to_h,
462
- "variables" => @vars.map{|x| [x.name, x.definition] }.to_h,
463
- "attributes" => atts
464
- }
465
- end
466
-
467
- def [] (name)
468
- return @name2var[name]
469
- end
470
-
471
- def dim (name)
472
- return @name2dim[name]
473
- end
474
-
475
- def has_dim?(name)
476
- return @name2dim.has_key?(name)
477
- end
478
-
479
- def has_var?(name)
480
- return @name2var.has_key?(name)
481
- end
482
-
483
- end
484
-
485
- class NCFileWriter
486
-
487
- include NC
488
-
489
- class Var
490
-
491
- include NC
492
-
493
- TYPEMAP = {
494
- "char" => NC_CHAR,
495
- "byte" => NC_BYTE,
496
- "short" => NC_SHORT,
497
- "int" => NC_INT,
498
- "float" => NC_FLOAT,
499
- "double" => NC_DOUBLE,
500
- }
501
-
502
- def initialize (ncfile, name, definition, compression = 0, define_mode = true)
503
- @ncfile = ncfile
504
- @file_id = ncfile.file_id
505
- @name = name
506
- @type = definition["type"] || NC_FLOAT
507
- if @type.is_a?(String)
508
- if TYPEMAP.include?(@type)
509
- @type = TYPEMAP[@type]
510
- else
511
- raise "invalid variable data type"
512
- end
513
- end
514
- @dims = definition["dim"]
515
- if @dims
516
- @shape = @dims.map{|key| @ncfile.dim(key).len }
517
- dim_ids = @dims.map{|key| @ncfile.dim(key).to_i }
518
- else
519
- dim_ids = []
520
- end
521
- if define_mode
522
- @var_id = nc_def_var(@file_id, @name, @type, dim_ids)
523
- if compression != 0
524
- nc_def_var_deflate(@file_id, @var_id, 1, 1, compression)
525
- end
526
- else
527
- @var_id = nc_inq_varid(@file_id, @name)
528
- end
529
- @attributes = definition.dup
530
- @attributes.delete("type")
531
- @attributes.delete("dim")
532
- if define_mode
533
- @attributes.each do |name, value|
534
- value = NCFileWriter.convert_attribute_value(value)
535
- begin
536
- nc_put_att(@file_id, @var_id, name, value)
537
- rescue => e
538
- raise(e.class, e.message + " {#{name}: #{value}}")
539
- end
540
- end
541
- end
542
- @attributes.freeze
543
- end
544
-
545
- attr_reader :name, :type, :attributes
546
-
547
- def to_i
548
- return @var_id
549
- end
550
-
551
- def []= (*argv)
552
- put(*argv)
553
- end
554
-
555
- def put (*argv)
556
- value = argv.pop
557
- info = CArray.scan_index(@shape, argv)
558
- case info.type
559
- when CA_REG_ADDRESS
560
- addr = info.index[0]
561
- index = []
562
- (0..@shape.size-1).reverse_each do |i|
563
- index[i] = addr % @shape[i]
564
- addr /= @shape[i]
565
- end
566
- put_var1(index, value)
567
- when CA_REG_FLATTEN
568
- put_var(value)
569
- when CA_REG_POINT
570
- put_var1(info.index, value)
571
- when CA_REG_ALL
572
- put_var(value)
573
- when CA_REG_BLOCK
574
- start = []
575
- count = []
576
- stride = []
577
- info.index.each do |idx|
578
- case idx
579
- when Array
580
- start << idx[0]
581
- count << idx[1]
582
- stride << idx[2]
583
- else
584
- start << idx
585
- count << 1
586
- stride << 1
587
- end
588
- end
589
- if stride.all?{|x| x == 1 }
590
- put_vara(start, count, value)
591
- else
592
- put_vars(start, count, stride, value)
593
- end
594
- else
595
- raise "invalid index"
596
- end
597
- end
598
-
599
- def put_var1 (index, value)
600
- return nc_put_var1(@file_id, @var_id, index, value)
601
- end
602
-
603
- def put_var (value)
604
- return nc_put_var(@file_id, @var_id, value)
605
- end
606
-
607
- def put_vara (start, count, value)
608
- return nc_put_vara(@file_id, @var_id, start, count, value)
609
- end
610
-
611
- def put_vars (start, count, stride, value)
612
- return nc_put_vars(@file_id, @var_id, start, count, stride, value)
613
- end
614
-
615
- def get_varm (start, count, stride, imap, value)
616
- return nc_put_varm(@file_id, @var_id, start, count, stride, imap, value)
617
- end
618
-
619
- end
620
-
621
- class Dim
622
-
623
- include NC
624
-
625
- def initialize (ncfile, name, len, define_mode = true)
626
- @ncfile = ncfile
627
- @file_id = ncfile.file_id
628
- @name = name
629
- @len = len
630
- if define_mode
631
- @dim_id = nc_def_dim(@file_id, @name, @len)
632
- else
633
- @dim_id = nc_inq_dimid(@file_id, @name)
634
- end
635
- end
636
-
637
- attr_reader :name, :len
638
-
639
- def to_i
640
- return @dim_id
641
- end
642
-
643
- end
644
-
645
- TYPEMAP = {
646
- "char" => NC_CHAR,
647
- "byte" => NC_BYTE,
648
- "short" => NC_SHORT,
649
- "int" => NC_INT,
650
- "float" => NC_FLOAT,
651
- "double" => NC_DOUBLE,
652
- }
653
-
654
- def initialize (file, mode = 0, template: nil, compression: 0)
655
- @dims = []
656
- @name2dim = {}
657
- @vars = []
658
- @name2var = {}
659
- @compression = compression
660
- @attributes = nil
661
- if template.nil?
662
- if compression > 0
663
- @file_id = nc_create(file, mode|NC_NETCDF4)
664
- else
665
- @file_id = nc_create(file, mode)
666
- end
667
- else
668
- IO.popen("ncgen -o #{file}", "w") { |io|
669
- io.write(template)
670
- }
671
- if compression > 0
672
- @file_id = nc_open(file, mode|NC_WRITE|NC_SHARE|NC_NETCDF4)
673
- else
674
- @file_id = nc_open(file, mode|NC_WRITE|NC_SHARE)
675
- end
676
- NCFile.open(file) {|nc|
677
- define nc.definition, define_mode: false
678
- }
679
- end
680
- end
681
-
682
- attr_reader :file_id
683
-
684
- # key of Hash in definition should be String
685
- def normalize_definition (definition)
686
- out = {}
687
- definition.each do |key, value|
688
- if value.is_a?(Hash)
689
- value = normalize_definition(value)
690
- end
691
- out[key.to_s] = value
692
- end
693
- return out
694
- end
695
-
696
- def check_definition (definition)
697
- if definition.has_key?("dimensions")
698
- dim_keys = definition["dimensions"].keys
699
- else
700
- raise "NetCDF definition shoud have 'dimensions' entry"
701
- end
702
- if definition.has_key?("variables")
703
- definition["variables"].each do |name, var_def|
704
- unless var_def.has_key?("type")
705
- warn "NCFileWriter: Variable '#{name}' does not have 'type' entry in definition"
706
- else
707
- unless ["char","byte","short","int","float","double"].include?(var_def["type"])
708
- raise "invalid type of NetCDF Variable '#{name}'"
709
- end
710
- end
711
- unless var_def.has_key?("dim")
712
- warn "NCFileWriter: Variable '#{name}' does not have 'dim' entry in definition"
713
- else
714
- unless var_def["dim"].is_a?(Array)
715
- raise "'dim' of NetCDF Variable '#{name}' should be an Array"
716
- else
717
- var_def["dim"].each do |dim|
718
- unless dim_keys.include?(dim)
719
- raise "invalid dimension '#{dim}' for NetCDF Variable '#{name}'"
720
- end
721
- end
722
- end
723
- end
724
- var_def.each do |k, v|
725
- case v
726
- when Hash
727
- if v.size > 1 or not ["byte","short","int","float","double"].include?(v.keys[0])
728
- raise "invalid specification in attribute '#{k}' for NetCDF Variable '#{name}'"
729
- end
730
- end
731
- end
732
- end
733
- end
734
- end
735
-
736
- def self.convert_attribute_value (value)
737
- case value
738
- when Hash
739
- type, value = *value.first
740
- case type
741
- when "char"
742
- value = CA_UINT8(value)
743
- when "byte"
744
- value = CA_INT8(value)
745
- when "short"
746
- value = CA_SHORT(value)
747
- when "int"
748
- value = CA_INT(value)
749
- when "float"
750
- value = CA_FLOAT(value)
751
- when "double"
752
- value = CA_DOUBLE(value)
753
- end
754
- end
755
- return value
756
- end
757
-
758
- def define (definition, define_mode: true)
759
- definition = normalize_definition(definition)
760
- check_definition(definition)
761
- if definition.has_key?("dimensions") and definition["dimensions"].is_a?(Hash)
762
- definition["dimensions"].each do |name, len|
763
- dim = Dim.new(self, name, len.to_i, define_mode)
764
- @dims.push dim
765
- @name2dim[name] = dim
766
- end
767
- else
768
- warn "NCFileWriter: No dimensions in definition"
769
- end
770
- if definition.has_key?("variables") and definition["variables"].is_a?(Hash)
771
- definition["variables"].each do |name, info|
772
- var = Var.new(self, name, info, @compression, define_mode)
773
- @vars.push var
774
- @name2var[name] = var
775
- end
776
- else
777
- warn "NCFileWriter: No variables in definition"
778
- end
779
- if definition.has_key?("attributes") and definition["attributes"].is_a?(Hash)
780
- @attributes = definition["attributes"]
781
- if define_mode
782
- @attributes.each do |name, value|
783
- value = NCFileWriter.convert_attribute_value(value)
784
- begin
785
- nc_put_att(@file_id, NC_GLOBAL, name, value)
786
- rescue => e
787
- raise(e.class, e.error_message + " {#{name}: #{value}}")
788
- end
789
- end
790
- end
791
- @attributes.freeze
792
- else
793
- @attributes = {}.freeze
794
- end
795
- if define_mode
796
- nc_enddef(@file_id)
797
- end
798
- end
799
-
800
- def dim (name)
801
- return @name2dim[name]
802
- end
803
-
804
- def [] (name)
805
- return @name2var[name]
806
- end
807
-
808
- def []= (name, value)
809
- return @name2var[name].put(value)
810
- end
811
-
812
- def close
813
- nc_close(@file_id)
814
- end
815
-
816
- end
817
-
818
- class NCFileWriter
819
-
820
- def self.scaled_integer (type, value)
821
- min, max = value.min, value.max
822
- case type
823
- when :short
824
- short_max = (0x7fff-1).to_f
825
- fillvalue = CA_SHORT(0x7fff+1)[0]
826
- scale_factor = (max - min)/short_max
827
- add_offset = min + short_max*scale_factor
828
- packed = ((value - add_offset)/scale_factor).short
829
- when :int
830
- int_max = (0x7fffffff-1).to_f
831
- fillvalue = CA_INT(0x7fffffff+1)[0]
832
- scale_factor = (max - min)/int_max
833
- add_offset = min + int_max*scale_factor
834
- packed = ((value - add_offset)/scale_factor).int
835
- else
836
- raise
837
- end
838
- packed.attribute["scale_factor"] = scale_factor
839
- packed.attribute["add_offset"] = add_offset
840
- if value.has_mask?
841
- packed[:is_masked] = fillvalue
842
- packed.attribute["_FillValue"] = fillvalue
843
- end
844
- return packed
845
- end
846
-
847
- end
848
-