Method: OpenC3::BinaryAccessor#handle_write_variable_bit_size

Defined in:
lib/openc3/accessors/binary_accessor.rb

#handle_write_variable_bit_size(item, value, buffer) ⇒ Object



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/openc3/accessors/binary_accessor.rb', line 144

def handle_write_variable_bit_size(item, value, buffer)
  # Update length field to new size
  if (item.data_type == :INT or item.data_type == :UINT) and not item.original_array_size
    # QUIC encoding is currently assumed for individual variable sized integers
    # see https://datatracker.ietf.org/doc/html/rfc9000#name-variable-length-integer-enc

    # Calculate current bit size so we can preserve bytes after the item
    length_item_value = @packet.read(item.variable_bit_size['length_item_name'], :CONVERTED)
    case length_item_value
    when 0
      current_bit_size = 6
    when 1
      current_bit_size = 14
    when 2
      current_bit_size = 30
    when 3
      current_bit_size = 62
    else
      raise "Value #{item.variable_bit_size['length_item_name']} has unknown QUIC bit size encoding: #{length_item_value}"
    end

    if item.data_type == :UINT
      if value <= 63
        # Length = 0, value up to 6-bits
        new_bit_size = 6
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 0)
      elsif value <= 16383
        # Length = 1, value up to 14-bits
        new_bit_size = 14
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 1)
      elsif value <= 1073741823
        # Length = 2, value up to 30-bits
        new_bit_size = 30
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 2)
      else
        # Length = 3, value up to 62-bits
        new_bit_size = 62
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 3)
      end
    else
      if value <= 31 and value >= -32
        # Length = 0, value up to 6-bits
        new_bit_size = 6
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 0)
      elsif value <= 8191 and value >= -8192
        # Length = 1, value up to 14-bits
        new_bit_size = 14
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 1)
      elsif value <= 536870911 and value >= -536870912
        # Length = 2, value up to 30-bits
        new_bit_size = 30
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 2)
      else
        # Length = 3, value up to 62-bits
        new_bit_size = 62
        item.bit_size = new_bit_size
        @packet.write(item.variable_bit_size['length_item_name'], 3)
      end
    end

    # Later items need their bit_offset adjusted by the change in this item
    adjustment = new_bit_size - current_bit_size
    bytes = (adjustment / 8)
    item_offset = item.bit_offset / 8
    if bytes > 0
      original_length = buffer.length
      # Add extra bytes because we're adjusting larger
      buffer << ("\000" * bytes)
      # We added bytes to the end so now we have to shift the buffer over
      #   NOTE: buffer[offset, length]
      # We copy to the shifted offset location with the remaining buffer length
      buffer[item_offset + bytes, buffer.length - (item_offset + bytes)] =
          # We copy from the original offset location with the original length minus the offset
          buffer[item_offset, original_length - item_offset]
    elsif bytes < 0
      # Remove extra bytes because we're adjusting smaller
      buffer[item_offset + 1, -bytes] = ''
    end
  # Probably not possible to get this condition because we don't allow 0 sized floats
  # but check for it just to cover all the possible data_types
  elsif item.data_type == :FLOAT
    raise "Variable bit size not currently supported for FLOAT data type"
  else
    # STRING, BLOCK, or array types

    # Calculate current bit size so we can preserve bytes after the item
    length_item_value = @packet.read(item.variable_bit_size['length_item_name'], :CONVERTED)
    current_bit_size = (length_item_value * item.variable_bit_size['length_bits_per_count']) + item.variable_bit_size['length_value_bit_offset']

    # Calculate bits after this item
    bits_with_item = item.bit_offset + current_bit_size
    bits_after_item = (buffer.length * 8) - bits_with_item
    if item.original_array_size
      item.array_size = -bits_after_item
    else
      item.bit_size = -bits_after_item
    end

    new_bit_size = value.length * 8
    length_value = (new_bit_size - item.variable_bit_size['length_value_bit_offset']) / item.variable_bit_size['length_bits_per_count']
    @packet.write(item.variable_bit_size['length_item_name'], length_value)

    # Later items need their bit_offset adjusted by the change in this item
    adjustment = new_bit_size - current_bit_size
  end

  # Recalculate bit offsets after this item
  if adjustment != 0 and item.bit_offset >= 0
    @packet.sorted_items.each do |sitem|
      if sitem.data_type == :DERIVED or sitem.bit_offset < item.bit_offset
        # Skip items before this item and derived items and items with negative bit offsets
        next
      end
      if sitem != item
        sitem.bit_offset += adjustment
      end
    end
  end
end