# encoding: ascii-8bit

# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved
#
# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.

require 'spec_helper'
require 'openc3'
require 'openc3/accessors/binary_accessor'
require 'openc3/packets/packet'

module OpenC3
  describe BinaryAccessor, no_ext: true do
    describe "multiple variable sized items" do
      before(:each) do
        @packet = Packet.new
      end

      it "can write QUIC encoded variable sized integers" do
        @packet.append_item("marker", 16, :STRING)
        @packet.write("marker", "**")
        expect(@packet.buffer).to eql "\x2A\x2A"

        @packet.append_item("item1_length", 2, :UINT)
        item1 = @packet.append_item("item1", 0, :UINT)
        item1.variable_bit_size = {'length_item_name' => 'item1_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        # This is required to get the defined_length_bits set correctly
        # It is normally called by packet_config when constructing the packet
        @packet.set_item(item1)
        expect(@packet.buffer).to eql "\x2A\x2A\x00"

        @packet.append_item("marker", 16, :STRING)
        @packet.write("marker", "##")
        expect(@packet.buffer).to eql "\x2A\x2A\x00\x23\x23"

        @packet.append_item("item2_length", 2, :UINT)
        item2 = @packet.append_item("item2", 0, :INT)
        item2.variable_bit_size = {'length_item_name' => 'item2_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        # This is required to get the defined_length_bits set correctly
        # It is normally called by packet_config when constructing the packet
        @packet.set_item(item2)
        expect(@packet.buffer).to eql "\x2A\x2A\x00\x23\x23\x00"

        @packet.append_item("marker", 16, :STRING)
        @packet.write("marker", "$$")
        expect(@packet.buffer).to eql "\x2A\x2A\x00\x23\x23\x00\x24\x24"

        # For all these example the 2 bits indicate the length
        # 0 is 6, 1 is 14, 2 is 30, 4 is 62
        # The 2 bit length field itself makes up a full byte boundary

        @packet.write("item1", 0)
        @packet.write("item2", 0)
        expect(@packet.buffer).to eql "\x2A\x2A\x00\x23\x23\x00\x24\x24"
        expect(@packet.read("item1")).to eql(0x0)
        expect(@packet.read("item2")).to eql(0x0)

        # Loop through all the combinations of bits
        [[6, 'c', 0], [14, 's>', 0x4000], [30, 'l>', 0x80000000], [62, 'q>', 0xC000000000000000]].each do |bits, format_string, length_bits|
          min_unsigned = 0
          max_unsigned = (2**bits) - 1
          mask = max_unsigned
          min_signed = -(2**(bits-1))
          max_signed = (2**(bits-1)) - 1

          @packet.write("item1", min_unsigned)
          @packet.write("item2", min_signed)
          # We know min unsigned is just 0 so pack that
          buffer = "\x2A\x2A\x00\x23\x23"
          # Mask off the length bits then set them
          buffer += [(min_signed & mask) | length_bits].pack(format_string)
          buffer += "\x24\x24"
          expect(@packet.buffer).to eql buffer
          expect(@packet.read("item1")).to eql(min_unsigned)
          expect(@packet.read("item2")).to eql(min_signed)

          @packet.write("item1", max_unsigned)
          @packet.write("item2", max_signed)
          buffer = "\x2A\x2A"
          # Mask off the length bits then set them
          buffer += [(max_unsigned & mask) | length_bits].pack(format_string)
          buffer += "\x23\x23"
          # Mask off the length bits then set them
          buffer += [(max_signed & mask) | length_bits].pack(format_string)
          buffer += "\x24\x24"
          expect(@packet.buffer).to eql buffer
          expect(@packet.read("item1")).to eql(max_unsigned)
          expect(@packet.read("item2")).to eql(max_signed)
        end
      end

      it "can define multiple variable sized items" do
        item1_length = @packet.append_item("item1_length", 32, :INT)
        expect(item1_length.bit_offset).to eql 0

        item1 = @packet.append_item("item1", 0, :STRING)
        @packet.write("item1_length", 0)
        item1.variable_bit_size = {'length_item_name' => 'item1_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        expect(item1.bit_offset).to eql 32

        item2_length = @packet.append_item("item2_length", 16, :UINT)
        expect(item2_length.bit_offset).to eql 32

        item2 = @packet.append_item("item2", 0, :STRING)
        @packet.write("item2_length", 0)
        item2.variable_bit_size = {'length_item_name' => 'item2_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        expect(item2.bit_offset).to eql 48
        expect(@packet.buffer).to eql "\x00\x00\x00\x00\x00\x00"

        item3_length = @packet.append_item("item3_length", 8, :UINT)
        expect(item3_length.bit_offset).to eql 48
        expect(@packet.buffer).to eql "\x00\x00\x00\x00\x00\x00\x00"

        item3 = @packet.append_item("item3", 8, :UINT, 0)
        @packet.write("item3_length", 0)
        item3.variable_bit_size = {'length_item_name' => 'item3_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        expect(item3.bit_offset).to eql 56
        expect(@packet.buffer).to eql "\x00\x00\x00\x00\x00\x00\x00"

        @packet.write("item1", "This is Awesome")
        expect(@packet.buffer).to eql "\x00\x00\x00\x0FThis is Awesome\x00\x00\x00"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
        expect(item3_length.bit_offset).to eq 168
        expect(item3.bit_offset).to eq 176

        @packet.write("item2", "So is this")
        expect(@packet.buffer).to eql "\x00\x00\x00\x0FThis is Awesome\x00\x0ASo is this\x00"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
        expect(item3_length.bit_offset).to eq 248
        expect(item3.bit_offset).to eq 256

        @packet.write("item3", [1, 2, 3, 4, 5])
        expect(@packet.buffer).to eql "\x00\x00\x00\x0FThis is Awesome\x00\x0ASo is this\x05\x01\x02\x03\x04\x05"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
        expect(item3_length.bit_offset).to eq 248
        expect(item3.bit_offset).to eq 256

        @packet.write("item2", "Small")
        expect(@packet.buffer).to eql "\x00\x00\x00\x0FThis is Awesome\x00\x05Small\x05\x01\x02\x03\x04\x05"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
        expect(item3_length.bit_offset).to eq 208
        expect(item3.bit_offset).to eq 216

        @packet.write("item1", "Yo")
        expect(@packet.buffer).to eql "\x00\x00\x00\x02Yo\x00\x05Small\x05\x01\x02\x03\x04\x05"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 48
        expect(item2.bit_offset).to eq 64
        expect(item3_length.bit_offset).to eq 104
        expect(item3.bit_offset).to eq 112

        # Recalculates offsets when buffer is written
        @packet.buffer = "\x00\x00\x00\x03Ok!\x00\x04Cool\x02\xA5\x5A"
        expect(item1_length.bit_offset).to eql 0
        expect(item1.bit_offset).to eql 32
        expect(item2_length.bit_offset).to eql 56
        expect(item2.bit_offset).to eql 72
        expect(item3_length.bit_offset).to eql 104
        expect(item3.bit_offset).to eql 112
        expect(@packet.read("item1_length")).to eql 3
        expect(@packet.read("item1")).to eql "Ok!"
        expect(@packet.read("item2_length")).to eql 4
        expect(@packet.read("item2")).to eql "Cool"
        expect(@packet.read("item3_length")).to eql 2
        expect(@packet.read("item3")).to eql [0xA5, 0x5A]
      end

      it "can define multiple variable sized items with non-zero value offsets" do
        item1_length = @packet.append_item("item1_length", 32, :INT)
        expect(item1_length.bit_offset).to eql 0
        expect(@packet.buffer).to eql "\x00\x00\x00\x00"
        item1 = @packet.append_item("item1", 0, :STRING)
        @packet.write("item1_length", 4) # Lengths must be initialized to zero equivalent
        expect(@packet.buffer).to eql "\x00\x00\x00\x04"
        item1.variable_bit_size = {'length_item_name' => 'item1_length', 'length_value_bit_offset' => -32, 'length_bits_per_count' => 8}
        expect(item1.bit_offset).to eql 32
        item2_length = @packet.append_item("item2_length", 16, :UINT)
        expect(item2_length.bit_offset).to eql 32
        expect(@packet.buffer).to eql "\x00\x00\x00\x04\x00\x00"
        item2 = @packet.append_item("item2", 0, :STRING)
        expect(@packet.buffer).to eql "\x00\x00\x00\x04\x00\x00"
        @packet.write("item2_length", 8) # Lengths must be initialized to zero equivalent
        item2.variable_bit_size = {'length_item_name' => 'item2_length', 'length_value_bit_offset' => -64, 'length_bits_per_count' => 8}
        expect(item2.bit_offset).to eql 48
        expect(@packet.buffer).to eql "\x00\x00\x00\x04\x00\x08"
        @packet.write("item1", "This is Awesome")
        expect(@packet.buffer).to eql "\x00\x00\x00\x13This is Awesome\x00\x08"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
        @packet.write("item2", "So is this")
        expect(@packet.buffer).to eql "\x00\x00\x00\x13This is Awesome\x00\x12So is this"
        expect(item1_length.bit_offset).to eq 0
        expect(item1.bit_offset).to eq 32
        expect(item2_length.bit_offset).to eq 152
        expect(item2.bit_offset).to eq 168
      end

      it "can define multiple variable sized items with overlaps 1" do
        item1_length = @packet.define_item("item1_length", 0, 32, :INT)
        item1 = @packet.define_item("item1", 32, 0, :STRING)
        item1.variable_bit_size = {'length_item_name' => 'item1_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        item2 = @packet.define_item("item2", 0, 0, :BLOCK)
        expect(item1_length.bit_offset).to eq(0)
        expect(item1.bit_offset).to eq(32)
        expect(item2.bit_offset).to eq(0)
        @packet.buffer = "\x00\x00\x00\x06\x48\x45\x4C\x4C\x4F\x00"
        expect(item1_length.bit_offset).to eq(0)
        expect(item1.bit_offset).to eq(32)
        expect(item2.bit_offset).to eq(0)
        expect(@packet.read("item1_length")).to eql 6
        expect(@packet.read("item1")).to eql "HELLO"
        expect(@packet.read("item2")).to eql "\x00\x00\x00\x06\x48\x45\x4C\x4C\x4F\x00"
      end

      it "can handle reading and writing multiple variable sized array items" do
        item1_length = @packet.append_item("item1_length", 32, :UINT)
        item1 = @packet.append_item("item1", 8, :UINT, 0)
        item1.variable_bit_size = {'length_item_name' => 'item1_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}
        item2_length = @packet.append_item("item2_length", 32, :UINT)
        item2 = @packet.append_item("item2", 8, :UINT, 0)
        item2.variable_bit_size = {'length_item_name' => 'item2_length', 'length_value_bit_offset' => 0, 'length_bits_per_count' => 8}

        expect(item1_length.bit_offset).to eq(0)
        expect(item1.bit_offset).to eq(32)
        expect(item1.array_size).to eq(0)
        expect(item2_length.bit_offset).to eq(32)
        expect(item2.bit_offset).to eq(64)
        expect(item2.array_size).to eq(0)
        @packet.buffer = "\x00\x00\x00\x06\x01\x02\x03\x04\x05\x06\x00\x00\x00\x02\x07\x08"
        expect(item1_length.bit_offset).to eq(0)
        expect(item1.bit_offset).to eq(32)
        expect(item2_length.bit_offset).to eq(80)
        expect(item2.bit_offset).to eq(112)
        expect(@packet.read("item1_length")).to eql 6
        expect(@packet.read("item1")).to eql [1, 2, 3, 4, 5, 6]
        expect(item1.array_size).to eq(48)
        expect(@packet.read("item2_length")).to eql 2
        expect(@packet.read("item2")).to eql [7, 8]
        expect(item2.array_size).to eq(16)

        @packet.write("item1", [11, 12, 13])
        @packet.write("item2", [17])
        expect(@packet.buffer).to eql "\x00\x00\x00\x03\x0B\x0C\x0D\x00\x00\x00\x01\x11"
        @packet.write("item2", [11, 12, 13])
        @packet.write("item1", [17])
        expect(@packet.buffer).to eql "\x00\x00\x00\x01\x11\x00\x00\x00\x03\x0B\x0C\x0D"
        expect(@packet.read("item2_length")).to eql 3
        expect(@packet.read("item2")).to eql [11, 12, 13]
        expect(item2.array_size).to eq(24)
        expect(@packet.read("item1_length")).to eql 1
        expect(@packet.read("item1")).to eql [17]
        expect(item1.array_size).to eq(8)
      end
    end

    describe "read only" do
      before(:each) do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
      end

      it "complains about unknown data types" do
        expect { BinaryAccessor.read(0, 32, :BLOB, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "data_type BLOB is not recognized")
      end

      it "complains about bit_offsets before the beginning of the buffer" do
        expect { BinaryAccessor.read(-((@data.length * 8) + 8), 32, :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "#{@data.length} byte buffer insufficient to read STRING at bit_offset #{-((@data.length * 8) + 8)} with bit_size 32")
      end

      it "complains about a negative bit_offset and zero bit_size" do
        expect { BinaryAccessor.read(-8, 0, :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "negative or zero bit_sizes (0) cannot be given with negative bit_offsets (-8)")
      end

      it "complains about a negative bit_offset and negative bit_size" do
        expect { BinaryAccessor.read(-8, -8, :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "negative or zero bit_sizes (-8) cannot be given with negative bit_offsets (-8)")
      end

      it "complains about negative bit_sizes larger than the size of the buffer" do
        expect { BinaryAccessor.read(0, -((@data.length * 8) + 8), :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "#{@data.length} byte buffer insufficient to read STRING at bit_offset 0 with bit_size #{-((@data.length * 8) + 8)}")
      end

      it "complains about negative or zero bit_sizes with data_types other than STRING and BLOCK" do
        expect { BinaryAccessor.read(0, -8, :INT, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
        expect { BinaryAccessor.read(0, -8, :UINT, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
        expect { BinaryAccessor.read(0, -8, :FLOAT, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
      end

      it "reads aligned strings" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          if (bit_offset / 8) <= 7
            expect(BinaryAccessor.read(bit_offset, (@data.length * 8) - bit_offset, :STRING, @data, :BIG_ENDIAN)).to eql(@data[(bit_offset / 8)..7])
          elsif (bit_offset / 8) == 8
            expect(BinaryAccessor.read(bit_offset, (@data.length * 8) - bit_offset, :STRING, @data, :BIG_ENDIAN)).to eql('')
          else
            expect(BinaryAccessor.read(bit_offset, (@data.length * 8) - bit_offset, :STRING, @data, :BIG_ENDIAN)).to eql(@data[(bit_offset / 8)..-1])
          end
        end
      end

      it "reads variable length strings with a zero and negative bit_size" do
        0.step(-(@data.length * 8), -8) do |bit_size|
          if (bit_size / 8) >= -8
            expect(BinaryAccessor.read(0, bit_size, :STRING, @data, :BIG_ENDIAN)).to eql(@data[0..7])
          else
            expect(BinaryAccessor.read(0, bit_size, :STRING, @data, :BIG_ENDIAN)).to eql(@data[0..((bit_size / 8) - 1)])
          end
        end
      end

      it "reads strings with negative bit_offsets" do
        expect(BinaryAccessor.read(-16, 16, :STRING, @data, :BIG_ENDIAN)).to eql(@data[-2..-1])
      end

      it "complains about unaligned strings" do
        expect { BinaryAccessor.read(1, 32, :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type STRING")
      end

      it "reads aligned blocks" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          expect(BinaryAccessor.read(bit_offset, (@data.length * 8) - bit_offset, :BLOCK, @data, :BIG_ENDIAN)).to eql(@data[(bit_offset / 8)..-1])
        end
      end

      it "reads variable length blocks with a zero and negative bit_size" do
        0.step(-(@data.length * 8), -8) do |bit_size|
          expect(BinaryAccessor.read(0, bit_size, :BLOCK, @data, :BIG_ENDIAN)).to eql(@data[0..((bit_size / 8) - 1)])
        end
      end

      it "reads blocks with negative bit_offsets" do
        expect(BinaryAccessor.read(-16, 16, :BLOCK, @data, :BIG_ENDIAN)).to eql(@data[-2..-1])
      end

      it "complains about unaligned blocks" do
        expect { BinaryAccessor.read(7, 16, :BLOCK, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 7 is not byte aligned for data_type BLOCK")
      end

      it "complains if read exceeds the size of the buffer" do
        expect { BinaryAccessor.read(8, 800, :STRING, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "16 byte buffer insufficient to read STRING at bit_offset 8 with bit_size 800")
      end

      it "reads aligned 8-bit unsigned integers" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          expect(BinaryAccessor.read(bit_offset, 8, :UINT, @data, :BIG_ENDIAN)).to eql(@data.getbyte(bit_offset / 8))
        end
      end

      it "reads aligned 8-bit signed integers" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          expected = @data.getbyte(bit_offset / 8)
          expected = expected - 256 if expected >= 128
          expect(BinaryAccessor.read(bit_offset, 8, :INT, @data, :BIG_ENDIAN)).to eql(expected)
        end
      end

      describe "given big endian data" do
        it "reads 1-bit unsigned integers" do
          expected = [0x1, 0x0]
          bit_size = 1
          expect(BinaryAccessor.read(8, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 1-bit signed integers" do
          expected = [0x1, 0x0]
          bit_size = 1
          expect(BinaryAccessor.read(8, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 7-bit unsigned integers" do
          expected = [0x40, 0x02]
          bit_size = 7
          expect(BinaryAccessor.read(8, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(3, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 7-bit signed integers" do
          expected = [0x40, 0x02]
          bit_size = 7
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(8, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(3, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 13-bit unsigned integers" do
          expected = [0x1C24, 0x20]
          bit_size = 13
          expect(BinaryAccessor.read(30, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(1,  bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 13-bit signed integers" do
          expected = [0x1C24, 0x20]
          bit_size = 13
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(30, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(1,  bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads aligned 16-bit unsigned integers" do
          expected_array = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 16, :UINT, @data, :BIG_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 16-bit signed integers" do
          expected_array = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**16) if expected >= 2**15
            expect(BinaryAccessor.read(bit_offset, 16, :INT, @data, :BIG_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 32-bit unsigned integers" do
          expected_array = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 32, :UINT, @data, :BIG_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 32-bit signed integers" do
          expected_array = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**32) if expected >= 2**31
            expect(BinaryAccessor.read(bit_offset, 32, :INT, @data, :BIG_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 32-bit floats" do
          expected_array = [-1.189360e-038, -3.139169e-036, 8.301067e-040, 1.086646e-031]
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-038).of(expected_array[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-036).of(expected_array[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-040).of(expected_array[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-031).of(expected_array[3])
        end

        it "reads 37-bit unsigned integers" do
          expected = [0x8182838485 >> 3, 0x00090A0B0C]
          bit_size = 37
          expect(BinaryAccessor.read(8,  bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(67, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 37-bit signed integers" do
          expected = [0x8182838485 >> 3, 0x00090A0B0C]
          bit_size = 37
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(8,  bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(67, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 63-bit unsigned integers" do
          expected = [0x8081828384858687 >> 1, 0x00090A0B0C0D0E0F]
          bit_size = 63
          expect(BinaryAccessor.read(0,  bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(65, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 63-bit signed integers" do
          expected = [0x8081828384858687 >> 1, 0x00090A0B0C0D0E0F]
          bit_size = 63
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(0,  bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(65, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 67-bit unsigned integers" do
          expected = [0x808182838485868700 >> 5, 0x8700090A0B0C0D0E0F >> 5]
          bit_size = 67
          expect(BinaryAccessor.read(0,  bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(56, bit_size, :UINT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads 67-bit signed integers" do
          expected = [0x808182838485868700 >> 5, 0x8700090A0B0C0D0E0F >> 5]
          bit_size = 67
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(0,  bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(56, bit_size, :INT, @data, :BIG_ENDIAN)).to eql(expected[1])
        end

        it "reads aligned 64-bit unsigned integers" do
          expected_array = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 64, :UINT, @data, :BIG_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 64-bit signed integers" do
          expected_array = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**64) if expected >= 2**63
            expect(BinaryAccessor.read(bit_offset, 64, :INT, @data, :BIG_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 64-bit floats" do
          expected_array = [-3.116851e-306, 1.257060e-308]
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-306).of(expected_array[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-308).of(expected_array[1])
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.read(17, 32, :FLOAT, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 17 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.read(0, 33, :FLOAT, @data, :BIG_ENDIAN) }.to raise_error(ArgumentError, "bit_size is 33 but must be 32 or 64 for data_type FLOAT")
        end
      end # given big endian data

      describe "given little endian data" do
        it "complains about ill-defined little endian bitfields" do
          expect { BinaryAccessor.read(3, 7, :UINT, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset 3 and bit_size 7 is invalid")
        end

        it "reads 1-bit unsigned integers" do
          expected = [0x1, 0x0]
          bit_size = 1
          expect(BinaryAccessor.read(8, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 1-bit signed integers" do
          expected = [0x1, 0x0]
          bit_size = 1
          expect(BinaryAccessor.read(8, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 7-bit unsigned integers" do
          expected = [0x40, 0x60]
          bit_size = 7
          expect(BinaryAccessor.read(8, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(15, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 7-bit signed integers" do
          expected = [0x40, 0x60]
          bit_size = 7
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(8, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(15, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 13-bit unsigned integers" do
          expected = [0x038281 >> 5, 0x0180 >> 2]
          bit_size = 13
          expect(BinaryAccessor.read(30, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9,  bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 13-bit signed integers" do
          expected = [0x038281 >> 5, 0x0180 >> 2]
          bit_size = 13
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(30, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(9,  bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads aligned 16-bit unsigned integers" do
          expected_array = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 16, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 16-bit signed integers" do
          expected_array = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**16) if expected >= 2**15
            expect(BinaryAccessor.read(bit_offset, 16, :INT, @data, :LITTLE_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 32-bit unsigned integers" do
          expected_array = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 32, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 32-bit signed integers" do
          expected_array = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**32) if expected >= 2**31
            expect(BinaryAccessor.read(bit_offset, 32, :INT, @data, :LITTLE_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 32-bit floats" do
          expected_array = [-7.670445e-037, -2.024055e-034, 2.658460e-032, 7.003653e-030]
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-037).of(expected_array[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-034).of(expected_array[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-032).of(expected_array[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-030).of(expected_array[3])
        end

        it "reads 37-bit unsigned integers" do
          expected = [0x8584838281 >> 3, 0x0F0E0D0C0B]
          bit_size = 37
          expect(BinaryAccessor.read(40,  bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(123, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 37-bit signed integers" do
          expected = [0x8584838281 >> 3, 0x0F0E0D0C0B]
          bit_size = 37
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(40,  bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(123, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 63-bit unsigned integers" do
          expected = [0x0F0E0D0C0B0A0900 >> 1, 0x0786858483828180]
          bit_size = 63
          expect(BinaryAccessor.read(120, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(57, bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 63-bit signed integers" do
          expected = [0x0F0E0D0C0B0A0900 >> 1, 0x0786858483828180]
          bit_size = 63
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(120,  bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
          expect(BinaryAccessor.read(57,   bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[1])
        end

        it "reads 67-bit unsigned integers" do
          expected = [0x0F0E0D0C0B0A090087 >> 5]
          bit_size = 67
          expect(BinaryAccessor.read(120,  bit_size, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
        end

        it "reads 67-bit signed integers" do
          expected = [0x0F0E0D0C0B0A090087 >> 5]
          bit_size = 67
          expected.each_with_index { |value, index| expected[index] = value - (2**bit_size) if value >= 2**(bit_size - 1) }
          expect(BinaryAccessor.read(120, bit_size, :INT, @data, :LITTLE_ENDIAN)).to eql(expected[0])
        end

        it "reads aligned 64-bit unsigned integers" do
          expected_array = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expect(BinaryAccessor.read(bit_offset, 64, :UINT, @data, :LITTLE_ENDIAN)).to eql(expected_array[index])
            index += 1
          end
        end

        it "reads aligned 64-bit signed integers" do
          expected_array = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**64) if expected >= 2**63
            expect(BinaryAccessor.read(bit_offset, 64, :INT, @data, :LITTLE_ENDIAN)).to eql(expected)
            index += 1
          end
        end

        it "reads aligned 64-bit floats" do
          expected_array = [-2.081577e-272, 3.691916e-236]
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-272).of(expected_array[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-236).of(expected_array[1])
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.read(1, 32, :FLOAT, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.read(0, 65, :FLOAT, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_size is 65 but must be 32 or 64 for data_type FLOAT")
        end
      end # little endian
    end # describe 'read'

    describe "read_array" do
      before(:each) do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
      end

      it "complains with unknown data_type" do
        expect { BinaryAccessor.read_array(0, 8, :BLAH, 0, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "data_type BLAH is not recognized")
      end

      describe "given little endian data" do
        it "complains about negative bit sizes" do
          expect { BinaryAccessor.read_array(0, -8, :UINT, @data.length * 8, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_size -8 must be positive for arrays")
        end

        context "when positive or zero bit_offset" do
          it "reads the given array_size amount of items" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(0, 8, :UINT, 32, @data, :LITTLE_ENDIAN)).to eql(data[0..3])
          end

          it "reads the given array_size amount of items" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(32, 8, :UINT, 32, @data, :LITTLE_ENDIAN)).to eql(data[4..7])
          end

          it "reads the total buffer given array_size = buffer size" do
            data = @data.unpack('c*')

            expect(BinaryAccessor.read_array(0, 8, :INT, @data.length * 8, @data, :LITTLE_ENDIAN)).to eql(data)
          end

          it "complains with an array_size not a multiple of bit_size" do
            @data.unpack('C*')
            expect { BinaryAccessor.read_array(0, 8, :UINT, 10, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "array_size 10 not a multiple of bit_size 8")
          end

          it "reads as many items as possible with a zero array_size" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(0, 8, :UINT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
          end

          it "excludes the remaining bits if array_size is negative" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(0, 8, :UINT, -32, @data, :LITTLE_ENDIAN)).to eql(data[0..-5])
          end

          it "returns an empty array if the offset equals the negative array size" do
            @data.unpack('C*')
            expect(BinaryAccessor.read_array((@data.length * 8) - 32, 8, :UINT, -32, @data, :LITTLE_ENDIAN)).to eql([])
          end

          it "complains if the offset is greater than the negative array size" do
            @data.unpack('C*')
            offset = (@data.length * 8) - 16
            expect { BinaryAccessor.read_array(offset, 8, :UINT, -32, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "16 byte buffer insufficient to read UINT at bit_offset #{offset} with bit_size 8")
          end
        end

        context "when negative bit_offset" do
          it "reads an array of aligned 8 bit unsigned integers" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(-32, 8, :UINT, 32, @data, :LITTLE_ENDIAN)).to eql(data[-4..-1])
          end

          it "reads an array if the negative offset is the size of the array" do
            data = @data.unpack('C*')
            expect(BinaryAccessor.read_array(-(@data.length * 8), 8, :UINT, @data.length * 8, @data, :LITTLE_ENDIAN)).to eql(data)
          end

          it "complains if the offset is larger than the buffer" do
            expect { BinaryAccessor.read_array(-((@data.length * 8) + 1), 8, :UINT, @data.length * 8, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "#{@data.length} byte buffer insufficient to read UINT at bit_offset -#{(@data.length * 8) + 1} with bit_size 8")
          end

          it "complains with zero array_size" do
            expect { BinaryAccessor.read_array(-32, 8, :UINT, 0, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "negative or zero array_size (0) cannot be given with negative bit_offset (-32)")
          end

          it "complains with negative array_size" do
            expect { BinaryAccessor.read_array(-32, 8, :UINT, -8, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "negative or zero array_size (-8) cannot be given with negative bit_offset (-32)")
          end
        end

        it "complains about accessing data from a buffer which is too small" do
          expect { BinaryAccessor.read_array(0, 256, :STRING, 256, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "16 byte buffer insufficient to read STRING at bit_offset 0 with bit_size 256")
        end

        it "returns an empty array when passed a zero length buffer" do
          expect(BinaryAccessor.read_array(0, 8, :UINT, 32, "", :LITTLE_ENDIAN)).to eql([])
        end

        it "complains about unaligned strings" do
          expect { BinaryAccessor.read_array(1, 32, :STRING, 32, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type STRING")
        end

        it "reads a single STRING item" do
          expect(BinaryAccessor.read_array(0, 128, :STRING, 0, @data, :LITTLE_ENDIAN)).to eql([@data[0..7]])
        end

        it "reads a single BLOCK item" do
          expect(BinaryAccessor.read_array(0, 128, :BLOCK, 0, @data, :LITTLE_ENDIAN)).to eql([@data])
        end

        it "reads BLOCK items" do
          data = BinaryAccessor.read_array(0, 8, :BLOCK, 0, @data, :LITTLE_ENDIAN)
          data.each_with_index { |val, i| expect(val).to eql(@data[i]) }
        end

        it "reads 1-bit integers" do
          expected = [0x1, 0x0, 0x0, 0x0]
          expect(BinaryAccessor.read_array(0, 1, :INT, 4, @data, :LITTLE_ENDIAN)).to eql(expected)
          expect(BinaryAccessor.read_array(0, 1, :INT, 2, @data, :LITTLE_ENDIAN)).to eql(expected[0..1])
        end

        it "complains about little endian bit-fields greater than 1-bit" do
          expect { BinaryAccessor.read_array(8, 7, :UINT, 21, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "read_array does not support little endian bit fields with bit_size greater than 1-bit")
        end

        it "reads 16 bit UINT items" do
          data = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          expect(BinaryAccessor.read_array(0, 16, :UINT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads 16 bit INT items" do
          data = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          data.map! { |x| (x & ~(1 << 15)) - (x & (1 << 15)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 16, :INT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads 32 bit UINT items" do
          data = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          expect(BinaryAccessor.read_array(0, 32, :UINT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads 32 bit INT items" do
          data = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          data.map! { |x| (x & ~(1 << 31)) - (x & (1 << 31)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 32, :INT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads 64 bit UINT items" do
          data = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          expect(BinaryAccessor.read_array(0, 64, :UINT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads 64 bit INT items" do
          data = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          data.map! { |x| (x & ~(1 << 63)) - (x & (1 << 63)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 64, :INT, 0, @data, :LITTLE_ENDIAN)).to eql(data)
        end

        it "reads aligned 32-bit floats" do
          expected_array = [-7.670445e-037, -2.024055e-034, 2.658460e-032, 7.003653e-030]
          actual = BinaryAccessor.read_array(0, 32, :FLOAT, 0, @data, :LITTLE_ENDIAN)
          actual.each_with_index do |val, index|
            expect(val).to be_within(1.0e-030).of(expected_array[index])
          end
        end

        it "reads aligned 64-bit floats" do
          expected_array = [-2.081577e-272, 3.691916e-236]
          actual = BinaryAccessor.read_array(0, 64, :FLOAT, 0, @data, :LITTLE_ENDIAN)
          actual.each_with_index do |val, index|
            expect(val).to be_within(1.0e-236).of(expected_array[index])
          end
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.read_array(1, 32, :FLOAT, 32, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.read_array(0, 65, :FLOAT, 65, @data, :LITTLE_ENDIAN) }.to raise_error(ArgumentError, "bit_size is 65 but must be 32 or 64 for data_type FLOAT")
        end
      end # given little endian data

      describe "given big endian data" do
        it "reads 7-bit unsigned integers" do
          expected = [0x40, 0x60, 0x50]
          bit_size = 7
          expect(BinaryAccessor.read_array(8, bit_size, :UINT, 21, @data, :BIG_ENDIAN)).to eql(expected)
        end

        it "reads 16 bit UINT items" do
          data = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          expect(BinaryAccessor.read_array(0, 16, :UINT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads 16 bit INT items" do
          data = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          data.map! { |x| (x & ~(1 << 15)) - (x & (1 << 15)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 16, :INT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads 32 bit UINT items" do
          data = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          expect(BinaryAccessor.read_array(0, 32, :UINT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads 32 bit INT items" do
          data = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          data.map! { |x| (x & ~(1 << 31)) - (x & (1 << 31)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 32, :INT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads 64 bit UINT items" do
          data = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          expect(BinaryAccessor.read_array(0, 64, :UINT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads 64 bit INT items" do
          data = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          data.map! { |x| (x & ~(1 << 63)) - (x & (1 << 63)) } # convert to negative
          expect(BinaryAccessor.read_array(0, 64, :INT, 0, @data, :BIG_ENDIAN)).to eql(data)
        end

        it "reads aligned 32-bit floats" do
          expected_array = [-1.189360e-038, -3.139169e-036, 8.301067e-040, 1.086646e-031]
          actual = BinaryAccessor.read_array(0, 32, :FLOAT, 0, @data, :BIG_ENDIAN)
          actual.each_with_index do |val, index|
            expect(val).to be_within(1.0e-030).of(expected_array[index])
          end
        end

        it "reads aligned 64-bit floats" do
          expected_array = [-3.116851e-306, 1.257060e-308]
          actual = BinaryAccessor.read_array(0, 64, :FLOAT, 0, @data, :BIG_ENDIAN)
          actual.each_with_index do |val, index|
            expect(val).to be_within(1.0e-236).of(expected_array[index])
          end
        end
      end # given big endian data
    end # describe "read_array"

    describe "write only" do
      before(:each) do
        @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        @baseline_data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
      end

      it "complains about unknown data types" do
        expect { BinaryAccessor.write(0, 0, 32, :BLOB, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "data_type BLOB is not recognized")
      end

      it "complains about bit_offsets before the beginning of the buffer" do
        expect { BinaryAccessor.write('', -((@data.length * 8) + 8), 32, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "#{@data.length} byte buffer insufficient to write STRING at bit_offset #{-((@data.length * 8) + 8)} with bit_size 32")
      end

      it "complains about a negative bit_offset and zero bit_size" do
        expect { BinaryAccessor.write('', -8, 0, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "negative or zero bit_sizes (0) cannot be given with negative bit_offsets (-8)")
      end

      it "complains about a negative bit_offset and negative bit_size" do
        expect { BinaryAccessor.write('', -8, -8, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "negative or zero bit_sizes (-8) cannot be given with negative bit_offsets (-8)")
      end

      it "complains about negative or zero bit_sizes with data_types other than STRING and BLOCK" do
        expect { BinaryAccessor.write(0, 0, -8, :INT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
        expect { BinaryAccessor.write(0, 0, -8, :UINT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
        expect { BinaryAccessor.write(0, 0, -8, :FLOAT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size -8 must be positive for data types other than :STRING and :BLOCK")
      end

      it "writes aligned strings" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = @baseline_data.clone
          first_byte_index = bit_offset / 8
          if first_byte_index > 0
            @expected_data[0..(first_byte_index - 1)] = "\x00" * first_byte_index
          end
          BinaryAccessor.write(@baseline_data[first_byte_index..-1], bit_offset, (@data.length * 8) - bit_offset, :STRING, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes variable length strings with a zero and negative bit_size" do
        0.step(-(@baseline_data.length * 8), -8) do |bit_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = @baseline_data.clone + ("\x00" * -(bit_size / 8))
          BinaryAccessor.write(@baseline_data, 0, bit_size, :STRING, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes strings with negative bit_offsets" do
        BinaryAccessor.write(@baseline_data[14..15], -16, 16, :STRING, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(("\x00" * 14) + @baseline_data[14..15])
      end

      it "complains about unaligned strings" do
        expect { BinaryAccessor.write('', 1, 32, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type STRING")
      end

      it "writes aligned blocks" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = @baseline_data.clone
          first_byte_index = bit_offset / 8
          if first_byte_index > 0
            @expected_data[0..(first_byte_index - 1)] = "\x00" * first_byte_index
          end
          BinaryAccessor.write(@baseline_data[first_byte_index..-1], bit_offset, (@data.length * 8) - bit_offset, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes variable length blocks with a zero and negative bit_size" do
        0.step(-(@data.length * 8), -8) do |bit_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = @baseline_data.clone + ("\x00" * -(bit_size / 8))
          BinaryAccessor.write(@baseline_data, 0, bit_size, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      context "when negative bit size" do
        it "writes a block to an empty buffer" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          buffer = ""
          expect { BinaryAccessor.write(data, 0, -16, :BLOCK, buffer, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "0 byte buffer insufficient to write BLOCK at bit_offset 0 with bit_size -16")
        end

        it "handles a huge bit offset with small buffer" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          buffer = ""
          expect { BinaryAccessor.write(data, 1024, 0, :BLOCK, buffer, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "0 byte buffer insufficient to write BLOCK at bit_offset 1024 with bit_size 0")
        end

        it "handles an edge case bit offset" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          buffer = "\x00" * 127
          expect { BinaryAccessor.write(data, 1024, 0, :BLOCK, buffer, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "127 byte buffer insufficient to write BLOCK at bit_offset 1024 with bit_size 0")
        end

        it "writes a block to a small buffer preserving the end" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          preserve = [0xBEEF].pack("n")
          buffer = preserve.clone # Should preserve this
          BinaryAccessor.write(data, 0, -16, :BLOCK, buffer, :BIG_ENDIAN, :ERROR)
          expect(buffer[0..-3]).to eql data
          expect(buffer[-2..-1]).to eql preserve
          data = BinaryAccessor.read(0, (data.length * 8) + 16, :BLOCK, buffer, :BIG_ENDIAN)
          expect(data).to eql buffer
        end

        it "writes a block to another small buffer preserving the end" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          preserve = [0xBEEF0123].pack("N")
          buffer = "\x00\x01" + preserve.clone # Should preserve this
          BinaryAccessor.write(data, 16, -32, :BLOCK, buffer, :BIG_ENDIAN, :ERROR)
          expect(buffer[0..1]).to eql "\x00\x01"
          expect(buffer[2..-5]).to eql data
          expect(buffer[-4..-1]).to eql preserve
          data = BinaryAccessor.read(0, 16 + (data.length * 8) + 32, :BLOCK, buffer, :BIG_ENDIAN)
          expect(data).to eql buffer
        end

        it "writes a block to a small buffer overwriting the end" do
          data = ''
          512.times do |index|
            data << [index].pack("n")
          end
          preserve = [0xBEEF].pack("n")
          buffer = [0xDEAD].pack("n") # Should write over this
          buffer << preserve.clone # Should preserve this
          BinaryAccessor.write(data, 0, -16, :BLOCK, buffer, :BIG_ENDIAN, :ERROR)
          expect(buffer[0..-3]).to eql data
          expect(buffer[-2..-1]).to eql preserve
          data = BinaryAccessor.read(0, (data.length * 8) + 16, :BLOCK, buffer, :BIG_ENDIAN)
          expect(data).to eql buffer
        end

        it "writes a smaller block in the middle of a buffer" do
          data = ''
          buffer = ''
          256.times do |index|
            data << [index].pack("n")
          end
          512.times do
            buffer << [0xDEAD].pack("n")
          end
          expected = buffer.clone
          BinaryAccessor.write(data, 128 * 8, -128 * 8, :BLOCK, buffer, :BIG_ENDIAN, :ERROR)
          expect(buffer.length).to eql(128 + 512 + 128)
          expect(buffer[0...128]).to eql expected[0...128]
          expect(buffer[128...-128]).to eql data
          expect(buffer[-128..-1]).to eql expected[0...128]
        end

        it "writes a larger block in the middle of a buffer" do
          data = ''
          buffer = ''
          256.times do |index|
            data << [index].pack("n")
          end
          512.times do
            buffer << [0xDEAD].pack("n")
          end
          expected = buffer.clone
          BinaryAccessor.write(data, 384 * 8, -384 * 8, :BLOCK, buffer, :BIG_ENDIAN, :ERROR)
          expect(buffer.length).to eql(384 + 512 + 384)
          expect(buffer[0...384]).to eql expected[0...384]
          expect(buffer[384...-384]).to eql data
          expect(buffer[-384..-1]).to eql expected[0...384]
        end

        it "complains when the negative index exceeds the buffer length" do
          data = "\x01"
          buffer = ''
          16.times do
            buffer << [0xDEAD].pack("n")
          end
          expect { BinaryAccessor.write(data, 0, -2024 * 8, :BLOCK, buffer, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "32 byte buffer insufficient to write BLOCK at bit_offset 0 with bit_size -16192")
        end
      end

      it "writes blocks with negative bit_offsets" do
        BinaryAccessor.write(@baseline_data[0..1], -16, 16, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data[-2..-1]).to eql(@baseline_data[0..1])
      end

      it "writes a blank string with zero bit size" do
        BinaryAccessor.write('', 0, 0, :STRING, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql('')
      end

      it "writes a blank block with zero bit size" do
        BinaryAccessor.write('', 0, 0, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql('')
      end

      it "writes a shorter string with zero bit size" do
        BinaryAccessor.write("\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0, :STRING, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "writes a shorter block with zero bit size" do
        BinaryAccessor.write("\x00\x00\x00\x00\x00\x00\x00\x00", 0, 0, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "writes a shorter string and zero fill to the given bit size" do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
        BinaryAccessor.write("\x01\x02\x03\x04\x05\x06\x07\x08", 0, 128, :STRING, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "writes a shorter block and zero fill to the given bit size" do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
        BinaryAccessor.write("\x01\x02\x03\x04\x05\x06\x07\x08", 0, 128, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x03\x04\x05\x06\x07\x08\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "complains about unaligned blocks" do
        expect { BinaryAccessor.write(@baseline_data, 7, 16, :BLOCK, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 7 is not byte aligned for data_type BLOCK")
      end

      it "complains if write exceeds the size of the buffer" do
        expect { BinaryAccessor.write(@baseline_data, 8, 800, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "16 byte buffer insufficient to write STRING at bit_offset 8 with bit_size 800")
      end

      it "truncates the buffer for 0 bitsize" do
        expect(@data.length).to eql 16
        BinaryAccessor.write("\x01\x02\x03", 8, 0, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x01\x02\x03")
        expect(@data.length).to eql 4
      end

      it "expands the buffer for 0 bitsize" do
        expect(@data.length).to eql 16
        BinaryAccessor.write("\x01\x02\x03", (14 * 8), 0, :BLOCK, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03")
        expect(@data.length).to eql 17
      end

      it "writes a frozen string" do
        buffer = "BLANKxxxWORLD"
        string = "HELLO".freeze
        # Specify 3 more bytes than given to exercise the padding logic
        string = BinaryAccessor.write(string, 0, (string.length + 3) * 8, :STRING, buffer, :BIG_ENDIAN, :ERROR)
        expect(buffer).to eql("HELLO\x00\x00\x00WORLD")
        expect(string).to eql("HELLO")
        expect(string.frozen?).to be true
      end

      it "complains about writing a frozen buffer" do
        buffer = "BLANK WORLD".freeze
        string = "HELLO"
        expect { BinaryAccessor.write(string, 0, string.length * 8, :STRING, buffer, :BIG_ENDIAN, :ERROR) }.to raise_error(RuntimeError, /can't modify frozen String/)
      end

      it "writes aligned 8-bit unsigned integers" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          byte_index = bit_offset / 8
          BinaryAccessor.write(@baseline_data.getbyte(byte_index), bit_offset, 8, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data[byte_index..byte_index]).to eq(@baseline_data[byte_index..byte_index])
        end
      end

      it "writes aligned 8-bit signed integers" do
        0.step((@data.length - 1) * 8, 8) do |bit_offset|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          byte_index = bit_offset / 8
          value = @baseline_data.getbyte(byte_index)
          value = value - 256 if value >= 128
          BinaryAccessor.write(value, bit_offset, 8, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data[byte_index..byte_index]).to eql(@baseline_data[byte_index..byte_index])
        end
      end

      it "converts floats when writing integers" do
        @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        BinaryAccessor.write(1.0, 0, 8, :UINT, @data, :BIG_ENDIAN, :ERROR)
        BinaryAccessor.write(2.5, 8, 8, :INT, @data, :BIG_ENDIAN, :ERROR)
        BinaryAccessor.write(4.99, 16, 8, :UINT, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "converts integer strings when writing integers" do
        @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        BinaryAccessor.write("1", 0, 8, :UINT, @data, :BIG_ENDIAN, :ERROR)
        BinaryAccessor.write("2", 8, 8, :INT, @data, :BIG_ENDIAN, :ERROR)
        BinaryAccessor.write("4", 16, 8, :UINT, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
      end

      it "complains about non-integer strings when writing integers" do
        expect { BinaryAccessor.write("1.0", 0, 8, :UINT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError)
        expect { BinaryAccessor.write("abc123", 0, 8, :UINT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError)
      end

      describe "given big endian data" do
        it "writes 1-bit unsigned integers" do
          BinaryAccessor.write(0x1, 8, 1, :UINT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 9, 1, :UINT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x1, 10, 1, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 1-bit signed integers" do
          @data[1] = "\x55"
          BinaryAccessor.write(0x1, 8, 1, :INT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 9, 1, :INT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x1, 10, 1, :INT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 11, 1, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 7-bit unsigned integers" do
          BinaryAccessor.write(0x40, 8, 7, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(0x20, 3, 7, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 7-bit signed integers" do
          BinaryAccessor.write(-64, 8, 7, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(32, 3, 7, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 13-bit unsigned integers" do
          BinaryAccessor.write(0x1C24, 30, 13, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x00\x00\x03\x84\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(0x0020, 1, 13, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 13-bit signed integers" do
          BinaryAccessor.write(-988, 30, 13, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x00\x00\x03\x84\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(32, 1, 13, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes aligned 16-bit unsigned integers" do
          expected_array = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 16, :UINT, @data, :BIG_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 16-bit signed integers" do
          expected_array = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**16) if expected >= 2**15
            BinaryAccessor.write(expected, bit_offset, 16, :INT, @data, :BIG_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit unsigned integers" do
          expected_array = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 32, :UINT, @data, :BIG_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit signed integers" do
          expected_array = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**32) if expected >= 2**31
            BinaryAccessor.write(expected, bit_offset, 32, :INT, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit negative integers" do
          BinaryAccessor.write(-2147483648, 0, 32, :INT, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(@data).to eql("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes aligned 32-bit floats" do
          expected_array = [-1.189360e-038, -3.139169e-036, 8.301067e-040, 1.086646e-031]
          BinaryAccessor.write(expected_array[0], 0,  32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[1], 32, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[2], 64, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[3], 96, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-038).of(expected_array[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-036).of(expected_array[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-040).of(expected_array[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-031).of(expected_array[3])
        end

        it "writes 37-bit unsigned integers" do
          BinaryAccessor.write(0x8182838485 >> 3, 8, 37, :UINT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x00090A0B0C, 67, 37, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x81\x82\x83\x84\x80\x00\x00\x00\x09\x0A\x0B\x0C\x00\x00\x00")
        end

        it "writes 37-bit signed integers" do
          BinaryAccessor.write((0x8182838485 >> 3) - (2**37), 8, 37, :INT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x00090A0B0C, 67, 37, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x81\x82\x83\x84\x80\x00\x00\x00\x09\x0A\x0B\x0C\x00\x00\x00")
        end

        it "writes 63-bit unsigned integers" do
          @data[-1] = "\xFF"
          BinaryAccessor.write(0x8081828384858687 >> 1, 0, 63, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x86\x00\x00\x00\x00\x00\x00\x00\xFF")
          @data[0] = "\xFF"
          BinaryAccessor.write(0x08090A0B0C0D0E0F, 65, 63, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\xFF\x81\x82\x83\x84\x85\x86\x86\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes 63-bit signed integers" do
          BinaryAccessor.write((0x8081828384858687 >> 1) - (2**63), 0, 63, :INT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(0x00090A0B0C0D0E0F, 65, 63, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x86\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes 67-bit unsigned integers" do
          BinaryAccessor.write(0x8081828384858687FF >> 5, 0, 67, :UINT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x87\xE0\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 67-bit signed integers" do
          BinaryAccessor.write((0x8081828384858687FF >> 5) - (2**67), 0, 67, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x87\xE0\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes aligned 64-bit unsigned integers" do
          expected_array = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 64, :UINT, @data, :BIG_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit signed integers" do
          expected_array = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**64) if expected >= 2**63
            BinaryAccessor.write(expected, bit_offset, 64, :INT, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit floats" do
          expected_array = [-3.116851e-306, 1.257060e-308]
          BinaryAccessor.write(expected_array[0], 0,  64, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[1], 64, 64, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-306).of(expected_array[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-308).of(expected_array[1])
        end

        it "converts integers to floats" do
          BinaryAccessor.write(1, 0, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          value = BinaryAccessor.read(0, 32, :FLOAT, @data, :BIG_ENDIAN)
          expect(value).to eql(1.0)
          expect(value).to be_a(Float)
          BinaryAccessor.write(4, 32, 64, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          value = BinaryAccessor.read(32, 64, :FLOAT, @data, :BIG_ENDIAN)
          expect(value).to eql(4.0)
          expect(value).to be_a(Float)
        end

        it "converts strings when writing floats" do
          BinaryAccessor.write("1", 0, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          value = BinaryAccessor.read(0, 32, :FLOAT, @data, :BIG_ENDIAN)
          expect(value).to eql(1.0)
          expect(value).to be_a(Float)
          BinaryAccessor.write("4.5", 32, 64, :FLOAT, @data, :BIG_ENDIAN, :ERROR)
          value = BinaryAccessor.read(32, 64, :FLOAT, @data, :BIG_ENDIAN)
          expect(value).to eql(4.5)
          expect(value).to be_a(Float)
        end

        it "complains about non-float strings when writing floats" do
          expect { BinaryAccessor.write("abc123", 0, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError)
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.write(0.0, 17, 32, :FLOAT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 17 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.write(0.0, 0, 33, :FLOAT, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size is 33 but must be 32 or 64 for data_type FLOAT")
        end
      end # given big endian data

      describe "given little endian data" do
        it "complains about ill-defined little endian bitfields" do
          expect { BinaryAccessor.write(0x1, 3, 7, :UINT, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "LITTLE_ENDIAN bitfield with bit_offset 3 and bit_size 7 is invalid")
        end

        it "writes 1-bit unsigned integers" do
          @data[1] = "\x55"
          BinaryAccessor.write(0x1, 8, 1, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 9, 1, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x1, 10, 1, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 11, 1, :INT, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 1-bit signed integers" do
          BinaryAccessor.write(0x1, 8, 1, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0, 9, 1, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x1, 10, 1, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 7-bit unsigned integers" do
          BinaryAccessor.write(0x40, 8, 7, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(0x7F, 11, 7, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\xC0\x1F\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 7-bit signed integers" do
          BinaryAccessor.write(-64, 8, 7, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(32, 11, 7, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 13-bit unsigned integers" do
          BinaryAccessor.write(0x1C24, 30, 13, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x84\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(0x0020, 9, 13, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 13-bit signed integers" do
          BinaryAccessor.write(-988, 30, 13, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x80\x84\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          BinaryAccessor.write(32, 9, 13, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes aligned 16-bit unsigned integers" do
          expected_array = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 16, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 16-bit signed integers" do
          expected_array = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          index = 0
          0.step((@data.length - 1) * 8, 16) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**16) if expected >= 2**15
            BinaryAccessor.write(expected, bit_offset, 16, :INT, @data, :LITTLE_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit unsigned integers" do
          expected_array = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 32, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit signed integers" do
          expected_array = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          index = 0
          0.step((@data.length - 1) * 8, 32) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**32) if expected >= 2**31
            BinaryAccessor.write(expected_array[index], bit_offset, 32, :INT, @data, :LITTLE_ENDIAN, :ERROR_ALLOW_HEX)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit floats" do
          expected_array = [-7.670445e-037, -2.024055e-034, 2.658460e-032, 7.003653e-030]
          BinaryAccessor.write(expected_array[0], 0,  32, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[1], 32, 32, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[2], 64, 32, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[3], 96, 32, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-037).of(expected_array[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-034).of(expected_array[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-032).of(expected_array[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-030).of(expected_array[3])
        end

        it "writes 37-bit unsigned integers" do
          BinaryAccessor.write(0x1584838281, 43,  37, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0C0B0A0900 >> 3, 96, 37, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x81\x82\x83\x84\x15\x00\x00\x00\x09\x0A\x0B\x0C\x00\x00\x00")
        end

        it "writes 37-bit signed integers" do
          BinaryAccessor.write(0x1584838281 - (2**37), 43,  37, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0C0B0A0900 >> 3, 96, 37, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x81\x82\x83\x84\x15\x00\x00\x00\x09\x0A\x0B\x0C\x00\x00\x00")
        end

        it "writes 63-bit unsigned integers" do
          BinaryAccessor.write(0x4786858483828180, 57,  63, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0F0E0D0C0B0A0900 >> 1, 120, 63, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x47\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes 63-bit signed integers" do
          BinaryAccessor.write(0x4786858483828180 - (2**63), 57, 63, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(0x0F0E0D0C0B0A0900 >> 1, 120, 63, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x80\x81\x82\x83\x84\x85\x86\x47\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes 67-bit unsigned integers" do
          BinaryAccessor.write(0x0F0E0D0C0B0A0900FF >> 5, 120, 67, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x00\x00\x00\x00\x00\x00\xE0\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes 67-bit signed integers" do
          BinaryAccessor.write(0x0F0E0D0C0B0A0900FF >> 5, 120, 67, :INT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x00\x00\x00\x00\x00\x00\xE0\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F")
        end

        it "writes aligned 64-bit unsigned integers" do
          expected_array = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            BinaryAccessor.write(expected_array[index], bit_offset, 64, :UINT, @data, :LITTLE_ENDIAN, :ERROR)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit signed integers" do
          expected_array = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          index = 0
          0.step((@data.length - 1) * 8, 64) do |bit_offset|
            expected = expected_array[index]
            expected = expected - (2**64) if expected >= 2**63
            BinaryAccessor.write(expected_array[index], bit_offset, 64, :INT, @data, :LITTLE_ENDIAN, :ERROR_ALLOW_HEX)
            index += 1
          end
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit floats" do
          expected_array = [-2.081577e-272, 3.691916e-236]
          BinaryAccessor.write(expected_array[0], 0,  64, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          BinaryAccessor.write(expected_array[1], 64, 64, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-272).of(expected_array[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-236).of(expected_array[1])
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.write(0.0, 1, 32, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.write(0.0, 0, 65, :FLOAT, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size is 65 but must be 32 or 64 for data_type FLOAT")
        end
      end # given little endian data

      describe "should support overflow types" do
        before(:each) do
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        end

        it "prevents overflow of STRING" do
          expect { BinaryAccessor.write("abcde", 0, 32, :STRING, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of 5 bytes does not fit into 4 bytes for data_type STRING")
        end

        it "prevents overflow of BLOCK" do
          expect { BinaryAccessor.write("abcde", 0, 32, :BLOCK, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of 5 bytes does not fit into 4 bytes for data_type BLOCK")
        end

        it "prevents overflow of 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -(value + 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -(value + 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -(value + 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -(value + 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -(value + 1)
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -1
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -1
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -1
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -1
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
          value = -1
          expect { BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "truncates STRING" do
          BinaryAccessor.write("abcde", 0, 32, :STRING, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(@data[0..4]).to eql "abcd\x00"
        end

        it "truncates BLOCK" do
          BinaryAccessor.write("abcde", 0, 32, :BLOCK, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(@data[0..4]).to eql "abcd\x00"
        end

        it "truncates 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "saturates 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -(value + 1); saturated_value = value + 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -(value + 1); saturated_value = value + 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -(value + 1); saturated_value = value + 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -(value + 1); saturated_value = value + 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -(value + 1); saturated_value = value + 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -1; saturated_value = 0
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -1; saturated_value = 0
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -1; saturated_value = 0
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -1; saturated_value = 0
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
          value = -1; saturated_value = 0
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "allows hex value entry of 8-bit INT" do
          bit_size = 8; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 16-bit INT" do
          bit_size = 16; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 32-bit INT" do
          bit_size = 32; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 64-bit INT" do
          bit_size = 64; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 3-bit INT" do
          bit_size = 3; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write(value, 0, bit_size, data_type, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end
      end
    end # describe "write"

    describe "write_array" do
      before(:each) do
        @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        @data_array = []
        @data.length.times { |i| @data_array << @data[i] }
        @baseline_data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
        @baseline_data_array = []
        @baseline_data.length.times { |i| @baseline_data_array << @baseline_data[i] }
      end

      it "complains about value other than Array" do
        expect { BinaryAccessor.write_array("", 0, 32, :STRING, 0, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "values must be an Array type class is String")
      end

      it "complains about unknown data types" do
        expect { BinaryAccessor.write_array([0], 0, 32, :BLOB, 0, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "data_type BLOB is not recognized")
      end

      it "complains about bit_offsets before the beginning of the buffer" do
        expect { BinaryAccessor.write_array([''], -((@data.length * 8) + 8), 32, :STRING, 0, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "#{@data.length} byte buffer insufficient to write STRING at bit_offset #{-((@data.length * 8) + 8)} with bit_size 32")
      end

      it "writes if a negative bit_offset is equal to length of buffer" do
        BinaryAccessor.write_array(@baseline_data_array, -(@data.length * 8), 8, :BLOCK, @baseline_data_array.length * 8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql @baseline_data
      end

      it "complains about a negative or zero bit_size" do
        expect { BinaryAccessor.write_array([''], 0, 0, :STRING, 0, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size 0 must be positive for arrays")
        expect { BinaryAccessor.write_array([''], 0, -8, :STRING, 0, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size -8 must be positive for arrays")
      end

      it "writes aligned strings with fixed array_size" do
        data = @data.clone
        BinaryAccessor.write_array(@baseline_data_array, 0, 8, :STRING, @baseline_data_array.length * 8, data, :BIG_ENDIAN, :ERROR)
        expect(data).to eql(@baseline_data)
      end

      it "writes aligned strings with zero array_size" do
        BinaryAccessor.write_array(@baseline_data_array, 0, 8, :STRING, 0, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(@baseline_data)
      end

      it "writes strings with negative bit_offsets" do
        BinaryAccessor.write_array(@baseline_data_array[14..15], -16, 8, :STRING, 16, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(("\x00" * 14) + @baseline_data[14..15])
      end

      it "complains about unaligned strings" do
        expect { BinaryAccessor.write_array([], 1, 32, :STRING, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type STRING")
      end

      it "complains if pass more values than the given array_size can hold" do
        expect { BinaryAccessor.write_array(@baseline_data_array, 0, 8, :BLOCK, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "too many values #{@baseline_data_array.length} for given array_size 32 and bit_size 8")
      end

      it "writes blocks with fixed array_size" do
        BinaryAccessor.write_array(@baseline_data_array, 0, 8, :BLOCK, @baseline_data_array.length * 8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(@baseline_data)
      end

      it "zeros fill if array_size > number of values passed" do
        data = @baseline_data.clone
        BinaryAccessor.write_array(["\x01", "\x02", "\x03", "\x04"], 0, 8, :BLOCK, 64, @baseline_data, :BIG_ENDIAN, :ERROR)
        expect(@baseline_data).to eql("\x01\x02\x03\x04" + ("\x00" * 4) + data[8..-1])
      end

      it "writes blocks with fixed array_size at non zero offset" do
        BinaryAccessor.write_array(@baseline_data_array[0..-5], 32, 8, :BLOCK, (@baseline_data_array.length * 8) - 32, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(("\x00" * 4) + @baseline_data[0..-5])
      end

      it "writes blocks with zero array_size" do
        BinaryAccessor.write_array(@baseline_data_array, 0, 8, :BLOCK, 0, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(@baseline_data)
      end

      it "writes blocks with negative bit_offsets" do
        BinaryAccessor.write_array(["\x80\x81", "\x82\x83"], -32, 16, :BLOCK, 32, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(("\x00" * 12) + @baseline_data[0..3])
      end

      it "complains with a pos array_size not a multiple of bit_size" do
        @data.unpack('C*')
        expect { BinaryAccessor.write_array([1, 2], 0, 8, :UINT, 10, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "array_size 10 not a multiple of bit_size 8")
      end

      it "complains with a neg array_size not a multiple of bit_size" do
        @data.unpack('C*')
        expect { BinaryAccessor.write_array([1, 2], 0, 8, :UINT, -10, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "array_size -10 not a multiple of bit_size 8")
      end

      it "excludes the remaining bits if array_size is negative" do
        data = @data.clone
        BinaryAccessor.write_array(@baseline_data_array[0..-5], 0, 8, :BLOCK, -32, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql(@baseline_data[0..-5] + data[-4..-1])
      end

      it "does not write if the offset equals the negative array size" do
        data = @data.clone
        BinaryAccessor.write_array([], (@data.length * 8) - 32, 8, :BLOCK, -32, @data, :LITTLE_ENDIAN, :ERROR)
        expect(@data).to eql(data)
      end

      it "expands the buffer to handle negative array size" do
        @data = "\x00\x01\x02\x00\x03"
        BinaryAccessor.write_array([1, 2, 3, 4], 0, 32, :UINT, -8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x03")
      end

      it "shrinks the buffer when handling negative array size" do
        # Start with one array item
        @data = "\x00\x01\x02\x00\x03"
        # Goto 4 array items array item
        BinaryAccessor.write_array([1, 2, 3, 4], 0, 32, :UINT, -8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x03")
        # Goto 2 array items
        BinaryAccessor.write_array([1, 2], 0, 32, :UINT, -8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x01\x00\x00\x00\x02\x03")
        # Goto 0 array items
        BinaryAccessor.write_array([], 0, 32, :UINT, -8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x03")
        # Go back to 1 array items
        BinaryAccessor.write_array([1], 0, 32, :UINT, -8, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x00\x00\x01\x03")
      end

      it "complain when passed a zero length buffer" do
        expect { BinaryAccessor.write_array([1, 2, 3], 0, 8, :UINT, 32, "", :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError)
      end

      it "expands the buffer if the offset is greater than the negative array size" do
        offset = (@data.length * 8) - 16
        data = @data.clone
        BinaryAccessor.write_array([1, 2], offset, 8, :UINT, -32, @data, :LITTLE_ENDIAN, :ERROR)
        expect(@data).to eql(data[0..-3] + "\x01\x02" + data[-4..-1])
      end

      it "complains with negative bit_offset and zero array_size" do
        expect { BinaryAccessor.write_array([1, 2], -32, 8, :UINT, 0, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "negative or zero array_size (0) cannot be given with negative bit_offset (-32)")
      end

      it "complains with negative array_size" do
        expect { BinaryAccessor.write_array([1, 2], -32, 8, :UINT, -8, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "negative or zero array_size (-8) cannot be given with negative bit_offset (-32)")
      end

      it "writes a shorter string and zero fill to the given bit size" do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
        BinaryAccessor.write_array(["\x01\x02", "\x01\x02", "\x01\x02", "\x01\x02"], 0, 32, :STRING, 128, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00")
      end

      it "writes a shorter string and zero fill to the given bit size" do
        @data = "\x80\x81\x82\x83\x84\x85\x86\x87\x00\x09\x0A\x0B\x0C\x0D\x0E\x0F"
        BinaryAccessor.write_array(["\x01\x02", "\x01\x02", "\x01\x02", "\x01\x02"], 0, 32, :BLOCK, 128, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00")
      end

      it "complains about unaligned blocks" do
        expect { BinaryAccessor.write_array(@baseline_data_array[0..1], 7, 16, :BLOCK, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 7 is not byte aligned for data_type BLOCK")
      end

      it "complains if write exceeds the size of the buffer" do
        expect { BinaryAccessor.write_array([], 8, 800, :STRING, 800, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "16 byte buffer insufficient to write STRING at bit_offset 8 with bit_size 800")
      end

      it "writes aligned 8-bit unsigned integers" do
        BinaryAccessor.write_array([0, 1, 2, 3, 4, 5, 255, 255], 0, 8, :UINT, 0, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x01\x02\x03\x04\x05\xFF\xFF")
      end

      it "writes aligned 8-bit signed integers" do
        BinaryAccessor.write_array([0, 1, 2, 3, 4, 5, -1, 127], 0, 8, :INT, 0, @data, :BIG_ENDIAN, :ERROR)
        expect(@data).to eql("\x00\x01\x02\x03\x04\x05\xFF\x7F")
      end

      it "complains about unaligned strings" do
        expect { BinaryAccessor.write_array(['X'], 1, 32, :STRING, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type STRING")
      end

      it "writes STRING items" do
        BinaryAccessor.write_array(['a'], 0, 64, :STRING, 0, @baseline_data, :BIG_ENDIAN, :ERROR)
        expect(@baseline_data).to eql("a\x00\x00\x00\x00\x00\x00\x00")
      end

      it "writes BLOCK items" do
        BinaryAccessor.write_array(["\x01", "\x02", "\x03", "\x04"], 0, 32, :BLOCK, 0, @baseline_data, :BIG_ENDIAN, :ERROR)
        expect(@baseline_data).to eql("\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00")
      end

      it "writes variable length arrays with a zero and negative array_size" do
        baseline_data_array_uint8 = []
        @baseline_data.length.times { |i| baseline_data_array_uint8 << @baseline_data[i].ord }
        0.step(-(@baseline_data.length * 8), -8) do |array_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = @baseline_data.clone + ("\x00" * -(array_size / 8))
          BinaryAccessor.write_array(baseline_data_array_uint8, 0, 8, :UINT, array_size, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes variable length arrays or 32-bit UINTS with a zero and negative array_size" do
        baseline_data = "\x01\x01\x01\x01\x02\x02\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04"
        data_array_uint32 = [0x01010101, 0x02020202, 0x03030303, 0x04040404]
        0.step(-(baseline_data.length * 8), -8) do |array_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = baseline_data.clone + ("\x00" * -(array_size / 8))
          BinaryAccessor.write_array(data_array_uint32, 0, 32, :UINT, array_size, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes variable length arrays of 32-bit UINTS with a zero and negative array_size and non-zero bit offset" do
        baseline_data = "\x01\x01\x01\x01\x02\x02\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04"
        data_array_uint32 = [0x01010101, 0x02020202, 0x03030303, 0x04040404]
        0.step(-(baseline_data.length * 8), -8) do |array_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + baseline_data.clone + ("\x00" * -(array_size / 8))
          BinaryAccessor.write_array(data_array_uint32, 128, 32, :UINT, array_size, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      it "writes variable length arrays of 32-bit UINTS with a zero and negative array_size and non-zero bit offset and grow the buffer" do
        baseline_data = "\x01\x01\x01\x01\x02\x02\x02\x02\x03\x03\x03\x03\x04\x04\x04\x04"
        data_array_uint32 = [0x01010101, 0x02020202, 0x03030303, 0x04040404]
        0.step(-(baseline_data.length * 8), -8) do |array_size|
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
          @expected_data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" + baseline_data.clone + ("\x00" * -(array_size / 8))
          BinaryAccessor.write_array(data_array_uint32, 128, 32, :UINT, array_size, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@expected_data)
        end
      end

      describe "given big endian data" do
        it "writes 1-bit unsigned integers" do
          BinaryAccessor.write_array([1, 0, 1], 8, 1, :UINT, 3, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 1-bit signed integers" do
          BinaryAccessor.write_array([1, 0, 1], 8, 1, :INT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0")
        end

        it "writes 7-bit unsigned integers" do
          BinaryAccessor.write_array([0x40, 0x60, 0x50], 8, 7, :UINT, 21, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\x81\x82\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes aligned 16-bit unsigned integers" do
          data = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          BinaryAccessor.write_array(data, 0, 16, :UINT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 16-bit signed integers" do
          data = [0x8081, 0x8283, 0x8485, 0x8687, 0x0009, 0x0A0B, 0x0C0D, 0x0E0F]
          data.map! { |x| (x & ~(1 << 15)) - (x & (1 << 15)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 16, :INT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit unsigned integers" do
          data = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          BinaryAccessor.write_array(data, 0, 32, :UINT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit signed integers" do
          data = [0x80818283, 0x84858687, 0x00090A0B, 0x0C0D0E0F]
          data.map! { |x| (x & ~(1 << 31)) - (x & (1 << 31)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 32, :INT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit floats" do
          data = [-1.189360e-038, -3.139169e-036, 8.301067e-040, 1.086646e-031]
          BinaryAccessor.write_array(data, 0, 32, :FLOAT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-038).of(data[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-036).of(data[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-040).of(data[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-031).of(data[3])
        end

        it "writes aligned 64-bit unsigned integers" do
          data = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          BinaryAccessor.write_array(data, 0, 64, :UINT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit signed integers" do
          data = [0x8081828384858687, 0x00090A0B0C0D0E0F]
          data.map! { |x| (x & ~(1 << 63)) - (x & (1 << 63)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 64, :INT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit floats" do
          data = [-3.116851e-306, 1.257060e-308]
          BinaryAccessor.write_array(data, 0, 64, :FLOAT, 0, @data, :BIG_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-306).of(data[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :BIG_ENDIAN)).to be_within(1.0e-308).of(data[1])
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.write_array([0.0], 17, 32, :FLOAT, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 17 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.write_array([0.0], 0, 33, :FLOAT, 33, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size is 33 but must be 32 or 64 for data_type FLOAT")
        end
      end # given big endian data

      describe "given little endian data" do
        it "writes 1-bit unsigned integers" do
          BinaryAccessor.write_array([1, 0, 1], 8, 1, :UINT, 3, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
        end

        it "writes 1-bit signed integers" do
          BinaryAccessor.write_array([1, 0, 1], 8, 1, :INT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql("\x00\xA0")
        end

        it "complains about little endian bit-fields greater than 1-bit" do
          expect { BinaryAccessor.write_array([0x40, 0x60, 0x50], 8, 7, :UINT, 21, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "write_array does not support little endian bit fields with bit_size greater than 1-bit")
        end

        it "writes aligned 16-bit unsigned integers" do
          data = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          BinaryAccessor.write_array(data, 0, 16, :UINT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 16-bit signed integers" do
          data = [0x8180, 0x8382, 0x8584, 0x8786, 0x0900, 0x0B0A, 0x0D0C, 0x0F0E]
          data.map! { |x| (x & ~(1 << 15)) - (x & (1 << 15)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 16, :INT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit unsigned integers" do
          data = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          BinaryAccessor.write_array(data, 0, 32, :UINT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit signed integers" do
          data = [0x83828180, 0x87868584, 0x0B0A0900, 0x0F0E0D0C]
          data.map! { |x| (x & ~(1 << 31)) - (x & (1 << 31)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 32, :INT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 32-bit floats" do
          data = [-7.670445e-037, -2.024055e-034, 2.658460e-032, 7.003653e-030]
          BinaryAccessor.write_array(data, 0, 32, :FLOAT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-037).of(data[0])
          expect(BinaryAccessor.read(32, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-034).of(data[1])
          expect(BinaryAccessor.read(64, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-032).of(data[2])
          expect(BinaryAccessor.read(96, 32, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-030).of(data[3])
        end

        it "writes aligned 64-bit unsigned integers" do
          data = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          BinaryAccessor.write_array(data, 0, 64, :UINT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit signed integers" do
          data = [0x8786858483828180, 0x0F0E0D0C0B0A0900]
          data.map! { |x| (x & ~(1 << 63)) - (x & (1 << 63)) } # convert to negative
          BinaryAccessor.write_array(data, 0, 64, :INT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(@data).to eql(@baseline_data)
        end

        it "writes aligned 64-bit floats" do
          data = [-2.081577e-272, 3.691916e-236]
          BinaryAccessor.write_array(data, 0, 64, :FLOAT, 0, @data, :LITTLE_ENDIAN, :ERROR)
          expect(BinaryAccessor.read(0,  64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-272).of(data[0])
          expect(BinaryAccessor.read(64, 64, :FLOAT, @data, :LITTLE_ENDIAN)).to be_within(1.0e-236).of(data[1])
        end

        it "complains about unaligned floats" do
          expect { BinaryAccessor.write_array([0.0], 1, 32, :FLOAT, 32, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_offset 1 is not byte aligned for data_type FLOAT")
        end

        it "complains about mis-sized floats" do
          expect { BinaryAccessor.write_array([0.0], 0, 65, :FLOAT, 65, @data, :LITTLE_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "bit_size is 65 but must be 32 or 64 for data_type FLOAT")
        end
      end # given little endian data

      describe "should support overflow types" do
        before(:each) do
          @data = "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
        end

        it "prevents overflow of STRING" do
          expect { BinaryAccessor.write_array(["abcde"], 0, 32, :STRING, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of 5 bytes does not fit into 4 bytes for data_type STRING")
        end

        it "prevents overflow of BLOCK" do
          expect { BinaryAccessor.write_array(["abcde"], 0, 32, :BLOCK, 32, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of 5 bytes does not fit into 4 bytes for data_type BLOCK")
        end

        it "prevents overflow of 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1)
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "prevents overflow of 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = 2**bit_size
          expect { BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR) }.to raise_error(ArgumentError, "value of #{value} invalid for #{bit_size}-bit #{data_type}")
        end

        it "truncates STRING" do
          BinaryAccessor.write_array(["abcde"], 0, 32, :STRING, 32, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(@data[0..4]).to eql "abcd\x00"
        end

        it "truncates BLOCK" do
          BinaryAccessor.write_array(["abcde"], 0, 32, :BLOCK, 32, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(@data[0..4]).to eql "abcd\x00"
        end

        it "truncates 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1); truncated_value = -value
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "truncates 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = (2**bit_size) + 1; truncated_value = 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :TRUNCATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql truncated_value
        end

        it "saturates 8-bit INT" do
          bit_size = 8; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 16-bit INT" do
          bit_size = 16; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 32-bit INT" do
          bit_size = 32; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 64-bit INT" do
          bit_size = 64; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 3-bit INT" do
          bit_size = 3; data_type = :INT; value = 2**(bit_size - 1); saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 8-bit UINT" do
          bit_size = 8; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 16-bit UINT" do
          bit_size = 16; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 32-bit UINT" do
          bit_size = 32; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 64-bit UINT" do
          bit_size = 64; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "saturates 3-bit UINT" do
          bit_size = 3; data_type = :UINT; value = 2**bit_size; saturated_value = value - 1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :SATURATE)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql saturated_value
        end

        it "allows hex value entry of 8-bit INT" do
          bit_size = 8; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 16-bit INT" do
          bit_size = 16; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 32-bit INT" do
          bit_size = 32; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 64-bit INT" do
          bit_size = 64; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end

        it "allows hex value entry of 3-bit INT" do
          bit_size = 3; data_type = :INT; value = (2**bit_size) - 1; allowed_value = -1
          BinaryAccessor.write_array([value], 0, bit_size, data_type, bit_size, @data, :BIG_ENDIAN, :ERROR_ALLOW_HEX)
          expect(BinaryAccessor.read(0, bit_size, data_type, @data, :BIG_ENDIAN)).to eql allowed_value
        end
      end
    end # describe "write_array"
  end # describe BinaryAccessor
end
