ronin-rb/ronin-support

View on GitHub
lib/ronin/support/binary/buffer.rb

Summary

Maintainability
D
2 days
Test Coverage
# frozen_string_literal: true
#
# Copyright (c) 2006-2023 Hal Brodigan (postmodern.mod3 at gmail.com)
#
# ronin-support is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ronin-support 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with ronin-support.  If not, see <https://www.gnu.org/licenses/>.
#

require 'ronin/support/binary/memory'
require 'ronin/support/binary/byte_slice'
require 'ronin/support/binary/ctypes/mixin'

module Ronin
  module Support
    module Binary
      #
      # Represents a binary buffer of data.
      #
      # ## Examples
      #
      # Writing bytes into an empty buffer:
      #
      #     buffer = Binary::Buffer.new(10)
      #     # => #<Ronin::Support::Binary::Buffer: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00">
      #     buffer[0] = 0x41
      #     buffer[1] = 0x42
      #     buffer[2] = 0x43
      #     buffer.to_s
      #     # => "ABC\x00\x00\x00\x00\x00\x00\x00"
      #
      # Writing different types of data to a buffer:
      #
      #     buffer = Binary::Buffer.new(16)
      #     # => #<Ronin::Support::Binary::Buffer: "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00">
      #     buffer.put_uint32(0,0x11223344)
      #     buffer.put_int32(4,-1)
      #     buffer.put_string(8,"ABC")
      #     buffer.put_float32(12,0.5)
      #     buffer.to_s
      #     # => "D3\"\x11\xFF\xFF\xFF\xFFABC\x00\x00\x00\x00?"
      #
      # Creating a buffer from an existing String:
      #
      #     buffer = Binary::Buffer.new("\x41\x00\x00\x00\x42\x00\x00\x00")
      #     # => #<Ronin::Support::Binary::Buffer: "A\u0000\u0000\u0000B\u0000\u0000\u0000">
      #     buffer.get_uint32(0)
      #     # => 65
      #     buffer.get_uint32(4)
      #     # => 66
      #
      # @api public
      #
      # @since 1.0.0
      #
      class Buffer < Memory

        include CTypes::Mixin

        #
        # Initializes the buffer.
        #
        # @param [Integer, String, ByteSlice] length_or_string
        #   The size of the buffer or an existing String which will be used
        #   as the underlying buffer.
        #
        # @param [Hash{Symbol => Object}] kwargs
        #   Additional keyword arguments.
        #
        # @option kwargs [:little, :big, :net, nil] :endian
        #   The desired endianness of the values within the buffer.
        #
        # @option kwargs [:x86, :x86_64,
        #                 :ppc, :ppc64,
        #                 :mips, :mips_le, :mips_be,
        #                 :mips64, :mips64_le, :mips64_be,
        #                 :arm, :arm_le, :arm_be,
        #                 :arm64, :arm64_le, :arm64_be] :arch
        #   The desired architecture for the values within the buffer.
        #
        # @raise [ArgumentError]
        #   Either the `length_or_string` argument was not an Integer or a
        #   String.
        #
        def initialize(length_or_string, **kwargs)
          initialize_type_system(**kwargs)

          super(length_or_string)
        end

        #
        # Reads from the IO stream and returns a buffer.
        #
        # @param [IO] io
        #   The IO object to read from.
        #
        # @param [Integer] size
        #   The size of the buffer to read.
        #
        # @return [Buffer]
        #   The read buffer.
        #
        # @see #read_from
        #
        # @api public
        #
        def self.read_from(io,size)
          new(io.read(size))
        end

        alias length size

        #
        # Writes a value to the buffer at the given index.
        #
        # @param [Integer, Range<Integer,Integer>] index_or_range
        #   The index within the string to write to.
        #
        # @param [Integer, nil] length
        #   Optional additional length argument.
        #
        # @param [String] value
        #   The integer, float, or character value to write to the buffer.
        #
        # @return [String]
        #   The string written into the buffer.
        #
        # @example Writing a single byte:
        #   buffer[0] = 0x41
        #
        # @example Writing a single char:
        #   buffer[0] = 'A'
        #
        # @example Writing an Array of bytes to the given range of indexes:
        #   buffer[0..3] = [0x41, 0x42, 0x43]
        #
        # @example Writing an Array of chars to the given range of indexes:
        #   buffer[0..3] = ['A', 'B', 'C']
        #
        # @example Writing an Array of bytes to the given index and length:
        #   buffer[0,3] = [0x41, 0x42, 0x43]
        #
        # @example Writing an Array of bytes to the given index and length:
        #   buffer[0,3] = ['A', 'B', 'C']
        #
        def []=(index_or_range,length=nil,value)
          value = case value
                  when Integer
                    value.chr(Encoding::ASCII_8BIT)
                  when ::Array
                    value.map { |char_or_byte|
                      case char_or_byte
                      when Integer
                        char_or_byte.chr(Encoding::ASCII_8BIT)
                      else
                        char_or_byte
                      end
                    }.join
                  else
                    value
                  end

          super(index_or_range,length,value)
        end

        #
        # Converts the buffer to a String.
        #
        # @return [String]
        #   The raw binary buffer.
        #
        def to_s
          @string.to_s
        end

        alias to_str to_s

        #
        # @group Reader Methods
        #

        #
        # Reads a value of the given type at the given offset.
        #
        # @param [Symbol] type
        #   The type of the value to read.
        #
        # @param [Integer] offset
        #   The offset within the buffer to read.
        #
        # @return [Integer, Float, String]
        #   The decoded value.
        #
        def get(type,offset)
          type = @type_system[type]

          if (offset < 0) || ((offset + type.size) > size)
            raise(IndexError,"offset #{offset} is out of bounds: 0...#{size - type.size}")
          end

          data = @string[offset,type.size]
          return type.unpack(data)
        end

        #
        # Alias for `get(:byte,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `byte` within the buffer.
        #
        # @return [Integer]
        #   The read `byte`.
        #
        # @see #get
        #
        def get_byte(offset)
          get(:byte,offset)
        end

        #
        # Alias for `get(:char,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `char` within the buffer.
        #
        # @return [String]
        #   The read `char`.
        #
        # @see #get
        #
        def get_char(offset)
          get(:char,offset)
        end

        #
        # Alias for `get(:uchar,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uchar` within the buffer.
        #
        # @return [String]
        #   The read `uchar`.
        #
        # @see #get
        #
        def get_uchar(offset)
          get(:uchar,offset)
        end

        #
        # Reads a null-byte terminated C string from the buffer, at the given
        # offset.
        #
        # @param [Integer] offset
        #   The offset of the `string` within the buffer.
        #
        # @param [Integer, nil] length
        #   The optional maximum desired length of the string.
        #
        # @return [String]
        #   The read C string, without the null-byte.
        #
        def get_string(offset,length=nil)
          if (offset < 0) || (offset >= size)
            raise(IndexError,"offset #{offset} is out of bounds: 0...#{size - 1}")
          elsif (length && (offset + length) > size)
            raise(IndexError,"offset #{offset} or length #{length} is out of bounds: 0...#{size - 1}")
          end

          if length
            substring = @string[offset,length]

            if (null_byte_index = substring.index("\0"))
              substring[0...null_byte_index]
            else
              substring
            end
          else
            if (null_byte_index = @string.index("\0",offset))
              @string[offset...null_byte_index]
            else
              @string[offset..]
            end
          end
        end

        #
        # Alias for `get(:int8,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `int8` within the buffer.
        #
        # @return [Integer]
        #   The read `int8`.
        #
        # @see #get
        #
        def get_int8(offset)
          get(:int8,offset)
        end

        #
        # Alias for `get(:int16,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `int16` within the buffer.
        #
        # @return [Integer]
        #   The read `int16`.
        #
        # @see #get
        #
        def get_int16(offset)
          get(:int16,offset)
        end

        #
        # Alias for `get(:int32,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `int32` within the buffer.
        #
        # @return [Integer]
        #   The read `int32`.
        #
        # @see #get
        #
        def get_int32(offset)
          get(:int32,offset)
        end

        #
        # Alias for `get(:int64,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `int64` within the buffer.
        #
        # @return [Integer]
        #   The read `int64`.
        #
        # @see #get
        #
        def get_int64(offset)
          get(:int64,offset)
        end

        #
        # Alias for `get(:uint8,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint8` within the buffer.
        #
        # @return [Integer]
        #   The read `uint8`.
        #
        # @see #get
        #
        def get_uint8(offset)
          get(:uint8,offset)
        end

        #
        # Alias for `get(:uint16,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint16` within the buffer.
        #
        # @return [Integer]
        #   The read `uint16`.
        #
        # @see #get
        #
        def get_uint16(offset)
          get(:uint16,offset)
        end

        #
        # Alias for `get(:uint32,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint32` within the buffer.
        #
        # @return [Integer]
        #   The read `uint32`.
        #
        # @see #get
        #
        def get_uint32(offset)
          get(:uint32,offset)
        end

        #
        # Alias for `get(:uint64,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint64` within the buffer.
        #
        # @return [Integer]
        #   The read `uint64`.
        #
        # @see #get
        #
        def get_uint64(offset)
          get(:uint64,offset)
        end

        #
        # Alias for `get(:short,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `short` within the buffer.
        #
        # @return [Integer]
        #   The read `short`.
        #
        # @see #get
        #
        def get_short(offset)
          get(:short,offset)
        end

        #
        # Alias for `get(:int,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `int` within the buffer.
        #
        # @return [Integer]
        #   The read `int`.
        #
        # @see #get
        #
        def get_int(offset)
          get(:int,offset)
        end

        #
        # Alias for `get(:long,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `long` within the buffer.
        #
        # @return [Integer]
        #   The read `long`.
        #
        # @see #get
        #
        def get_long(offset)
          get(:long,offset)
        end

        #
        # Alias for `get(:long_long,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `long_long` within the buffer.
        #
        # @return [Integer]
        #   The read `long_long`.
        #
        # @see #get
        #
        def get_long_long(offset)
          get(:long_long,offset)
        end

        #
        # Alias for `get(:ushort,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `ushort` within the buffer.
        #
        # @return [Integer]
        #   The read `ushort`.
        #
        # @see #get
        #
        def get_ushort(offset)
          get(:ushort,offset)
        end

        #
        # Alias for `get(:uint,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint` within the buffer.
        #
        # @return [Integer]
        #   The read `uint`.
        #
        # @see #get
        #
        def get_uint(offset)
          get(:uint,offset)
        end

        #
        # Alias for `get(:ulong,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `ulong` within the buffer.
        #
        # @return [Integer]
        #   The read `ulong`.
        #
        # @see #get
        #
        def get_ulong(offset)
          get(:ulong,offset)
        end

        #
        # Alias for `get(:ulong_long,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `ulong_long` within the buffer.
        #
        # @return [Integer]
        #   The read `ulong_long`.
        #
        # @see #get
        #
        def get_ulong_long(offset)
          get(:ulong_long,offset)
        end

        #
        # Alias for `get(:float32,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `float32` within the buffer.
        #
        # @return [Float]
        #   The read `float32`.
        #
        # @see #get
        #
        def get_float32(offset)
          get(:float32,offset)
        end

        #
        # Alias for `get(:float64,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `float64` within the buffer.
        #
        # @return [Float]
        #   The read `float64`.
        #
        # @see #get
        #
        def get_float64(offset)
          get(:float64,offset)
        end

        #
        # Alias for `get(:float,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `float` within the buffer.
        #
        # @return [Float]
        #   The read `float`.
        #
        # @see #get
        #
        def get_float(offset)
          get(:float,offset)
        end

        #
        # Alias for `get(:double,offset)`.
        #
        # @param [Integer] offset
        #   The offset of the `double` within the buffer.
        #
        # @return [Float]
        #   The read `double`.
        #
        # @see #get
        #
        def get_double(offset)
          get(:double,offset)
        end

        #
        # Returns the buffer starting at the given offset and with the given
        # size.
        #
        # @param [Integer] offset
        #   The offset within the buffer.
        #
        # @param [Integer] size
        #   The number of bytes for the buffer.
        #
        # @return [Buffer]
        #   The new buffer.
        #
        # @example
        #   subbuffer = buffer.buffer_at(10,40)
        #
        def buffer_at(offset,size)
          Buffer.new(byteslice(offset,size))
        end

        #
        # Returns the array starting at the given offset, with the given
        # length, containing the given type.
        #
        # @param [Integer] offset
        #   The offset within the buffer that the array starts at.
        #
        # @param [Symbol] type
        #   The type name.
        #
        # @param [Integer] length
        #   The number of elements in the array.
        #
        # @return [Array]
        #   The new array.
        #
        # @example
        #   array = buffer.array_at(0,:uint32,10)
        #
        def array_at(offset,type,length)
          type = @type_system[type]
          size = type.size * length

          return Array.new(type,byteslice(offset,size))
        end

        #
        # Returns a new {Struct} instance starting at the given offset.
        #
        # @param [Integer] offset
        #   The offset within the buffer that the struct starts at.
        #
        # @param [Class<Binary::Struct>] struct_class
        #   The struct class.
        #
        # @return [Binary::Struct]
        #   The new struct instance.
        #
        # @example
        #   struct = buffer.struct_at(10,MyStruct)
        #   # => #<MyStruct: ...>
        #
        def struct_at(offset,struct_class)
          unless struct_class < Struct
            raise(ArgumentError,"the given class must be a #{Struct} subclass: #{struct_class.inspect}")
          end

          return struct_class.new(byteslice(offset,struct_class.size))
        end

        #
        # Returns a new {Union} instance starting at the given offset.
        #
        # @param [Integer] offset
        #   The offset within the buffer that the union starts at.
        #
        # @param [Class<Union>] union_class
        #   The union class.
        #
        # @return [Union]
        #   The new union instance.
        #
        # @example
        #   union = buffer.union_at(10,MyUnion)
        #   # => #<MyUnion: ...>
        #
        def union_at(offset,union_class)
          unless union_class < Union
            raise(ArgumentError,"the given class must be a #{Union} subclass: #{union_class.inspect}")
          end

          return union_class.new(byteslice(offset,union_class.size))
        end

        #
        # Reads an array of the given type, starting at the given offset, with
        # the given length.
        #
        # @param [Symbol] type
        #   The type of the value to read.
        #
        # @param [Integer] offset
        #   The offset that the array starts at within the buffer.
        #
        # @param [Integer] count
        #   The number of desired elements within the array.
        #
        # @return [::Array<Object>]
        #   The read array of types.
        #
        def get_array_of(type,offset,count)
          type       = @type_system[type]
          array_type = type[count]

          if (offset < 0) || ((offset + array_type.size) > size)
            raise(IndexError,"offset #{offset} or size #{array_type.size} is out of bounds: 0...#{size - type.size}")
          end

          slice = @string[offset,array_type.size]
          return array_type.unpack(slice)
        end

        #
        # Alias to `get_array_of(:byte,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of bytes to read.
        #
        # @return [::Array<Integer>]
        #   The read array of bytes.
        #
        # @see #get_array_of
        #
        def get_array_of_byte(offset,count)
          get_array_of(:byte,offset,count)
        end

        alias get_bytes get_array_of_byte

        #
        # Alias to `get_array_of(:char,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of chars to read.
        #
        # @return [::Array<Integer>]
        #   The read array of chars.
        #
        # @see #get_array_of
        #
        def get_array_of_char(offset,count)
          get_array_of(:char,offset,count)
        end

        alias get_chars get_array_of_char

        #
        # Alias to `get_array_of(:uchar,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of unsigned chars to read.
        #
        # @return [::Array<Integer>]
        #   The read array of unsigned chars.
        #
        # @see #get_array_of
        #
        def get_array_of_uchar(offset,count)
          get_array_of(:uchar,offset,count)
        end

        alias get_uchars get_array_of_uchar

        #
        # Alias to `get_array_of(:int8,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `int8` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `int8` values.
        #
        # @see #get_array_of
        #
        def get_array_of_int8(offset,count)
          get_array_of(:int8,offset,count)
        end

        #
        # Alias to `get_array_of(:int16,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `int16` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `int16` values.
        #
        # @see #get_array_of
        #
        def get_array_of_int16(offset,count)
          get_array_of(:int16,offset,count)
        end

        #
        # Alias to `get_array_of(:int32,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `int32` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `int32` values.
        #
        # @see #get_array_of
        #
        def get_array_of_int32(offset,count)
          get_array_of(:int32,offset,count)
        end

        #
        # Alias to `get_array_of(:int64,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `int64` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `int64` values.
        #
        # @see #get_array_of
        #
        def get_array_of_int64(offset,count)
          get_array_of(:int64,offset,count)
        end

        #
        # Alias to `get_array_of(:uint8,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `uint8` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `uint8` values.
        #
        # @see #get_array_of
        #
        def get_array_of_uint8(offset,count)
          get_array_of(:uint8,offset,count)
        end

        #
        # Alias to `get_array_of(:uint16,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `uint16` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `uint16` values.
        #
        # @see #get_array_of
        #
        def get_array_of_uint16(offset,count)
          get_array_of(:uint16,offset,count)
        end

        #
        # Alias to `get_array_of(:uint32,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `uint32` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `uint32` values.
        #
        # @see #get_array_of
        #
        def get_array_of_uint32(offset,count)
          get_array_of(:uint32,offset,count)
        end

        #
        # Alias to `get_array_of(:uint64,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `uint64` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `uint64` values.
        #
        # @see #get_array_of
        #
        def get_array_of_uint64(offset,count)
          get_array_of(:uint64,offset,count)
        end

        #
        # Alias to `get_array_of(:short,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `short` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `short` values.
        #
        # @see #get_array_of
        #
        def get_array_of_short(offset,count)
          get_array_of(:short,offset,count)
        end

        #
        # Alias to `get_array_of(:int,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `int` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `int` values.
        #
        # @see #get_array_of
        #
        def get_array_of_int(offset,count)
          get_array_of(:int,offset,count)
        end

        alias get_ints get_array_of_int

        #
        # Alias to `get_array_of(:long,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `long` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `long` values.
        #
        # @see #get_array_of
        #
        def get_array_of_long(offset,count)
          get_array_of(:long,offset,count)
        end

        #
        # Alias to `get_array_of(:long_long,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `long_long` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `long_long` values.
        #
        # @see #get_array_of
        #
        def get_array_of_long_long(offset,count)
          get_array_of(:long_long,offset,count)
        end

        #
        # Alias to `get_array_of(:ushort,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `ushort` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `ushort` values.
        #
        # @see #get_array_of
        #
        def get_array_of_ushort(offset,count)
          get_array_of(:ushort,offset,count)
        end

        #
        # Alias to `get_array_of(:uint,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `uint` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `uint` values.
        #
        # @see #get_array_of
        #
        def get_array_of_uint(offset,count)
          get_array_of(:uint,offset,count)
        end

        alias get_uints get_array_of_uint

        #
        # Alias to `get_array_of(:ulong,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `ulong` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `ulong` values.
        #
        # @see #get_array_of
        #
        def get_array_of_ulong(offset,count)
          get_array_of(:ulong,offset,count)
        end

        #
        # Alias to `get_array_of(:ulong_long,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `ulong_long` values to read.
        #
        # @return [::Array<Integer>]
        #   The read array of `ulong_long` values.
        #
        # @see #get_array_of
        #
        def get_array_of_ulong_long(offset,count)
          get_array_of(:ulong_long,offset,count)
        end

        #
        # Alias to `get_array_of(:float32,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `float32` values to read.
        #
        # @return [::Array<Float>]
        #   The read array of `float32` values.
        #
        # @see #get_array_of
        #
        def get_array_of_float32(offset,count)
          get_array_of(:float32,offset,count)
        end

        #
        # Alias to `get_array_of(:float64,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `float64` values to read.
        #
        # @return [::Array<Float>]
        #   The read array of `float64` values.
        #
        # @see #get_array_of
        #
        def get_array_of_float64(offset,count)
          get_array_of(:float64,offset,count)
        end

        #
        # Alias to `get_array_of(:float,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `float` values to read.
        #
        # @return [::Array<Float>]
        #   The read array of `float` values.
        #
        # @see #get_array_of
        #
        def get_array_of_float(offset,count)
          get_array_of(:float,offset,count)
        end

        alias get_floats get_array_of_float

        #
        # Alias to `get_array_of(:double,offset,count)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [Integer] count
        #   The number of `double` values to read.
        #
        # @return [::Array<Float>]
        #   The read array of `double` values.
        #
        # @see #get_array_of
        #
        def get_array_of_double(offset,count)
          get_array_of(:double,offset,count)
        end

        alias get_doubles get_array_of_double

        #
        # @group Writer Methods
        #

        #
        # Writes a value of the given type to the given offset.
        #
        # @param [Symbol] type
        #   The type of the value to write.
        #
        # @param [Integer] offset
        #   The offset within the buffer to write.
        #
        # @param [Integer, Float, String] value
        #   The value to write.
        #
        def put(type,offset,value)
          type = @type_system[type]

          if (offset < 0) || ((offset + type.size) > size)
            raise(IndexError,"offset #{offset} is out of bounds: 0...#{size - type.size}")
          end

          data = type.pack(value)

          @string[offset,type.size] = data
          return self
        end

        #
        # Alias for `put(:byte,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `byte` within the buffer.
        #
        # @param [Integer] value
        #   The `char` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_byte(offset,value)
          put(:byte,offset,value)
        end

        #
        # Alias for `put(:char,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `char` within the buffer.
        #
        # @param [String] value
        #   The `char` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_char(offset,value)
          put(:char,offset,value)
        end

        #
        # Writes a null-terminated C string into the buffer at the given
        # offset.
        #
        # @param [Integer] offset
        #   The offset to start writing the string into the buffer.
        #
        # @param [String] string
        #   The String to write into the buffer.
        #
        # @return [self]
        #
        def put_string(offset,string)
          ascii_string = string.encode(Encoding::ASCII_8BIT)
          cstring      = "#{ascii_string}\0"

          if (offset < 0) || ((offset + cstring.bytesize) >= size)
            raise(IndexError,"offset #{offset} or C string size #{cstring.bytesize} is out of bounds: 0...#{size - 1}")
          end

          @string[offset,cstring.bytesize] = cstring
          return self
        end

        #
        # Alias for `put(:uchar,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uchar` within the buffer.
        #
        # @param [String] value
        #   The `uchar` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uchar(offset,value)
          put(:uchar,offset,value)
        end

        #
        # Alias for `put(:int8,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `int8` within the buffer.
        #
        # @param [Integer] value
        #   The `int8` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_int8(offset,value)
          put(:int8,offset,value)
        end

        #
        # Alias for `put(:int16,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `int16` within the buffer.
        #
        # @param [Integer] value
        #   The `int16` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_int16(offset,value)
          put(:int16,offset,value)
        end

        #
        # Alias for `put(:int32,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `int32` within the buffer.
        #
        # @param [Integer] value
        #   The `int32` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_int32(offset,value)
          put(:int32,offset,value)
        end

        #
        # Alias for `put(:int64,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `int64` within the buffer.
        #
        # @param [Integer] value
        #   The `int64` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_int64(offset,value)
          put(:int64,offset,value)
        end

        #
        # Alias for `put(:uint8,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint8` within the buffer.
        #
        # @param [Integer] value
        #   The `uint8` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uint8(offset,value)
          put(:uint8,offset,value)
        end

        #
        # Alias for `put(:uint16,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint16` within the buffer.
        #
        # @param [Integer] value
        #   The `uint16` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uint16(offset,value)
          put(:uint16,offset,value)
        end

        #
        # Alias for `put(:uint32,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint32` within the buffer.
        #
        # @param [Integer] value
        #   The `uint32` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uint32(offset,value)
          put(:uint32,offset,value)
        end

        #
        # Alias for `put(:uint64,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint64` within the buffer.
        #
        # @param [Integer] value
        #   The `uint64` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uint64(offset,value)
          put(:uint64,offset,value)
        end

        #
        # Alias for `put(:short,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `short` within the buffer.
        #
        # @param [Integer] value
        #   The `short` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_short(offset,value)
          put(:short,offset,value)
        end

        #
        # Alias for `put(:int,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `int` within the buffer.
        #
        # @param [Integer] value
        #   The `int` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_int(offset,value)
          put(:int,offset,value)
        end

        #
        # Alias for `put(:long,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `long` within the buffer.
        #
        # @param [Integer] value
        #   The `long` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_long(offset,value)
          put(:long,offset,value)
        end

        #
        # Alias for `put(:long_long,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `long_long` within the buffer.
        #
        # @param [Integer] value
        #   The `long_long` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_long_long(offset,value)
          put(:long_long,offset,value)
        end

        #
        # Alias for `put(:ushort,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `ushort` within the buffer.
        #
        # @param [Integer] value
        #   The `ushort` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_ushort(offset,value)
          put(:ushort,offset,value)
        end

        #
        # Alias for `put(:uint,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `uint` within the buffer.
        #
        # @param [Integer] value
        #   The `uint` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_uint(offset,value)
          put(:uint,offset,value)
        end

        #
        # Alias for `put(:ulong,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `ulong` within the buffer.
        #
        # @param [Integer] value
        #   The `ulong` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_ulong(offset,value)
          put(:ulong,offset,value)
        end

        #
        # Alias for `put(:ulong_long,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `ulong_long` within the buffer.
        #
        # @param [Integer] value
        #   The `ulong_long` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_ulong_long(offset,value)
          put(:ulong_long,offset,value)
        end

        #
        # Alias for `put(:float32,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `float32` within the buffer.
        #
        # @param [Float] value
        #   The `float32` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_float32(offset,value)
          put(:float32,offset,value)
        end

        #
        # Alias for `put(:float64,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `float64` within the buffer.
        #
        # @param [Float] value
        #   The `float64` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_float64(offset,value)
          put(:float64,offset,value)
        end

        #
        # Alias for `put(:float,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `float` within the buffer.
        #
        # @param [Float] value
        #   The `float` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_float(offset,value)
          put(:float,offset,value)
        end

        #
        # Alias for `put(:double,offset,value)`.
        #
        # @param [Integer] offset
        #   The offset of the `double` within the buffer.
        #
        # @param [Float] value
        #   The `double` value to write into the buffer.
        #
        # @return [self]
        #
        # @see #put
        #
        def put_double(offset,value)
          put(:double,offset,value)
        end

        #
        # Writes an array of the given type, to the given offset within the
        # buffer.
        #
        # @param [Symbol] type
        #   The type of the value to write.
        #
        # @param [Integer] offset
        #   The offset that the array should start at within the buffer.
        #
        # @param [::Array<Object>] array
        #   The array of values to write.
        #
        # @return [self]
        #
        def put_array_of(type,offset,array)
          type       = @type_system[type]
          array_type = type[array.length]

          if (offset < 0) || ((offset + array_type.size) > size)
            raise(IndexError,"offset #{offset} or size #{array_type.size} is out of bounds: 0...#{size - type.size}")
          end

          data = array_type.pack(array)

          @string[offset,array_type.size] = data
          return self
        end

        #
        # Alias to `put_array_of(:byte,offset,bytes)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] bytes
        #   The array of bytes to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_byte(offset,bytes)
          put_array_of(:byte,offset,bytes)
        end

        alias put_bytes put_array_of_byte

        #
        # Alias to `put_array_of(:char,offset,bytes)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [String] chars
        #   The array of characters to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_char(offset,chars)
          put_array_of(:char,offset,chars)
        end

        alias put_chars put_array_of_char

        #
        # Alias to `put_array_of(:uchar,offset,bytes)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [String] chars
        #   The array of unsigned characters to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uchar(offset,chars)
          put_array_of(:uchar,offset,chars)
        end

        alias put_uchars put_array_of_uchar

        #
        # Alias to `put_array_of(:int8,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `int8` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_int8(offset,ints)
          put_array_of(:int8,offset,ints)
        end

        #
        # Alias to `put_array_of(:int16,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `int16` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_int16(offset,ints)
          put_array_of(:int16,offset,ints)
        end

        #
        # Alias to `put_array_of(:int32,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `int32` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_int32(offset,ints)
          put_array_of(:int32,offset,ints)
        end

        #
        # Alias to `put_array_of(:int64,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `int64` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_int64(offset,ints)
          put_array_of(:int64,offset,ints)
        end

        #
        # Alias to `put_array_of(:uint8,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `uint8` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uint8(offset,uints)
          put_array_of(:uint8,offset,uints)
        end

        #
        # Alias to `put_array_of(:uint16,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `uint16` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uint16(offset,uints)
          put_array_of(:uint16,offset,uints)
        end

        #
        # Alias to `put_array_of(:uint32,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `uint32` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uint32(offset,uints)
          put_array_of(:uint32,offset,uints)
        end

        #
        # Alias to `put_array_of(:uint64,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `uint64` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uint64(offset,uints)
          put_array_of(:uint64,offset,uints)
        end

        #
        # Alias to `put_array_of(:short,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `short` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_short(offset,ints)
          put_array_of(:short,offset,ints)
        end

        #
        # Alias to `put_array_of(:int,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `int` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_int(offset,ints)
          put_array_of(:int,offset,ints)
        end

        alias put_ints put_array_of_int

        #
        # Alias to `put_array_of(:long,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `long` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_long(offset,ints)
          put_array_of(:long,offset,ints)
        end

        #
        # Alias to `put_array_of(:long_long,offset,ints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] ints
        #   The array of `long_long` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_long_long(offset,ints)
          put_array_of(:long_long,offset,ints)
        end

        #
        # Alias to `put_array_of(:ushort,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `ushort` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_ushort(offset,uints)
          put_array_of(:ushort,offset,uints)
        end

        #
        # Alias to `put_array_of(:uint,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `uint` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_uint(offset,uints)
          put_array_of(:uint,offset,uints)
        end

        alias put_uints put_array_of_uint

        #
        # Alias to `put_array_of(:ulong,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `ulong` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_ulong(offset,uints)
          put_array_of(:ulong,offset,uints)
        end

        #
        # Alias to `put_array_of(:ulong_long,offset,uints)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Integer>] uints
        #   The array of `ulong_long` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_ulong_long(offset,uints)
          put_array_of(:ulong_long,offset,uints)
        end

        #
        # Alias to `put_array_of(:float32,offset,floats)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Float>] floats
        #   The array of `float32` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_float32(offset,floats)
          put_array_of(:float32,offset,floats)
        end

        #
        # Alias to `put_array_of(:float64,offset,floats)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Float>] floats
        #   The array of `float64` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_float64(offset,floats)
          put_array_of(:float64,offset,floats)
        end

        #
        # Alias to `put_array_of(:float,offset,floats)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Float>] floats
        #   The array of `float` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_float(offset,floats)
          put_array_of(:float,offset,floats)
        end

        alias put_floats put_array_of_float

        #
        # Alias to `put_array_of(:double,offset,floats)`.
        #
        # @param [Integer] offset
        #   The offset within the buffer to start reading at.
        #
        # @param [::Array<Float>] floats
        #   The array of `double` values to write.
        #
        # @return [self]
        #
        # @see #put_array_of
        #
        def put_array_of_double(offset,floats)
          put_array_of(:double,offset,floats)
        end

        alias put_doubles put_array_of_double

      end
    end
  end
end