openc3 5.0.8 → 5.0.10
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/openc3cli +128 -95
- data/data/config/_id_items.yaml +2 -1
- data/data/config/_id_params.yaml +2 -1
- data/data/config/_items.yaml +2 -1
- data/data/config/_params.yaml +2 -1
- data/data/config/command_modifiers.yaml +30 -0
- data/data/config/item_modifiers.yaml +10 -0
- data/data/config/microservice.yaml +12 -0
- data/data/config/param_item_modifiers.yaml +10 -0
- data/data/config/plugins.yaml +0 -9
- data/data/config/telemetry_modifiers.yaml +10 -0
- data/data/config/widgets.yaml +49 -73
- data/ext/openc3/ext/packet/packet.c +20 -2
- data/ext/openc3/ext/structure/structure.c +12 -17
- data/lib/openc3/accessors/accessor.rb +71 -0
- data/lib/openc3/accessors/binary_accessor.rb +1226 -0
- data/lib/openc3/accessors/cbor_accessor.rb +83 -0
- data/lib/openc3/accessors/html_accessor.rb +28 -0
- data/lib/openc3/accessors/json_accessor.rb +131 -0
- data/lib/openc3/accessors/xml_accessor.rb +67 -0
- data/lib/openc3/accessors.rb +23 -0
- data/lib/openc3/api/cmd_api.rb +5 -2
- data/lib/openc3/api/interface_api.rb +15 -0
- data/lib/openc3/api/limits_api.rb +3 -3
- data/lib/openc3/config/config_parser.rb +10 -4
- data/lib/openc3/core_ext/file.rb +5 -0
- data/lib/openc3/core_ext/tempfile.rb +20 -0
- data/lib/openc3/io/json_rpc.rb +3 -3
- data/lib/openc3/microservices/cleanup_microservice.rb +7 -5
- data/lib/openc3/models/cvt_model.rb +1 -10
- data/lib/openc3/models/interface_model.rb +41 -0
- data/lib/openc3/models/microservice_model.rb +33 -1
- data/lib/openc3/models/plugin_model.rb +1 -1
- data/lib/openc3/models/target_model.rb +85 -68
- data/lib/openc3/packets/binary_accessor.rb +2 -1207
- data/lib/openc3/packets/packet.rb +106 -6
- data/lib/openc3/packets/packet_config.rb +30 -7
- data/lib/openc3/packets/parsers/limits_response_parser.rb +1 -3
- data/lib/openc3/packets/parsers/processor_parser.rb +1 -2
- data/lib/openc3/packets/parsers/xtce_parser.rb +68 -3
- data/lib/openc3/packets/structure.rb +39 -14
- data/lib/openc3/packets/structure_item.rb +15 -1
- data/lib/openc3/script/storage.rb +19 -1
- data/lib/openc3/utilities/local_mode.rb +521 -0
- data/lib/openc3/utilities/simulated_target.rb +3 -2
- data/lib/openc3/utilities/target_file.rb +146 -0
- data/lib/openc3/version.rb +5 -5
- data/lib/openc3.rb +1 -0
- metadata +45 -7
@@ -0,0 +1,1226 @@
|
|
1
|
+
# encoding: ascii-8bit
|
2
|
+
|
3
|
+
# Copyright 2022 Ball Aerospace & Technologies Corp.
|
4
|
+
# All Rights Reserved.
|
5
|
+
#
|
6
|
+
# This program is free software; you can modify and/or redistribute it
|
7
|
+
# under the terms of the GNU Affero General Public License
|
8
|
+
# as published by the Free Software Foundation; version 3 with
|
9
|
+
# attribution addendums as found in the LICENSE.txt
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU Affero General Public License for more details.
|
15
|
+
|
16
|
+
# Modified by OpenC3, Inc.
|
17
|
+
# All changes Copyright 2022, OpenC3, Inc.
|
18
|
+
# All Rights Reserved
|
19
|
+
|
20
|
+
# This file contains the implementation of the BinaryAccessor class.
|
21
|
+
# This class allows for easy reading and writing of binary data in Ruby
|
22
|
+
|
23
|
+
require 'openc3/accessors/accessor'
|
24
|
+
require 'openc3/ext/packet' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
|
25
|
+
|
26
|
+
module OpenC3
|
27
|
+
# Provides methods for binary reading and writing
|
28
|
+
class BinaryAccessor < Accessor
|
29
|
+
# Constants for ruby packing directives
|
30
|
+
PACK_8_BIT_INT = 'c'
|
31
|
+
PACK_NATIVE_16_BIT_INT = 's'
|
32
|
+
PACK_LITTLE_ENDIAN_16_BIT_UINT = 'v'
|
33
|
+
PACK_BIG_ENDIAN_16_BIT_UINT = 'n'
|
34
|
+
PACK_NATIVE_32_BIT_INT = 'l'
|
35
|
+
PACK_NATIVE_32_BIT_UINT = 'L'
|
36
|
+
PACK_NATIVE_64_BIT_INT = 'q'
|
37
|
+
PACK_NATIVE_64_BIT_UINT = 'Q'
|
38
|
+
PACK_LITTLE_ENDIAN_32_BIT_UINT = 'V'
|
39
|
+
PACK_BIG_ENDIAN_32_BIT_UINT = 'N'
|
40
|
+
PACK_LITTLE_ENDIAN_32_BIT_FLOAT = 'e'
|
41
|
+
PACK_LITTLE_ENDIAN_64_BIT_FLOAT = 'E'
|
42
|
+
PACK_BIG_ENDIAN_32_BIT_FLOAT = 'g'
|
43
|
+
PACK_BIG_ENDIAN_64_BIT_FLOAT = 'G'
|
44
|
+
PACK_NULL_TERMINATED_STRING = 'Z*'
|
45
|
+
PACK_BLOCK = 'a*'
|
46
|
+
PACK_8_BIT_INT_ARRAY = 'c*'
|
47
|
+
PACK_8_BIT_UINT_ARRAY = 'C*'
|
48
|
+
PACK_NATIVE_16_BIT_INT_ARRAY = 's*'
|
49
|
+
PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY = 'n*'
|
50
|
+
PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY = 'v*'
|
51
|
+
PACK_NATIVE_32_BIT_INT_ARRAY = 'l*'
|
52
|
+
PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY = 'N*'
|
53
|
+
PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY = 'V*'
|
54
|
+
PACK_NATIVE_64_BIT_INT_ARRAY = 'q*'
|
55
|
+
PACK_NATIVE_64_BIT_UINT_ARRAY = 'Q*'
|
56
|
+
PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY = 'e*'
|
57
|
+
PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY = 'E*'
|
58
|
+
PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY = 'g*'
|
59
|
+
PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY = 'G*'
|
60
|
+
|
61
|
+
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
62
|
+
MIN_INT8 = -128
|
63
|
+
MAX_INT8 = 127
|
64
|
+
MAX_UINT8 = 255
|
65
|
+
MIN_INT16 = -32768
|
66
|
+
MAX_INT16 = 32767
|
67
|
+
MAX_UINT16 = 65535
|
68
|
+
MIN_INT32 = -(2**31)
|
69
|
+
MAX_INT32 = (2**31) - 1
|
70
|
+
MAX_UINT32 = (2**32) - 1
|
71
|
+
MIN_INT64 = -(2**63)
|
72
|
+
MAX_INT64 = (2**63) - 1
|
73
|
+
MAX_UINT64 = (2**64) - 1
|
74
|
+
end
|
75
|
+
|
76
|
+
# Additional Constants
|
77
|
+
ZERO_STRING = "\000"
|
78
|
+
|
79
|
+
# Valid data types
|
80
|
+
DATA_TYPES = [:INT, :UINT, :FLOAT, :STRING, :BLOCK]
|
81
|
+
|
82
|
+
# Valid overflow types
|
83
|
+
OVERFLOW_TYPES = [:TRUNCATE, :SATURATE, :ERROR, :ERROR_ALLOW_HEX]
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
# Determines the endianness of the host running this code
|
88
|
+
#
|
89
|
+
# This method is protected to force the use of the constant
|
90
|
+
# HOST_ENDIANNESS rather than this method
|
91
|
+
#
|
92
|
+
# @return [Symbol] :BIG_ENDIAN or :LITTLE_ENDIAN
|
93
|
+
def self.get_host_endianness
|
94
|
+
value = 0x01020304
|
95
|
+
packed = [value].pack(PACK_NATIVE_32_BIT_UINT)
|
96
|
+
unpacked = packed.unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT)[0]
|
97
|
+
if unpacked == value
|
98
|
+
:LITTLE_ENDIAN
|
99
|
+
else
|
100
|
+
:BIG_ENDIAN
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.raise_buffer_error(read_write, buffer, data_type, given_bit_offset, given_bit_size)
|
105
|
+
raise ArgumentError, "#{buffer.length} byte buffer insufficient to #{read_write} #{data_type} at bit_offset #{given_bit_offset} with bit_size #{given_bit_size}"
|
106
|
+
end
|
107
|
+
|
108
|
+
public
|
109
|
+
|
110
|
+
# Store the host endianness so that it only has to be determined once
|
111
|
+
HOST_ENDIANNESS = get_host_endianness()
|
112
|
+
# Valid endianess
|
113
|
+
ENDIANNESS = [:BIG_ENDIAN, :LITTLE_ENDIAN]
|
114
|
+
|
115
|
+
def self.read_item(item, buffer)
|
116
|
+
return nil if item.data_type == :DERIVED
|
117
|
+
if item.array_size
|
118
|
+
return read_array(item.bit_offset, item.bit_size, item.data_type, item.array_size, buffer, item.endianness)
|
119
|
+
else
|
120
|
+
return read(item.bit_offset, item.bit_size, item.data_type, buffer, item.endianness)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.write_item(item, value, buffer)
|
125
|
+
return nil if item.data_type == :DERIVED
|
126
|
+
if item.array_size
|
127
|
+
return write_array(value, item.bit_offset, item.bit_size, item.data_type, item.array_size, buffer, item.endianness, item.overflow)
|
128
|
+
else
|
129
|
+
return write(value, item.bit_offset, item.bit_size, item.data_type, buffer, item.endianness, item.overflow)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
|
134
|
+
# Reads binary data of any data type from a buffer
|
135
|
+
#
|
136
|
+
# @param bit_offset [Integer] Bit offset to the start of the item. A
|
137
|
+
# negative number means to offset from the end of the buffer.
|
138
|
+
# @param bit_size [Integer] Size of the item in bits
|
139
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
140
|
+
# @param buffer [String] Binary string buffer to read from
|
141
|
+
# @param endianness [Symbol] {ENDIANNESS}
|
142
|
+
# @return [Integer] value read from the buffer
|
143
|
+
def self.read(bit_offset, bit_size, data_type, buffer, endianness)
|
144
|
+
given_bit_offset = bit_offset
|
145
|
+
given_bit_size = bit_size
|
146
|
+
|
147
|
+
bit_offset = check_bit_offset_and_size(:read, given_bit_offset, given_bit_size, data_type, buffer)
|
148
|
+
|
149
|
+
# If passed a negative bit size with strings or blocks
|
150
|
+
# recalculate based on the buffer length
|
151
|
+
if (bit_size <= 0) && ((data_type == :STRING) || (data_type == :BLOCK))
|
152
|
+
bit_size = (buffer.length * 8) - bit_offset + bit_size
|
153
|
+
if bit_size == 0
|
154
|
+
return ""
|
155
|
+
elsif bit_size < 0
|
156
|
+
raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
result, lower_bound, upper_bound = check_bounds_and_buffer_size(bit_offset, bit_size, buffer.length, endianness, data_type)
|
161
|
+
raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size) unless result
|
162
|
+
|
163
|
+
if (data_type == :STRING) || (data_type == :BLOCK)
|
164
|
+
#######################################
|
165
|
+
# Handle :STRING and :BLOCK data types
|
166
|
+
#######################################
|
167
|
+
|
168
|
+
if byte_aligned(bit_offset)
|
169
|
+
if data_type == :STRING
|
170
|
+
return buffer[lower_bound..upper_bound].unpack('Z*')[0]
|
171
|
+
else
|
172
|
+
return buffer[lower_bound..upper_bound].unpack('a*')[0]
|
173
|
+
end
|
174
|
+
else
|
175
|
+
raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
|
176
|
+
end
|
177
|
+
|
178
|
+
elsif (data_type == :INT) || (data_type == :UINT)
|
179
|
+
###################################
|
180
|
+
# Handle :INT and :UINT data types
|
181
|
+
###################################
|
182
|
+
|
183
|
+
if byte_aligned(bit_offset) && even_bit_size(bit_size)
|
184
|
+
|
185
|
+
if data_type == :INT
|
186
|
+
###########################################################
|
187
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :INT
|
188
|
+
###########################################################
|
189
|
+
|
190
|
+
case bit_size
|
191
|
+
when 8
|
192
|
+
return buffer[lower_bound].unpack(PACK_8_BIT_INT)[0]
|
193
|
+
when 16
|
194
|
+
if endianness == HOST_ENDIANNESS
|
195
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_16_BIT_INT)[0]
|
196
|
+
else # endianness != HOST_ENDIANNESS
|
197
|
+
temp = buffer[lower_bound..upper_bound].reverse
|
198
|
+
return temp.unpack(PACK_NATIVE_16_BIT_INT)[0]
|
199
|
+
end
|
200
|
+
when 32
|
201
|
+
if endianness == HOST_ENDIANNESS
|
202
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_32_BIT_INT)[0]
|
203
|
+
else # endianness != HOST_ENDIANNESS
|
204
|
+
temp = buffer[lower_bound..upper_bound].reverse
|
205
|
+
return temp.unpack(PACK_NATIVE_32_BIT_INT)[0]
|
206
|
+
end
|
207
|
+
when 64
|
208
|
+
if endianness == HOST_ENDIANNESS
|
209
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_INT)[0]
|
210
|
+
else # endianness != HOST_ENDIANNESS
|
211
|
+
temp = buffer[lower_bound..upper_bound].reverse
|
212
|
+
return temp.unpack(PACK_NATIVE_64_BIT_INT)[0]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
else # data_type == :UINT
|
216
|
+
###########################################################
|
217
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :UINT
|
218
|
+
###########################################################
|
219
|
+
|
220
|
+
case bit_size
|
221
|
+
when 8
|
222
|
+
return buffer.getbyte(lower_bound)
|
223
|
+
when 16
|
224
|
+
if endianness == :BIG_ENDIAN
|
225
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_16_BIT_UINT)[0]
|
226
|
+
else # endianness == :LITTLE_ENDIAN
|
227
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_16_BIT_UINT)[0]
|
228
|
+
end
|
229
|
+
when 32
|
230
|
+
if endianness == :BIG_ENDIAN
|
231
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_UINT)[0]
|
232
|
+
else # endianness == :LITTLE_ENDIAN
|
233
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT)[0]
|
234
|
+
end
|
235
|
+
when 64
|
236
|
+
if endianness == HOST_ENDIANNESS
|
237
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_UINT)[0]
|
238
|
+
else # endianness != HOST_ENDIANNESS
|
239
|
+
temp = buffer[lower_bound..upper_bound].reverse
|
240
|
+
return temp.unpack(PACK_NATIVE_64_BIT_UINT)[0]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
else
|
246
|
+
##########################
|
247
|
+
# Handle :INT and :UINT Bitfields
|
248
|
+
##########################
|
249
|
+
|
250
|
+
# Extract Data for Bitfield
|
251
|
+
if endianness == :LITTLE_ENDIAN
|
252
|
+
# Bitoffset always refers to the most significant bit of a bitfield
|
253
|
+
num_bytes = (((bit_offset % 8) + bit_size - 1) / 8) + 1
|
254
|
+
upper_bound = bit_offset / 8
|
255
|
+
lower_bound = upper_bound - num_bytes + 1
|
256
|
+
|
257
|
+
if lower_bound < 0
|
258
|
+
raise(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset #{given_bit_offset} and bit_size #{given_bit_size} is invalid")
|
259
|
+
end
|
260
|
+
|
261
|
+
temp_data = buffer[lower_bound..upper_bound].reverse
|
262
|
+
else
|
263
|
+
temp_data = buffer[lower_bound..upper_bound]
|
264
|
+
end
|
265
|
+
|
266
|
+
# Determine temp upper bound
|
267
|
+
temp_upper = upper_bound - lower_bound
|
268
|
+
|
269
|
+
# Handle Bitfield
|
270
|
+
start_bits = bit_offset % 8
|
271
|
+
start_mask = ~(0xFF << (8 - start_bits))
|
272
|
+
total_bits = (temp_upper + 1) * 8
|
273
|
+
right_shift = total_bits - start_bits - bit_size
|
274
|
+
|
275
|
+
# Mask off unwanted bits at beginning
|
276
|
+
temp = temp_data.getbyte(0) & start_mask
|
277
|
+
|
278
|
+
if upper_bound > lower_bound
|
279
|
+
# Combine bytes into a FixNum
|
280
|
+
temp_data[1..temp_upper].each_byte { |temp_value| temp = temp << 8; temp = temp + temp_value }
|
281
|
+
end
|
282
|
+
|
283
|
+
# Shift off unwanted bits at end
|
284
|
+
temp = temp >> right_shift
|
285
|
+
|
286
|
+
if data_type == :INT
|
287
|
+
# Convert to negative if necessary
|
288
|
+
if (bit_size > 1) && (temp[bit_size - 1] == 1)
|
289
|
+
temp = -((1 << bit_size) - temp)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
return temp
|
294
|
+
end
|
295
|
+
|
296
|
+
elsif data_type == :FLOAT
|
297
|
+
##########################
|
298
|
+
# Handle :FLOAT data type
|
299
|
+
##########################
|
300
|
+
|
301
|
+
if byte_aligned(bit_offset)
|
302
|
+
case bit_size
|
303
|
+
when 32
|
304
|
+
if endianness == :BIG_ENDIAN
|
305
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_FLOAT)[0]
|
306
|
+
else # endianness == :LITTLE_ENDIAN
|
307
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT)[0]
|
308
|
+
end
|
309
|
+
when 64
|
310
|
+
if endianness == :BIG_ENDIAN
|
311
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_64_BIT_FLOAT)[0]
|
312
|
+
else # endianness == :LITTLE_ENDIAN
|
313
|
+
return buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT)[0]
|
314
|
+
end
|
315
|
+
else
|
316
|
+
raise(ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}")
|
317
|
+
end
|
318
|
+
else
|
319
|
+
raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
|
320
|
+
end
|
321
|
+
|
322
|
+
else
|
323
|
+
############################
|
324
|
+
# Handle Unknown data types
|
325
|
+
############################
|
326
|
+
|
327
|
+
raise(ArgumentError, "data_type #{data_type} is not recognized")
|
328
|
+
end
|
329
|
+
|
330
|
+
return return_value
|
331
|
+
end
|
332
|
+
|
333
|
+
# Writes binary data of any data type to a buffer
|
334
|
+
#
|
335
|
+
# @param value [Varies] Value to write into the buffer
|
336
|
+
# @param bit_offset [Integer] Bit offset to the start of the item. A
|
337
|
+
# negative number means to offset from the end of the buffer.
|
338
|
+
# @param bit_size [Integer] Size of the item in bits
|
339
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
340
|
+
# @param buffer [String] Binary string buffer to write to
|
341
|
+
# @param endianness [Symbol] {ENDIANNESS}
|
342
|
+
# @param overflow [Symbol] {OVERFLOW_TYPES}
|
343
|
+
# @return [Integer] value passed in as a parameter
|
344
|
+
def self.write(value, bit_offset, bit_size, data_type, buffer, endianness, overflow)
|
345
|
+
given_bit_offset = bit_offset
|
346
|
+
given_bit_size = bit_size
|
347
|
+
|
348
|
+
bit_offset = check_bit_offset_and_size(:write, given_bit_offset, given_bit_size, data_type, buffer)
|
349
|
+
|
350
|
+
# If passed a negative bit size with strings or blocks
|
351
|
+
# recalculate based on the value length in bytes
|
352
|
+
if (bit_size <= 0) && ((data_type == :STRING) || (data_type == :BLOCK))
|
353
|
+
value = value.to_s
|
354
|
+
bit_size = value.length * 8
|
355
|
+
end
|
356
|
+
|
357
|
+
result, lower_bound, upper_bound = check_bounds_and_buffer_size(bit_offset, bit_size, buffer.length, endianness, data_type)
|
358
|
+
raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size) if !result && (given_bit_size > 0)
|
359
|
+
|
360
|
+
# Check overflow type
|
361
|
+
if (overflow != :TRUNCATE) && (overflow != :SATURATE) && (overflow != :ERROR) && (overflow != :ERROR_ALLOW_HEX)
|
362
|
+
raise(ArgumentError, "unknown overflow type #{overflow}")
|
363
|
+
end
|
364
|
+
|
365
|
+
if (data_type == :STRING) || (data_type == :BLOCK)
|
366
|
+
#######################################
|
367
|
+
# Handle :STRING and :BLOCK data types
|
368
|
+
#######################################
|
369
|
+
value = value.to_s
|
370
|
+
|
371
|
+
if byte_aligned(bit_offset)
|
372
|
+
temp = value
|
373
|
+
if given_bit_size <= 0
|
374
|
+
end_bytes = -(given_bit_size / 8)
|
375
|
+
old_upper_bound = buffer.length - 1 - end_bytes
|
376
|
+
# Lower bound + end_bytes can never be more than 1 byte outside of the given buffer
|
377
|
+
if (lower_bound + end_bytes) > buffer.length
|
378
|
+
raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size)
|
379
|
+
end
|
380
|
+
|
381
|
+
if old_upper_bound < lower_bound
|
382
|
+
# String was completely empty
|
383
|
+
if end_bytes > 0
|
384
|
+
# Preserve bytes at end of buffer
|
385
|
+
buffer << "\000" * value.length
|
386
|
+
buffer[lower_bound + value.length, end_bytes] = buffer[lower_bound, end_bytes]
|
387
|
+
end
|
388
|
+
elsif bit_size == 0
|
389
|
+
# Remove entire string
|
390
|
+
buffer[lower_bound, old_upper_bound - lower_bound + 1] = ''
|
391
|
+
elsif upper_bound < old_upper_bound
|
392
|
+
# Remove extra bytes from old string
|
393
|
+
buffer[upper_bound + 1, old_upper_bound - upper_bound] = ''
|
394
|
+
elsif (upper_bound > old_upper_bound) && (end_bytes > 0)
|
395
|
+
# Preserve bytes at end of buffer
|
396
|
+
diff = upper_bound - old_upper_bound
|
397
|
+
buffer << "\000" * diff
|
398
|
+
buffer[upper_bound + 1, end_bytes] = buffer[old_upper_bound + 1, end_bytes]
|
399
|
+
end
|
400
|
+
else # given_bit_size > 0
|
401
|
+
byte_size = bit_size / 8
|
402
|
+
if value.length < byte_size
|
403
|
+
# Pad the requested size with zeros
|
404
|
+
temp = value.ljust(byte_size, "\000")
|
405
|
+
elsif value.length > byte_size
|
406
|
+
if overflow == :TRUNCATE
|
407
|
+
# Resize the value to fit the field
|
408
|
+
value[byte_size, value.length - byte_size] = ''
|
409
|
+
else
|
410
|
+
raise(ArgumentError, "value of #{value.length} bytes does not fit into #{byte_size} bytes for data_type #{data_type}")
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
if bit_size != 0
|
415
|
+
buffer[lower_bound, temp.length] = temp
|
416
|
+
end
|
417
|
+
else
|
418
|
+
raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
|
419
|
+
end
|
420
|
+
|
421
|
+
elsif (data_type == :INT) || (data_type == :UINT)
|
422
|
+
###################################
|
423
|
+
# Handle :INT data type
|
424
|
+
###################################
|
425
|
+
value = Integer(value)
|
426
|
+
min_value, max_value, hex_max_value = get_check_overflow_ranges(bit_size, data_type)
|
427
|
+
value = check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
|
428
|
+
|
429
|
+
if byte_aligned(bit_offset) && even_bit_size(bit_size)
|
430
|
+
###########################################################
|
431
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit
|
432
|
+
###########################################################
|
433
|
+
|
434
|
+
if data_type == :INT
|
435
|
+
###########################################################
|
436
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :INT
|
437
|
+
###########################################################
|
438
|
+
|
439
|
+
case bit_size
|
440
|
+
when 8
|
441
|
+
buffer.setbyte(lower_bound, value)
|
442
|
+
when 16
|
443
|
+
if endianness == HOST_ENDIANNESS
|
444
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_16_BIT_INT)
|
445
|
+
else # endianness != HOST_ENDIANNESS
|
446
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_16_BIT_INT).reverse
|
447
|
+
end
|
448
|
+
when 32
|
449
|
+
if endianness == HOST_ENDIANNESS
|
450
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_32_BIT_INT)
|
451
|
+
else # endianness != HOST_ENDIANNESS
|
452
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_32_BIT_INT).reverse
|
453
|
+
end
|
454
|
+
when 64
|
455
|
+
if endianness == HOST_ENDIANNESS
|
456
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_INT)
|
457
|
+
else # endianness != HOST_ENDIANNESS
|
458
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_INT).reverse
|
459
|
+
end
|
460
|
+
end
|
461
|
+
else # data_type == :UINT
|
462
|
+
###########################################################
|
463
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :UINT
|
464
|
+
###########################################################
|
465
|
+
|
466
|
+
case bit_size
|
467
|
+
when 8
|
468
|
+
buffer.setbyte(lower_bound, value)
|
469
|
+
when 16
|
470
|
+
if endianness == :BIG_ENDIAN
|
471
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_16_BIT_UINT)
|
472
|
+
else # endianness == :LITTLE_ENDIAN
|
473
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_16_BIT_UINT)
|
474
|
+
end
|
475
|
+
when 32
|
476
|
+
if endianness == :BIG_ENDIAN
|
477
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_32_BIT_UINT)
|
478
|
+
else # endianness == :LITTLE_ENDIAN
|
479
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_32_BIT_UINT)
|
480
|
+
end
|
481
|
+
when 64
|
482
|
+
if endianness == HOST_ENDIANNESS
|
483
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_UINT)
|
484
|
+
else # endianness != HOST_ENDIANNESS
|
485
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_NATIVE_64_BIT_UINT).reverse
|
486
|
+
end
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
else
|
491
|
+
###########################################################
|
492
|
+
# Handle bit fields
|
493
|
+
###########################################################
|
494
|
+
|
495
|
+
# Extract Existing Data
|
496
|
+
if endianness == :LITTLE_ENDIAN
|
497
|
+
# Bitoffset always refers to the most significant bit of a bitfield
|
498
|
+
num_bytes = (((bit_offset % 8) + bit_size - 1) / 8) + 1
|
499
|
+
upper_bound = bit_offset / 8
|
500
|
+
lower_bound = upper_bound - num_bytes + 1
|
501
|
+
if lower_bound < 0
|
502
|
+
raise(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset #{given_bit_offset} and bit_size #{given_bit_size} is invalid")
|
503
|
+
end
|
504
|
+
|
505
|
+
temp_data = buffer[lower_bound..upper_bound].reverse
|
506
|
+
else
|
507
|
+
temp_data = buffer[lower_bound..upper_bound]
|
508
|
+
end
|
509
|
+
|
510
|
+
# Determine temp upper bound
|
511
|
+
temp_upper = upper_bound - lower_bound
|
512
|
+
|
513
|
+
# Determine Values needed to Handle Bitfield
|
514
|
+
start_bits = bit_offset % 8
|
515
|
+
start_mask = (0xFF << (8 - start_bits))
|
516
|
+
total_bits = (temp_upper + 1) * 8
|
517
|
+
end_bits = total_bits - start_bits - bit_size
|
518
|
+
end_mask = ~(0xFF << end_bits)
|
519
|
+
|
520
|
+
# Add in Start Bits
|
521
|
+
temp = temp_data.getbyte(0) & start_mask
|
522
|
+
|
523
|
+
# Adjust value to correct number of bits
|
524
|
+
temp_mask = (2**bit_size) - 1
|
525
|
+
temp_value = value & temp_mask
|
526
|
+
|
527
|
+
# Add in New Data
|
528
|
+
temp = (temp << (bit_size - (8 - start_bits))) + temp_value
|
529
|
+
|
530
|
+
# Add in Remainder of Existing Data
|
531
|
+
temp = (temp << end_bits) + (temp_data.getbyte(temp_upper) & end_mask)
|
532
|
+
|
533
|
+
# Extract into an array of bytes
|
534
|
+
temp_array = []
|
535
|
+
(0..temp_upper).each { temp_array.insert(0, (temp & 0xFF)); temp = temp >> 8 }
|
536
|
+
|
537
|
+
# Store into data
|
538
|
+
if endianness == :LITTLE_ENDIAN
|
539
|
+
buffer[lower_bound..upper_bound] = temp_array.pack(PACK_8_BIT_UINT_ARRAY).reverse
|
540
|
+
else
|
541
|
+
buffer[lower_bound..upper_bound] = temp_array.pack(PACK_8_BIT_UINT_ARRAY)
|
542
|
+
end
|
543
|
+
|
544
|
+
end
|
545
|
+
|
546
|
+
elsif data_type == :FLOAT
|
547
|
+
##########################
|
548
|
+
# Handle :FLOAT data type
|
549
|
+
##########################
|
550
|
+
value = Float(value)
|
551
|
+
|
552
|
+
if byte_aligned(bit_offset)
|
553
|
+
case bit_size
|
554
|
+
when 32
|
555
|
+
if endianness == :BIG_ENDIAN
|
556
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_32_BIT_FLOAT)
|
557
|
+
else # endianness == :LITTLE_ENDIAN
|
558
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT)
|
559
|
+
end
|
560
|
+
when 64
|
561
|
+
if endianness == :BIG_ENDIAN
|
562
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_BIG_ENDIAN_64_BIT_FLOAT)
|
563
|
+
else # endianness == :LITTLE_ENDIAN
|
564
|
+
buffer[lower_bound..upper_bound] = [value].pack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT)
|
565
|
+
end
|
566
|
+
else
|
567
|
+
raise(ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}")
|
568
|
+
end
|
569
|
+
else
|
570
|
+
raise(ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}")
|
571
|
+
end
|
572
|
+
|
573
|
+
else
|
574
|
+
############################
|
575
|
+
# Handle Unknown data types
|
576
|
+
############################
|
577
|
+
|
578
|
+
raise(ArgumentError, "data_type #{data_type} is not recognized")
|
579
|
+
end
|
580
|
+
|
581
|
+
return value
|
582
|
+
end
|
583
|
+
|
584
|
+
protected
|
585
|
+
|
586
|
+
# Check the bit size and bit offset for problems. Recalulate the bit offset
|
587
|
+
# and return back through the passed in pointer.
|
588
|
+
def self.check_bit_offset_and_size(read_or_write, given_bit_offset, given_bit_size, data_type, buffer)
|
589
|
+
bit_offset = given_bit_offset
|
590
|
+
|
591
|
+
if (given_bit_size <= 0) && (data_type != :STRING) && (data_type != :BLOCK)
|
592
|
+
raise(ArgumentError, "bit_size #{given_bit_size} must be positive for data types other than :STRING and :BLOCK")
|
593
|
+
end
|
594
|
+
|
595
|
+
if (given_bit_size <= 0) && (given_bit_offset < 0)
|
596
|
+
raise(ArgumentError, "negative or zero bit_sizes (#{given_bit_size}) cannot be given with negative bit_offsets (#{given_bit_offset})")
|
597
|
+
end
|
598
|
+
|
599
|
+
if given_bit_offset < 0
|
600
|
+
bit_offset = (buffer.length * 8) + bit_offset
|
601
|
+
if bit_offset < 0
|
602
|
+
raise_buffer_error(read_or_write, buffer, data_type, given_bit_offset, given_bit_size)
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
return bit_offset
|
607
|
+
end
|
608
|
+
|
609
|
+
# Calculate the bounds of the string to access the item based on the bit_offset and bit_size.
|
610
|
+
# Also determine if the buffer size is sufficient.
|
611
|
+
def self.check_bounds_and_buffer_size(bit_offset, bit_size, buffer_length, endianness, data_type)
|
612
|
+
result = true # Assume ok
|
613
|
+
|
614
|
+
# Define bounds of string to access this item
|
615
|
+
lower_bound = bit_offset / 8
|
616
|
+
upper_bound = (bit_offset + bit_size - 1) / 8
|
617
|
+
|
618
|
+
# Sanity check buffer size
|
619
|
+
if upper_bound >= buffer_length
|
620
|
+
# If it's not the special case of little endian bit field then we fail and return false
|
621
|
+
if !((endianness == :LITTLE_ENDIAN) &&
|
622
|
+
((data_type == :INT) || (data_type == :UINT)) &&
|
623
|
+
# Not byte aligned with an even bit size
|
624
|
+
(!((byte_aligned(bit_offset)) && (even_bit_size(bit_size)))) &&
|
625
|
+
(lower_bound < buffer_length)
|
626
|
+
)
|
627
|
+
result = false
|
628
|
+
end
|
629
|
+
end
|
630
|
+
return result, lower_bound, upper_bound
|
631
|
+
end
|
632
|
+
|
633
|
+
def self.get_check_overflow_ranges(bit_size, data_type)
|
634
|
+
min_value = 0 # Default for UINT cases
|
635
|
+
|
636
|
+
case bit_size
|
637
|
+
when 8
|
638
|
+
hex_max_value = MAX_UINT8
|
639
|
+
if data_type == :INT
|
640
|
+
min_value = MIN_INT8
|
641
|
+
max_value = MAX_INT8
|
642
|
+
else
|
643
|
+
max_value = MAX_UINT8
|
644
|
+
end
|
645
|
+
when 16
|
646
|
+
hex_max_value = MAX_UINT16
|
647
|
+
if data_type == :INT
|
648
|
+
min_value = MIN_INT16
|
649
|
+
max_value = MAX_INT16
|
650
|
+
else
|
651
|
+
max_value = MAX_UINT16
|
652
|
+
end
|
653
|
+
when 32
|
654
|
+
hex_max_value = MAX_UINT32
|
655
|
+
if data_type == :INT
|
656
|
+
min_value = MIN_INT32
|
657
|
+
max_value = MAX_INT32
|
658
|
+
else
|
659
|
+
max_value = MAX_UINT32
|
660
|
+
end
|
661
|
+
when 64
|
662
|
+
hex_max_value = MAX_UINT64
|
663
|
+
if data_type == :INT
|
664
|
+
min_value = MIN_INT64
|
665
|
+
max_value = MAX_INT64
|
666
|
+
else
|
667
|
+
max_value = MAX_UINT64
|
668
|
+
end
|
669
|
+
else # Bitfield
|
670
|
+
if data_type == :INT
|
671
|
+
# Note signed integers must allow up to the maximum unsigned value to support values given in hex
|
672
|
+
if bit_size > 1
|
673
|
+
max_value = 2**(bit_size - 1)
|
674
|
+
# min_value = -(2 ** bit_size - 1)
|
675
|
+
min_value = -max_value
|
676
|
+
# max_value = (2 ** bit_size - 1) - 1
|
677
|
+
max_value -= 1
|
678
|
+
# hex_max_value = (2 ** bit_size) - 1
|
679
|
+
hex_max_value = (2**bit_size) - 1
|
680
|
+
else # 1-bit signed
|
681
|
+
min_value = -1
|
682
|
+
max_value = 1
|
683
|
+
hex_max_value = 1
|
684
|
+
end
|
685
|
+
else
|
686
|
+
max_value = (2**bit_size) - 1
|
687
|
+
hex_max_value = max_value
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
return min_value, max_value, hex_max_value
|
692
|
+
end
|
693
|
+
|
694
|
+
def self.byte_aligned(value)
|
695
|
+
(value % 8) == 0
|
696
|
+
end
|
697
|
+
|
698
|
+
def self.even_bit_size(bit_size)
|
699
|
+
(bit_size == 8) || (bit_size == 16) || (bit_size == 32) || (bit_size == 64)
|
700
|
+
end
|
701
|
+
|
702
|
+
public
|
703
|
+
|
704
|
+
end
|
705
|
+
|
706
|
+
# Reads an array of binary data of any data type from a buffer
|
707
|
+
#
|
708
|
+
# @param bit_offset [Integer] Bit offset to the start of the array. A
|
709
|
+
# negative number means to offset from the end of the buffer.
|
710
|
+
# @param bit_size [Integer] Size of each item in the array in bits
|
711
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
712
|
+
# @param array_size [Integer] Size in bits of the array. 0 or negative means
|
713
|
+
# fill the array with as many bit_size number of items that exist (negative
|
714
|
+
# means excluding the final X number of bits).
|
715
|
+
# @param buffer [String] Binary string buffer to read from
|
716
|
+
# @param endianness [Symbol] {ENDIANNESS}
|
717
|
+
# @return [Array] Array created from reading the buffer
|
718
|
+
def self.read_array(bit_offset, bit_size, data_type, array_size, buffer, endianness)
|
719
|
+
# Save given values of bit offset, bit size, and array_size
|
720
|
+
given_bit_offset = bit_offset
|
721
|
+
given_bit_size = bit_size
|
722
|
+
given_array_size = array_size
|
723
|
+
|
724
|
+
# Handle negative and zero bit sizes
|
725
|
+
raise ArgumentError, "bit_size #{given_bit_size} must be positive for arrays" if bit_size <= 0
|
726
|
+
|
727
|
+
# Handle negative bit offsets
|
728
|
+
if bit_offset < 0
|
729
|
+
bit_offset = ((buffer.length * 8) + bit_offset)
|
730
|
+
raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size) if bit_offset < 0
|
731
|
+
end
|
732
|
+
|
733
|
+
# Handle negative and zero array sizes
|
734
|
+
if array_size <= 0
|
735
|
+
if given_bit_offset < 0
|
736
|
+
raise ArgumentError, "negative or zero array_size (#{given_array_size}) cannot be given with negative bit_offset (#{given_bit_offset})"
|
737
|
+
else
|
738
|
+
array_size = ((buffer.length * 8) - bit_offset + array_size)
|
739
|
+
if array_size == 0
|
740
|
+
return []
|
741
|
+
elsif array_size < 0
|
742
|
+
raise_buffer_error(:read, buffer, data_type, given_bit_offset, given_bit_size)
|
743
|
+
end
|
744
|
+
end
|
745
|
+
end
|
746
|
+
|
747
|
+
# Calculate number of items in the array
|
748
|
+
# If there is a remainder then we have a problem
|
749
|
+
raise ArgumentError, "array_size #{given_array_size} not a multiple of bit_size #{given_bit_size}" if array_size % bit_size != 0
|
750
|
+
|
751
|
+
num_items = array_size / bit_size
|
752
|
+
|
753
|
+
# Define bounds of string to access this item
|
754
|
+
lower_bound = bit_offset / 8
|
755
|
+
upper_bound = (bit_offset + array_size - 1) / 8
|
756
|
+
|
757
|
+
# Check for byte alignment
|
758
|
+
byte_aligned = ((bit_offset % 8) == 0)
|
759
|
+
|
760
|
+
case data_type
|
761
|
+
when :STRING, :BLOCK
|
762
|
+
#######################################
|
763
|
+
# Handle :STRING and :BLOCK data types
|
764
|
+
#######################################
|
765
|
+
|
766
|
+
if byte_aligned
|
767
|
+
value = []
|
768
|
+
num_items.times do
|
769
|
+
value << self.read(bit_offset, bit_size, data_type, buffer, endianness)
|
770
|
+
bit_offset += bit_size
|
771
|
+
end
|
772
|
+
else
|
773
|
+
raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
|
774
|
+
end
|
775
|
+
|
776
|
+
when :INT, :UINT
|
777
|
+
###################################
|
778
|
+
# Handle :INT and :UINT data types
|
779
|
+
###################################
|
780
|
+
|
781
|
+
if byte_aligned and (bit_size == 8 or bit_size == 16 or bit_size == 32 or bit_size == 64)
|
782
|
+
###########################################################
|
783
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :INT and :UINT
|
784
|
+
###########################################################
|
785
|
+
|
786
|
+
case bit_size
|
787
|
+
when 8
|
788
|
+
if data_type == :INT
|
789
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_8_BIT_INT_ARRAY)
|
790
|
+
else # data_type == :UINT
|
791
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_8_BIT_UINT_ARRAY)
|
792
|
+
end
|
793
|
+
|
794
|
+
when 16
|
795
|
+
if data_type == :INT
|
796
|
+
if endianness == HOST_ENDIANNESS
|
797
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_16_BIT_INT_ARRAY)
|
798
|
+
else # endianness != HOST_ENDIANNESS
|
799
|
+
temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 2)
|
800
|
+
value = temp.to_s.unpack(PACK_NATIVE_16_BIT_INT_ARRAY)
|
801
|
+
end
|
802
|
+
else # data_type == :UINT
|
803
|
+
if endianness == :BIG_ENDIAN
|
804
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY)
|
805
|
+
else # endianness == :LITTLE_ENDIAN
|
806
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY)
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
when 32
|
811
|
+
if data_type == :INT
|
812
|
+
if endianness == HOST_ENDIANNESS
|
813
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_32_BIT_INT_ARRAY)
|
814
|
+
else # endianness != HOST_ENDIANNESS
|
815
|
+
temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 4)
|
816
|
+
value = temp.to_s.unpack(PACK_NATIVE_32_BIT_INT_ARRAY)
|
817
|
+
end
|
818
|
+
else # data_type == :UINT
|
819
|
+
if endianness == :BIG_ENDIAN
|
820
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY)
|
821
|
+
else # endianness == :LITTLE_ENDIAN
|
822
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY)
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
when 64
|
827
|
+
if data_type == :INT
|
828
|
+
if endianness == HOST_ENDIANNESS
|
829
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_INT_ARRAY)
|
830
|
+
else # endianness != HOST_ENDIANNESS
|
831
|
+
temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 8)
|
832
|
+
value = temp.to_s.unpack(PACK_NATIVE_64_BIT_INT_ARRAY)
|
833
|
+
end
|
834
|
+
else # data_type == :UINT
|
835
|
+
if endianness == HOST_ENDIANNESS
|
836
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_NATIVE_64_BIT_UINT_ARRAY)
|
837
|
+
else # endianness != HOST_ENDIANNESS
|
838
|
+
temp = self.byte_swap_buffer(buffer[lower_bound..upper_bound], 8)
|
839
|
+
value = temp.to_s.unpack(PACK_NATIVE_64_BIT_UINT_ARRAY)
|
840
|
+
end
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
else
|
845
|
+
##################################
|
846
|
+
# Handle :INT and :UINT Bitfields
|
847
|
+
##################################
|
848
|
+
raise ArgumentError, "read_array does not support little endian bit fields with bit_size greater than 1-bit" if endianness == :LITTLE_ENDIAN and bit_size > 1
|
849
|
+
|
850
|
+
value = []
|
851
|
+
num_items.times do
|
852
|
+
value << self.read(bit_offset, bit_size, data_type, buffer, endianness)
|
853
|
+
bit_offset += bit_size
|
854
|
+
end
|
855
|
+
end
|
856
|
+
|
857
|
+
when :FLOAT
|
858
|
+
##########################
|
859
|
+
# Handle :FLOAT data type
|
860
|
+
##########################
|
861
|
+
|
862
|
+
if byte_aligned
|
863
|
+
case bit_size
|
864
|
+
when 32
|
865
|
+
if endianness == :BIG_ENDIAN
|
866
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY)
|
867
|
+
else # endianness == :LITTLE_ENDIAN
|
868
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY)
|
869
|
+
end
|
870
|
+
|
871
|
+
when 64
|
872
|
+
if endianness == :BIG_ENDIAN
|
873
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY)
|
874
|
+
else # endianness == :LITTLE_ENDIAN
|
875
|
+
value = buffer[lower_bound..upper_bound].unpack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY)
|
876
|
+
end
|
877
|
+
|
878
|
+
else
|
879
|
+
raise ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}"
|
880
|
+
end
|
881
|
+
|
882
|
+
else
|
883
|
+
raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
|
884
|
+
end
|
885
|
+
|
886
|
+
else
|
887
|
+
############################
|
888
|
+
# Handle Unknown data types
|
889
|
+
############################
|
890
|
+
|
891
|
+
raise ArgumentError, "data_type #{data_type} is not recognized"
|
892
|
+
end
|
893
|
+
|
894
|
+
value
|
895
|
+
end # def read_array
|
896
|
+
|
897
|
+
# Writes an array of binary data of any data type to a buffer
|
898
|
+
#
|
899
|
+
# @param values [Array] Values to write into the buffer
|
900
|
+
# @param bit_offset [Integer] Bit offset to the start of the array. A
|
901
|
+
# negative number means to offset from the end of the buffer.
|
902
|
+
# @param bit_size [Integer] Size of each item in the array in bits
|
903
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
904
|
+
# @param array_size [Integer] Size in bits of the array as represented in the buffer.
|
905
|
+
# Size 0 means to fill the buffer with as many bit_size number of items that exist
|
906
|
+
# (negative means excluding the final X number of bits).
|
907
|
+
# @param buffer [String] Binary string buffer to write to
|
908
|
+
# @param endianness [Symbol] {ENDIANNESS}
|
909
|
+
# @return [Array] values passed in as a parameter
|
910
|
+
def self.write_array(values, bit_offset, bit_size, data_type, array_size, buffer, endianness, overflow)
|
911
|
+
# Save given values of bit offset, bit size, and array_size
|
912
|
+
given_bit_offset = bit_offset
|
913
|
+
given_bit_size = bit_size
|
914
|
+
given_array_size = array_size
|
915
|
+
|
916
|
+
# Verify an array was given
|
917
|
+
raise ArgumentError, "values must be an Array type class is #{values.class}" unless values.kind_of? Array
|
918
|
+
|
919
|
+
# Handle negative and zero bit sizes
|
920
|
+
raise ArgumentError, "bit_size #{given_bit_size} must be positive for arrays" if bit_size <= 0
|
921
|
+
|
922
|
+
# Handle negative bit offsets
|
923
|
+
if bit_offset < 0
|
924
|
+
bit_offset = ((buffer.length * 8) + bit_offset)
|
925
|
+
raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size) if bit_offset < 0
|
926
|
+
end
|
927
|
+
|
928
|
+
# Handle negative and zero array sizes
|
929
|
+
if array_size <= 0
|
930
|
+
if given_bit_offset < 0
|
931
|
+
raise ArgumentError, "negative or zero array_size (#{given_array_size}) cannot be given with negative bit_offset (#{given_bit_offset})"
|
932
|
+
else
|
933
|
+
end_bytes = -(given_array_size / 8)
|
934
|
+
lower_bound = bit_offset / 8
|
935
|
+
upper_bound = (bit_offset + (bit_size * values.length) - 1) / 8
|
936
|
+
old_upper_bound = buffer.length - 1 - end_bytes
|
937
|
+
|
938
|
+
if upper_bound < old_upper_bound
|
939
|
+
# Remove extra bytes from old buffer
|
940
|
+
buffer[(upper_bound + 1)..old_upper_bound] = ''
|
941
|
+
elsif upper_bound > old_upper_bound
|
942
|
+
# Grow buffer and preserve bytes at end of buffer if necesssary
|
943
|
+
buffer_length = buffer.length
|
944
|
+
diff = upper_bound - old_upper_bound
|
945
|
+
buffer << ZERO_STRING * diff
|
946
|
+
if end_bytes > 0
|
947
|
+
buffer[(upper_bound + 1)..(buffer.length - 1)] = buffer[(old_upper_bound + 1)..(buffer_length - 1)]
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
951
|
+
array_size = ((buffer.length * 8) - bit_offset + array_size)
|
952
|
+
end
|
953
|
+
end
|
954
|
+
|
955
|
+
# Get data bounds for this array
|
956
|
+
lower_bound = bit_offset / 8
|
957
|
+
upper_bound = (bit_offset + array_size - 1) / 8
|
958
|
+
num_bytes = upper_bound - lower_bound + 1
|
959
|
+
|
960
|
+
# Check for byte alignment
|
961
|
+
byte_aligned = ((bit_offset % 8) == 0)
|
962
|
+
|
963
|
+
# Calculate the number of writes
|
964
|
+
num_writes = array_size / bit_size
|
965
|
+
# Check for a negative array_size and adjust the number of writes
|
966
|
+
# to simply be the number of values in the passed in array
|
967
|
+
if given_array_size <= 0
|
968
|
+
num_writes = values.length
|
969
|
+
end
|
970
|
+
|
971
|
+
# Ensure the buffer has enough room
|
972
|
+
if bit_offset + num_writes * bit_size > buffer.length * 8
|
973
|
+
raise_buffer_error(:write, buffer, data_type, given_bit_offset, given_bit_size)
|
974
|
+
end
|
975
|
+
|
976
|
+
# Ensure the given_array_size is an even multiple of bit_size
|
977
|
+
raise ArgumentError, "array_size #{given_array_size} not a multiple of bit_size #{given_bit_size}" if array_size % bit_size != 0
|
978
|
+
|
979
|
+
raise ArgumentError, "too many values #{values.length} for given array_size #{given_array_size} and bit_size #{given_bit_size}" if num_writes < values.length
|
980
|
+
|
981
|
+
# Check overflow type
|
982
|
+
raise "unknown overflow type #{overflow}" unless OVERFLOW_TYPES.include?(overflow)
|
983
|
+
|
984
|
+
case data_type
|
985
|
+
when :STRING, :BLOCK
|
986
|
+
#######################################
|
987
|
+
# Handle :STRING and :BLOCK data types
|
988
|
+
#######################################
|
989
|
+
|
990
|
+
if byte_aligned
|
991
|
+
num_writes.times do |index|
|
992
|
+
self.write(values[index], bit_offset, bit_size, data_type, buffer, endianness, overflow)
|
993
|
+
bit_offset += bit_size
|
994
|
+
end
|
995
|
+
else
|
996
|
+
raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
|
997
|
+
end
|
998
|
+
|
999
|
+
when :INT, :UINT
|
1000
|
+
###################################
|
1001
|
+
# Handle :INT and :UINT data types
|
1002
|
+
###################################
|
1003
|
+
|
1004
|
+
if byte_aligned and (bit_size == 8 or bit_size == 16 or bit_size == 32 or bit_size == 64)
|
1005
|
+
###########################################################
|
1006
|
+
# Handle byte-aligned 8, 16, 32, and 64 bit :INT and :UINT
|
1007
|
+
###########################################################
|
1008
|
+
|
1009
|
+
case bit_size
|
1010
|
+
when 8
|
1011
|
+
if data_type == :INT
|
1012
|
+
values = self.check_overflow_array(values, MIN_INT8, MAX_INT8, MAX_UINT8, bit_size, data_type, overflow)
|
1013
|
+
packed = values.pack(PACK_8_BIT_INT_ARRAY)
|
1014
|
+
else # data_type == :UINT
|
1015
|
+
values = self.check_overflow_array(values, 0, MAX_UINT8, MAX_UINT8, bit_size, data_type, overflow)
|
1016
|
+
packed = values.pack(PACK_8_BIT_UINT_ARRAY)
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
when 16
|
1020
|
+
if data_type == :INT
|
1021
|
+
values = self.check_overflow_array(values, MIN_INT16, MAX_INT16, MAX_UINT16, bit_size, data_type, overflow)
|
1022
|
+
if endianness == HOST_ENDIANNESS
|
1023
|
+
packed = values.pack(PACK_NATIVE_16_BIT_INT_ARRAY)
|
1024
|
+
else # endianness != HOST_ENDIANNESS
|
1025
|
+
packed = values.pack(PACK_NATIVE_16_BIT_INT_ARRAY)
|
1026
|
+
self.byte_swap_buffer!(packed, 2)
|
1027
|
+
end
|
1028
|
+
else # data_type == :UINT
|
1029
|
+
values = self.check_overflow_array(values, 0, MAX_UINT16, MAX_UINT16, bit_size, data_type, overflow)
|
1030
|
+
if endianness == :BIG_ENDIAN
|
1031
|
+
packed = values.pack(PACK_BIG_ENDIAN_16_BIT_UINT_ARRAY)
|
1032
|
+
else # endianness == :LITTLE_ENDIAN
|
1033
|
+
packed = values.pack(PACK_LITTLE_ENDIAN_16_BIT_UINT_ARRAY)
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
when 32
|
1038
|
+
if data_type == :INT
|
1039
|
+
values = self.check_overflow_array(values, MIN_INT32, MAX_INT32, MAX_UINT32, bit_size, data_type, overflow)
|
1040
|
+
if endianness == HOST_ENDIANNESS
|
1041
|
+
packed = values.pack(PACK_NATIVE_32_BIT_INT_ARRAY)
|
1042
|
+
else # endianness != HOST_ENDIANNESS
|
1043
|
+
packed = values.pack(PACK_NATIVE_32_BIT_INT_ARRAY)
|
1044
|
+
self.byte_swap_buffer!(packed, 4)
|
1045
|
+
end
|
1046
|
+
else # data_type == :UINT
|
1047
|
+
values = self.check_overflow_array(values, 0, MAX_UINT32, MAX_UINT32, bit_size, data_type, overflow)
|
1048
|
+
if endianness == :BIG_ENDIAN
|
1049
|
+
packed = values.pack(PACK_BIG_ENDIAN_32_BIT_UINT_ARRAY)
|
1050
|
+
else # endianness == :LITTLE_ENDIAN
|
1051
|
+
packed = values.pack(PACK_LITTLE_ENDIAN_32_BIT_UINT_ARRAY)
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
when 64
|
1056
|
+
if data_type == :INT
|
1057
|
+
values = self.check_overflow_array(values, MIN_INT64, MAX_INT64, MAX_UINT64, bit_size, data_type, overflow)
|
1058
|
+
if endianness == HOST_ENDIANNESS
|
1059
|
+
packed = values.pack(PACK_NATIVE_64_BIT_INT_ARRAY)
|
1060
|
+
else # endianness != HOST_ENDIANNESS
|
1061
|
+
packed = values.pack(PACK_NATIVE_64_BIT_INT_ARRAY)
|
1062
|
+
self.byte_swap_buffer!(packed, 8)
|
1063
|
+
end
|
1064
|
+
else # data_type == :UINT
|
1065
|
+
values = self.check_overflow_array(values, 0, MAX_UINT64, MAX_UINT64, bit_size, data_type, overflow)
|
1066
|
+
if endianness == HOST_ENDIANNESS
|
1067
|
+
packed = values.pack(PACK_NATIVE_64_BIT_UINT_ARRAY)
|
1068
|
+
else # endianness != HOST_ENDIANNESS
|
1069
|
+
packed = values.pack(PACK_NATIVE_64_BIT_UINT_ARRAY)
|
1070
|
+
self.byte_swap_buffer!(packed, 8)
|
1071
|
+
end
|
1072
|
+
end
|
1073
|
+
end
|
1074
|
+
|
1075
|
+
# Adjust packed size to hold number of items written
|
1076
|
+
buffer[lower_bound..upper_bound] = adjust_packed_size(num_bytes, packed) if num_bytes > 0
|
1077
|
+
|
1078
|
+
else
|
1079
|
+
##################################
|
1080
|
+
# Handle :INT and :UINT Bitfields
|
1081
|
+
##################################
|
1082
|
+
|
1083
|
+
raise ArgumentError, "write_array does not support little endian bit fields with bit_size greater than 1-bit" if endianness == :LITTLE_ENDIAN and bit_size > 1
|
1084
|
+
|
1085
|
+
num_writes.times do |index|
|
1086
|
+
self.write(values[index], bit_offset, bit_size, data_type, buffer, endianness, overflow)
|
1087
|
+
bit_offset += bit_size
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
when :FLOAT
|
1092
|
+
##########################
|
1093
|
+
# Handle :FLOAT data type
|
1094
|
+
##########################
|
1095
|
+
|
1096
|
+
if byte_aligned
|
1097
|
+
case bit_size
|
1098
|
+
when 32
|
1099
|
+
if endianness == :BIG_ENDIAN
|
1100
|
+
packed = values.pack(PACK_BIG_ENDIAN_32_BIT_FLOAT_ARRAY)
|
1101
|
+
else # endianness == :LITTLE_ENDIAN
|
1102
|
+
packed = values.pack(PACK_LITTLE_ENDIAN_32_BIT_FLOAT_ARRAY)
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
when 64
|
1106
|
+
if endianness == :BIG_ENDIAN
|
1107
|
+
packed = values.pack(PACK_BIG_ENDIAN_64_BIT_FLOAT_ARRAY)
|
1108
|
+
else # endianness == :LITTLE_ENDIAN
|
1109
|
+
packed = values.pack(PACK_LITTLE_ENDIAN_64_BIT_FLOAT_ARRAY)
|
1110
|
+
end
|
1111
|
+
|
1112
|
+
else
|
1113
|
+
raise ArgumentError, "bit_size is #{given_bit_size} but must be 32 or 64 for data_type #{data_type}"
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
# Adjust packed size to hold number of items written
|
1117
|
+
buffer[lower_bound..upper_bound] = adjust_packed_size(num_bytes, packed) if num_bytes > 0
|
1118
|
+
|
1119
|
+
else
|
1120
|
+
raise ArgumentError, "bit_offset #{given_bit_offset} is not byte aligned for data_type #{data_type}"
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
else
|
1124
|
+
############################
|
1125
|
+
# Handle Unknown data types
|
1126
|
+
############################
|
1127
|
+
raise ArgumentError, "data_type #{data_type} is not recognized"
|
1128
|
+
end # case data_type
|
1129
|
+
|
1130
|
+
values
|
1131
|
+
end # def write_array
|
1132
|
+
|
1133
|
+
# Adjusts the packed array to be the given number of bytes
|
1134
|
+
#
|
1135
|
+
# @param num_bytes [Integer] The desired number of bytes
|
1136
|
+
# @param packed [Array] The packed data buffer
|
1137
|
+
def self.adjust_packed_size(num_bytes, packed)
|
1138
|
+
difference = num_bytes - packed.length
|
1139
|
+
if difference > 0
|
1140
|
+
packed << (ZERO_STRING * difference)
|
1141
|
+
elsif difference < 0
|
1142
|
+
packed = packed[0..(packed.length - 1 + difference)]
|
1143
|
+
end
|
1144
|
+
packed
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
# Byte swaps every X bytes of data in a buffer overwriting the buffer
|
1148
|
+
#
|
1149
|
+
# @param buffer [String] Buffer to modify
|
1150
|
+
# @param num_bytes_per_word [Integer] Number of bytes per word that will be swapped
|
1151
|
+
# @return [String] buffer passed in as a parameter
|
1152
|
+
def self.byte_swap_buffer!(buffer, num_bytes_per_word)
|
1153
|
+
num_swaps = buffer.length / num_bytes_per_word
|
1154
|
+
index = 0
|
1155
|
+
num_swaps.times do
|
1156
|
+
range = index..(index + num_bytes_per_word - 1)
|
1157
|
+
buffer[range] = buffer[range].reverse
|
1158
|
+
index += num_bytes_per_word
|
1159
|
+
end
|
1160
|
+
buffer
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
# Byte swaps every X bytes of data in a buffer into a new buffer
|
1164
|
+
#
|
1165
|
+
# @param buffer [String] Buffer that will be copied then modified
|
1166
|
+
# @param num_bytes_per_word [Integer] Number of bytes per word that will be swapped
|
1167
|
+
# @return [String] modified buffer
|
1168
|
+
def self.byte_swap_buffer(buffer, num_bytes_per_word)
|
1169
|
+
buffer = buffer.clone
|
1170
|
+
self.byte_swap_buffer!(buffer, num_bytes_per_word)
|
1171
|
+
end
|
1172
|
+
|
1173
|
+
# Checks for overflow of an integer data type
|
1174
|
+
#
|
1175
|
+
# @param value [Integer] Value to write into the buffer
|
1176
|
+
# @param min_value [Integer] Minimum allowed value
|
1177
|
+
# @param max_value [Integer] Maximum allowed value
|
1178
|
+
# @param hex_max_value [Integer] Maximum allowed value if specified in hex
|
1179
|
+
# @param bit_size [Integer] Size of the item in bits
|
1180
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
1181
|
+
# @param overflow [Symbol] {OVERFLOW_TYPES}
|
1182
|
+
# @return [Integer] Potentially modified value
|
1183
|
+
def self.check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
|
1184
|
+
if overflow == :TRUNCATE
|
1185
|
+
# Note this will always convert to unsigned equivalent for signed integers
|
1186
|
+
value = value % (hex_max_value + 1)
|
1187
|
+
else
|
1188
|
+
if value > max_value
|
1189
|
+
if overflow == :SATURATE
|
1190
|
+
value = max_value
|
1191
|
+
else
|
1192
|
+
if overflow == :ERROR or value > hex_max_value
|
1193
|
+
raise ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}"
|
1194
|
+
end
|
1195
|
+
end
|
1196
|
+
elsif value < min_value
|
1197
|
+
if overflow == :SATURATE
|
1198
|
+
value = min_value
|
1199
|
+
else
|
1200
|
+
raise ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}"
|
1201
|
+
end
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
value
|
1205
|
+
end
|
1206
|
+
|
1207
|
+
# Checks for overflow of an array of integer data types
|
1208
|
+
#
|
1209
|
+
# @param values [Array[Integer]] Values to write into the buffer
|
1210
|
+
# @param min_value [Integer] Minimum allowed value
|
1211
|
+
# @param max_value [Integer] Maximum allowed value
|
1212
|
+
# @param hex_max_value [Integer] Maximum allowed value if specified in hex
|
1213
|
+
# @param bit_size [Integer] Size of the item in bits
|
1214
|
+
# @param data_type [Symbol] {DATA_TYPES}
|
1215
|
+
# @param overflow [Symbol] {OVERFLOW_TYPES}
|
1216
|
+
# @return [Array[Integer]] Potentially modified values
|
1217
|
+
def self.check_overflow_array(values, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
|
1218
|
+
if overflow != :TRUNCATE
|
1219
|
+
values.each_with_index do |value, index|
|
1220
|
+
values[index] = check_overflow(value, min_value, max_value, hex_max_value, bit_size, data_type, overflow)
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
values
|
1224
|
+
end
|
1225
|
+
end # class BinaryAccessor
|
1226
|
+
end
|