# frozen_string_literal: true module Sftui # frozen_string_literal: true # Len Address (4 bytes) Data # +----+----+----+----+----+------------------------+----+----+ # | | | | | | |0xff|0xff| # +----+----+----+----+----+------------------------+----+----+ class Frame attr_accessor :bytes LENGTH_LENGTH = 1 ADDRESS_LENGTH = 4 TERMINATE_LENGTH = 2 TERMINATE_CHAR = 0xff BITWISE_MASK = 0xff TRANSACTION_ENDING_CHAR = 0x0d def initialize(bytes) @bytes = bytes end def completed? bytes.length == length && bytes[(-1 - TERMINATE_LENGTH + 1)..-1] == terminate_bytes end def machine_id return unless completed? to_integer(addr_bytes) end def +(other) # rubocop:disable Metrics/AbcSize raise unless other.completed? && other.machine_id == machine_id combined_data_bytes = data_bytes + other.data_bytes combined_length = LENGTH_LENGTH + ADDRESS_LENGTH + TERMINATE_LENGTH + combined_data_bytes.length Frame.new( to_bytes(combined_length, LENGTH_LENGTH) + addr_bytes + combined_data_bytes + terminate_bytes ) end def sanitized_data last_frame_in_transaction? ? data_bytes[0..-2].pack('c*') : data_bytes.pack('c*') end def data_bytes return [] unless completed? bytes[data_offset..(-1 - TERMINATE_LENGTH)] end def last_frame_in_transaction? data_bytes[-1] == TRANSACTION_ENDING_CHAR end def length_bytes bytes[0..LENGTH_LENGTH - 1] end def addr_bytes bytes[addr_offset..addr_offset + ADDRESS_LENGTH - 1] end def terminate_bytes Array.new(TERMINATE_LENGTH, TERMINATE_CHAR) end def display # rubocop:disable Metrics/AbcSize table = TTY::Table.new(header: %w[Length Address Data Terminate]) table << [ length_bytes.map { |byte| to_hex(byte) }.join(' '), addr_bytes.map { |byte| to_hex(byte) }.join(' '), data_bytes.map { |byte| to_hex(byte) }.join(' '), terminate_bytes.map { |byte| to_hex(byte) }.join(' ') ] table.render(:unicode, alignments: %i[center center]) end private def length to_integer(length_bytes) end def addr_offset LENGTH_LENGTH end def data_offset LENGTH_LENGTH + ADDRESS_LENGTH end def to_integer(bytes) sum = 0 index = 0 while index < bytes.length sum += bytes[index] << ((bytes.length - index - 1) * 8) index += 1 end sum end def to_bytes(integer, length) bytes = Array.new(length) index = 0 while index < length bytes[-1 - index] = (integer >> (index * 8)) & BITWISE_MASK index += 1 end bytes end def to_hex(byte) "0x#{byte.to_s(16)}" end end end