#------------------------------------------------------------------------------
# Automatically generated by the Fast Binary Encoding compiler, do not modify!
# https://github.com/chronoxor/FastBinaryEncoding
# Source: FBE
# FBE version: 1.15.0.0
#------------------------------------------------------------------------------

# rubocop:disable Lint/MissingCopEnableDirective
# rubocop:disable Lint/UnneededCopDisableDirective
# rubocop:disable Metrics/AbcSize
# rubocop:disable Metrics/ClassLength
# rubocop:disable Metrics/CyclomaticComplexity
# rubocop:disable Metrics/LineLength
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/PerceivedComplexity

require 'base64'
require 'bigdecimal'
require 'json'
require 'set'
require 'uuidtools'

module FBE

  # Fast Binary Encoding enum utility module
  module Enum

    attr_reader :key, :value

    def initialize(key, value)
      @key = key
      @value = value
    end

    def self.included(base)
      base.extend Enumerable
      base.extend ClassMethods
    end

    module ClassMethods
      # Define an enumerated value.
      #
      # === Parameters
      # [key] Enumerator key.
      # [value] Enumerator value.
      def define(key, value)
        @_enum_hash ||= {}
        @_enums_by_value ||= {}

        validate_key!(key)
        store_new_instance(key, value)
        define_singleton_method(key) { value }
      end

      def store_new_instance(key, value)
        new_instance = new(key, value)
        @_enum_hash[key] = new_instance
        @_enums_by_value[value] = new_instance
      end

      def const_missing(key)
        raise "Constant is missing for key '#{key}' in enum '#{name}'"
      end

      # Iterate over all enumerated values.
      # Required for Enumerable mixin
      def each(&block)
        @_enum_hash.each(&block)
      end

      # Attempt to parse an enum key and return the
      # corresponding value.
      #
      # === Parameters
      # [k] The key string to parse.
      #
      # Returns the corresponding value or nil.
      def parse(k)
        k = k.to_s.upcase
        each do |key, enum|
          return enum.value if key.to_s.upcase == k
        end
        nil
      end

      # Whether the specified key exists in this enum.
      #
      # === Parameters
      # [k] The string key to check.
      #
      # Returns true if the key exists, false otherwise.
      def key?(k)
        @_enum_hash.key?(k)
      end

      # Gets the string value for the specified key.
      #
      # === Parameters
      # [k] The key symbol to get the value for.
      #
      # Returns the corresponding enum instance or nil.
      def value(k)
        enum = @_enum_hash[k]
        enum.value if enum
      end

      # Whether the specified value exists in this enum.
      #
      # === Parameters
      # [k] The string value to check.
      #
      # Returns true if the value exists, false otherwise.
      def value?(v)
        @_enums_by_value.key?(v)
      end

      # Gets the key symbol for the specified value.
      #
      # === Parameters
      # [v] The string value to parse.
      #
      # Returns the corresponding key symbol or nil.
      def key(v)
        enum = @_enums_by_value[v]
        enum.key if enum
      end

      # Returns all enum keys.
      def keys
        @_enum_hash.values.map(&:key)
      end

      # Returns all enum values.
      def values
        @_enum_hash.values.map(&:value)
      end

      def to_h
        Hash[@_enum_hash.map do |key, enum|
          [key, enum.value]
        end]
      end

      private

      def upper?(s)
        !/[[:upper:]]/.match(s).nil?
      end

      def validate_key!(key)
        return unless @_enum_hash.key?(key)
        raise "Duplicate key '#{key}' in enum '#{name}'"
      end

      def validate_value!(value)
        return unless @_enums_by_value.key?(value)
        raise "Duplicate value '#{value}' in enum '#{name}'"
      end
    end

  end

  # Fast Binary Encoding flags utility module
  module Flags

    attr_reader :key, :value

    def initialize(key, value)
      @key = key
      @value = value
    end

    def self.included(base)
      base.extend Enumerable
      base.extend ClassMethods
    end

    module ClassMethods
      # Define an enumerated value.
      #
      # === Parameters
      # [key] Enumerator key.
      # [value] Enumerator value.
      def define(key, value)
        @_enum_hash ||= {}
        @_enums_by_value ||= {}

        validate_key!(key)
        store_new_instance(key, value)
        define_singleton_method(key) { value }

        name = key.to_s

        # Flags check method
        define_method(name + '?') do
          @value & value != 0
        end

        # Flags set/reset method
        define_method(name + '=') do |set|
          if set
            @value |= value
          else
            @value &= ~value
          end
        end
      end

      def store_new_instance(key, value)
        new_instance = new(key, value)
        @_enum_hash[key] = new_instance
        @_enums_by_value[value] = new_instance
      end

      def const_missing(key)
        raise "Constant is missing for key '#{key}' in flags '#{name}'"
      end

      # Iterate over all enumerated values.
      # Required for Enumerable mixin
      def each(&block)
        @_enum_hash.each(&block)
      end

      # Attempt to parse an enum key and return the
      # corresponding value.
      #
      # === Parameters
      # [k] The key string to parse.
      #
      # Returns the corresponding value or nil.
      def parse(k)
        k = k.to_s.upcase
        each do |key, enum|
          return enum.value if key.to_s.upcase == k
        end
        nil
      end

      # Whether the specified key exists in this enum.
      #
      # === Parameters
      # [k] The string key to check.
      #
      # Returns true if the key exists, false otherwise.
      def key?(k)
        @_enum_hash.key?(k)
      end

      # Gets the string value for the specified key.
      #
      # === Parameters
      # [k] The key symbol to get the value for.
      #
      # Returns the corresponding enum instance or nil.
      def value(k)
        enum = @_enum_hash[k]
        enum.value if enum
      end

      # Whether the specified value exists in this enum.
      #
      # === Parameters
      # [k] The string value to check.
      #
      # Returns true if the value exists, false otherwise.
      def value?(v)
        @_enums_by_value.key?(v)
      end

      # Gets the key symbol for the specified value.
      #
      # === Parameters
      # [v] The string value to parse.
      #
      # Returns the corresponding key symbol or nil.
      def key(v)
        enum = @_enums_by_value[v]
        enum.key if enum
      end

      # Returns all enum keys.
      def keys
        @_enum_hash.values.map(&:key)
      end

      # Returns all enum values.
      def values
        @_enum_hash.values.map(&:value)
      end

      def to_h
        Hash[@_enum_hash.map do |key, enum|
          [key, enum.value]
        end]
      end

      private

      def upper?(s)
        !/[[:upper:]]/.match(s).nil?
      end

      def validate_key!(key)
        return unless @_enum_hash.key?(key)
        raise "Duplicate key '#{key}' in flags '#{name}'"
      end

      def validate_value!(value)
        return unless @_enums_by_value.key?(value)
        raise "Duplicate value '#{value}' in flags '#{name}'"
      end
    end

  end

  # Fast Binary Encoding integer number constants
  class Integer
    N_BYTES = [42].pack('i').size
    N_BITS = N_BYTES * 16
    MAX = 2 ** (N_BITS - 2) - 1
    MIN = -MAX - 1
  end

  # Fast Binary Encoding write buffer based on the dynamic byte array
  class WriteBuffer
    def initialize(capacity = 0)
      @_buffer = capacity > 0 ? [0].pack('C') * capacity : ''
      @_size = 0
      @_offset = 0
    end

    def empty?
      @_buffer.nil? || @_size <= 0
    end

    def buffer
      @_buffer
    end

    def capacity
      @_buffer.length
    end

    def size
      @_size
    end

    def offset
      @_offset
    end

    # Attach an empty memory buffer
    def attach_new
      @_buffer = ''
      @_size = 0
      @_offset = 0
    end

    # Attach an empty memory buffer with a given capacity
    def attach_capacity(capacity)
      @_buffer = [0].pack('C') * capacity
      @_size = 0
      @_offset = 0
    end

    # Attach a given memory buffer
    def attach_buffer(buffer, offset = 0, size = nil)
      raise ArgumentError, "Invalid buffer!" if buffer.nil?

      if buffer.is_a?(String)
        @_buffer = buffer
        size = @_buffer.length if size.nil?
      elsif buffer.is_a?(Array)
        @_buffer = buffer.join
        size = @_buffer.length if size.nil?
      elsif buffer.is_a?(WriteBuffer)
        @_buffer = buffer.buffer
        size = buffer.size if size.nil?
      elsif buffer.is_a?(ReadBuffer)
        @_buffer = buffer.buffer
        size = buffer.size if size.nil?
      else
        raise ArgumentError, "Unknown buffer type!"
      end

      raise ArgumentError, "Invalid size!" if size <= 0
      raise ArgumentError, "Invalid offset!" if offset > size

      @_size = size
      @_offset = offset
    end

    # Allocate memory in the current write buffer and return offset to the allocated memory block
    def allocate(size)
      raise ArgumentError, "Invalid allocation size!" if size < 0

      offset = @_size

      # Calculate a new buffer size
      total = @_size + size

      if total <= @_buffer.length
        @_size = total
        return offset
      end

      @_buffer += [0].pack('C') * [total, 2 * @_buffer.length].max
      @_size = total
      offset
    end

    # Remove some memory of the given size from the current write buffer
    def remove(offset, size)
      raise ArgumentError, "Invalid offset & size!" if (offset + size) > @_buffer.length

      @_buffer.slice!(offset, size)
      @_size -= size
      if @_offset >= (offset + size)
        @_offset -= size
      elsif @_offset >= offset
        @_offset -= @_offset - offset
        @_offset = @_size if @_offset > @_size
      end
    end

    # Reserve memory of the given capacity in the current write buffer
    def reserve(capacity)
      raise ArgumentError, "Invalid reserve capacity!" if capacity < 0

      @_buffer += [0].pack('C') * [capacity, 2 * @_buffer.length].max if capacity > @_buffer.length
    end

    # Resize the current write buffer
    def resize(size)
      reserve(size)
      @_size = size
      @_offset = @_size if @_offset > @_size
    end

    # Reset the current write buffer and its offset
    def reset
      @_size = 0
      @_offset = 0
    end

    # Shift the current write buffer offset
    def shift(offset)
      @_offset += offset
    end

    # Unshift the current write buffer offset
    def unshift(offset)
      @_offset -= offset
    end
  end

  # Fast Binary Encoding read buffer based on the constant byte buffer
  class ReadBuffer
    def initialize
      @_buffer = nil
      @_size = 0
      @_offset = 0
    end

    def empty?
      @_buffer.nil? || @_size <= 0
    end

    def buffer
      @_buffer
    end

    def capacity
      @_buffer.length
    end

    def size
      @_size
    end

    def offset
      @_offset
    end

    # Attach a given memory buffer
    def attach_buffer(buffer, offset = 0, size = nil)
      raise ArgumentError, "Invalid buffer!" if buffer.nil?

      if buffer.is_a?(String)
        @_buffer = buffer
        size = @_buffer.length if size.nil?
      elsif buffer.is_a?(Array)
        @_buffer = buffer.join
        size = @_buffer.length if size.nil?
      elsif buffer.is_a?(WriteBuffer)
        @_buffer = buffer.buffer
        size = buffer.size if size.nil?
      elsif buffer.is_a?(ReadBuffer)
        @_buffer = buffer.buffer
        size = buffer.size if size.nil?
      else
        raise ArgumentError, "Unknown buffer type!"
      end

      raise ArgumentError, "Invalid size!" if size <= 0
      raise ArgumentError, "Invalid offset!" if offset > size

      @_size = size
      @_offset = offset
    end

    # Reset the current read buffer and its offset
    def reset
      @_buffer = nil
      @_size = 0
      @_offset = 0
    end

    # Shift the current read buffer offset
    def shift(offset)
      @_offset += offset
    end

    # Unshift the current read buffer offset
    def unshift(offset)
      @_offset -= offset
    end
  end

  # Fast Binary Encoding base model
  class Model
    def initialize(buffer = WriteBuffer.new)
      @_buffer = buffer
    end

    def buffer
      @_buffer
    end

    # Attach an empty memory buffer
    def attach_new
      @_buffer.attach_new
    end

    # Attach an empty memory buffer with a given capacity
    def attach_capacity(capacity)
      @_buffer.attach_capacity(capacity)
    end

    # Attach a given memory buffer
    def attach_buffer(buffer, offset = 0, size = nil)
      @_buffer.attach_buffer(buffer, offset, size)
    end

    # Allocate memory in the current write buffer and return offset to the allocated memory block
    def allocate(size)
      @_buffer.allocate(size)
    end

    # Remove some memory of the given size from the current write buffer
    def remove(offset, size)
      @_buffer.remove(offset, size)
    end

    # Reserve memory of the given capacity in the current write buffer
    def reserve(capacity)
      @_buffer.reserve(capacity)
    end

    # Resize the current write buffer
    def resize(size)
      @_buffer.resize(size)
    end

    # Reset the current write buffer and its offset
    def reset
      @_buffer.reset
    end

    # Shift the current write buffer offset
    def shift(offset)
      @_buffer.shift(offset)
    end

    # Unshift the current write buffer offset
    def unshift(offset)
      @_buffer.unshift(offset)
    end

    protected

    # Buffer I/O methods

    def read_uint32(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('L<')[0]
    end

    def write_uint32(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 4] = [value].pack('L<')
    end
  end

  # Fast Binary Encoding base field model
  # noinspection RubyTooManyMethodsInspection
  class FieldModelBase
    def initialize(buffer, offset)
      @_buffer = buffer
      @_offset = offset
    end

    def buffer
      @_buffer
    end

    # Get the field offset
    def fbe_offset
      @_offset
    end

    # Set the field offset
    def fbe_offset=(offset)
      @_offset = offset
    end

    # Get the field size
    def fbe_size
      0
    end

    # Get the field extra size
    def fbe_extra
      0
    end

    # Shift the current field offset
    def fbe_shift(offset)
      @_offset += offset
    end

    # Unshift the current field offset
    def fbe_unshift(offset)
      @_offset -= offset
    end

    protected

    # Buffer I/O methods

    def read_bool(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 1).unpack('C')[0] != 0
    end

    def read_byte(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 1).unpack('C')[0]
    end

    def read_char(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 1).unpack('C')[0].chr
    end

    def read_wchar(offset)
      # noinspection RubyResolve
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('L<')[0].chr(Encoding::UTF_8)
    end

    def read_int8(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 1).unpack('c')[0]
    end

    def read_uint8(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 1).unpack('C')[0]
    end

    def read_int16(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 2).unpack('s<')[0]
    end

    def read_uint16(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 2).unpack('S<')[0]
    end

    def read_uint16_be(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 2).unpack('S>')[0]
    end

    def read_int32(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('l<')[0]
    end

    def read_uint32(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('L<')[0]
    end

    def read_uint32_be(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('L>')[0]
    end

    def read_int64(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 8).unpack('q<')[0]
    end

    def read_uint64(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 8).unpack('Q<')[0]
    end

    def read_float(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 4).unpack('e')[0]
    end

    def read_double(offset)
      @_buffer.buffer.slice(@_buffer.offset + offset, 8).unpack('E')[0]
    end

    def read_bytes(offset, size)
      @_buffer.buffer.slice(@_buffer.offset + offset, size)
    end

    def read_timestamp(offset)
      nanoseconds = read_uint64(offset)
      Time.at(nanoseconds / 1000000000, nanoseconds % 1000000000, :nsec).utc
    end

    def read_uuid(offset)
      UUIDTools::UUID.parse_raw(@_buffer.buffer.slice(@_buffer.offset + offset, 16))
    end

    def write_bool(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 1] = [value ? 1 : 0].pack('C')
    end

    def write_byte(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 1] = [value].pack('C')
    end

    def write_char(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 1] = [value.ord].pack('C')
    end

    def write_wchar(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 4] = [value.ord].pack('L<')
    end

    def write_int8(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 1] = [value].pack('c')
    end

    def write_uint8(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 1] = [value].pack('C')
    end

    def write_int16(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 2] = [value].pack('s<')
    end

    def write_uint16(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 2] = [value].pack('S<')
    end

    def write_int32(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 4] = [value].pack('l<')
    end

    def write_uint32(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 4] = [value].pack('L<')
    end

    def write_int64(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 8] = [value].pack('q<')
    end

    def write_uint64(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 8] = [value].pack('Q<')
    end

    def write_float(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 4] = [value].pack('e')
    end

    def write_double(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 8] = [value].pack('E')
    end

    def write_bytes(offset, buffer)
      @_buffer.buffer[@_buffer.offset + offset, buffer.length] = buffer
    end

    def write_count(offset, value, value_count)
      (0...value_count).each { |i| @_buffer.buffer[@_buffer.offset + offset + i] = value.chr }
    end

    def write_timestamp(offset, value)
      nanoseconds = value.to_i * 1000000000 + value.nsec
      write_uint64(offset, nanoseconds)
    end

    def write_uuid(offset, value)
      @_buffer.buffer[@_buffer.offset + offset, 16] = value.raw
    end
  end

  # Fast Binary Encoding field model
  class FieldModel < FieldModelBase
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      true
    end
  end

  # Fast Binary Encoding bool field model
  class FieldModelBool < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      1
    end

    # Get the value
    def get(defaults = false)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_bool(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_bool(fbe_offset, value)
    end
  end

  # Fast Binary Encoding byte field model
  class FieldModelByte < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      1
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_byte(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_byte(fbe_offset, value)
    end
  end

  # Fast Binary Encoding char field model
  class FieldModelChar < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      1
    end

    # Get the value
    def get(defaults = "\0")
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_char(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_char(fbe_offset, value)
    end
  end

  # Fast Binary Encoding wchar field model
  class FieldModelWChar < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the value
    def get(defaults = "\0")
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_wchar(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_wchar(fbe_offset, value)
    end
  end

  # Fast Binary Encoding int8 field model
  class FieldModelInt8 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      1
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_int8(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_int8(fbe_offset, value)
    end
  end

  # Fast Binary Encoding uint8 field model
  class FieldModelUInt8 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      1
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_uint8(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_uint8(fbe_offset, value)
    end
  end

  # Fast Binary Encoding int16 field model
  class FieldModelInt16 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      2
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_int16(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_int16(fbe_offset, value)
    end
  end

  # Fast Binary Encoding uint16 field model
  class FieldModelUInt16 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      2
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_uint16(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_uint16(fbe_offset, value)
    end
  end

  # Fast Binary Encoding int32 field model
  class FieldModelInt32 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_int32(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_int32(fbe_offset, value)
    end
  end

  # Fast Binary Encoding uint32 field model
  class FieldModelUInt32 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_uint32(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_uint32(fbe_offset, value)
    end
  end

  # Fast Binary Encoding int64 field model
  class FieldModelInt64 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      8
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_int64(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_int64(fbe_offset, value)
    end
  end

  # Fast Binary Encoding uint64 field model
  class FieldModelUInt64 < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      8
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_uint64(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_uint64(fbe_offset, value)
    end
  end

  # Fast Binary Encoding float field model
  class FieldModelFloat < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the value
    def get(defaults = 0.0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_float(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_float(fbe_offset, value)
    end
  end

  # Fast Binary Encoding double field model
  class FieldModelDouble < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      8
    end

    # Get the value
    def get(defaults = 0.0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_double(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_double(fbe_offset, value)
    end
  end

  # Fast Binary Encoding timestamp field model
  class FieldModelTimestamp < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      8
    end

    # Get the value
    def get(defaults = 0)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_timestamp(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_timestamp(fbe_offset, value)
    end
  end

  # Fast Binary Encoding uuid field model
  class FieldModelUUID < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      16
    end

    # Get the value
    def get(defaults = UUIDTools::UUID.parse_int(0))
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      read_uuid(fbe_offset)
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      write_uuid(fbe_offset, value)
    end
  end

  # Fast Binary Encoding decimal field model
  class FieldModelDecimal < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      16
    end

    # Get the decimal value
    def get(defaults = BigDecimal(0))
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      # Read decimal parts
      low = read_uint32(fbe_offset)
      mid = read_uint32(fbe_offset + 4)
      high = read_uint32(fbe_offset + 8)
      flags = read_uint32(fbe_offset + 12)

      # Calculate decimal value
      negative = (flags & 0x80000000) != 0
      scale = (flags & 0x7FFFFFFF) >> 16
      result = BigDecimal(high) * 18446744073709551616
      result += BigDecimal(mid) * 4294967296
      result += BigDecimal(low)
      result /= 10 ** scale
      result = -result if negative

      # Return decimal value
      result
    end

    # Set the decimal value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      # Extract decimal parts
      sign, significant_digits, _, exponent = value.split
      number = significant_digits.to_i(10)
      if exponent > significant_digits.length
        number *= 10 ** (exponent - 1)
        scale = 0
      else
        scale = significant_digits.length - exponent
      end

      # Check for decimal number overflow
      bits = number.bit_length
      if (bits < 0) || (bits > 96)
        # Value too big for .NET Decimal (bit length is limited to [0, 96])
        write_count(fbe_offset, 0, fbe_size)
        return
      end

      # Check for decimal scale overflow
      if (scale < 0) || (scale > 28)
        # Value scale exceeds .NET Decimal limit of [0, 28]
        write_count(fbe_offset, 0, fbe_size)
        return
      end

      # Write unscaled value to bytes 0-11
      bytes = []
      while number > 0
        bytes.insert(-1, number & 0xFF)
        number >>= 8
      end
      bytes = bytes.pack("C*")
      write_bytes(fbe_offset, bytes)
      write_count(fbe_offset + bytes.length, 0, 12 - bytes.length)

      # Write scale at byte 14
      write_byte(fbe_offset + 14, scale)

      # Write signum at byte 15
      write_byte(fbe_offset + 15, (sign == -1) ? 0x80 : 0)
    end
  end

  # Fast Binary Encoding bytes field model
  class FieldModelBytes < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the field extra size
    def fbe_extra
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_bytes_offset = read_uint32(fbe_offset)
      if (fbe_bytes_offset == 0) || ((@_buffer.offset + fbe_bytes_offset + 4) > @_buffer.size)
        return 0
      end

      fbe_bytes_size = read_uint32(fbe_bytes_offset)
      4 + fbe_bytes_size
    end

    # Is the bytes value valid?
    def valid?
      verify
    end

    # Check if the bytes value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_bytes_offset = read_uint32(fbe_offset)
      if fbe_bytes_offset == 0
        return true
      end

      if (@_buffer.offset + fbe_bytes_offset + 4) > @_buffer.size
        return false
      end

      fbe_bytes_size = read_uint32(fbe_bytes_offset)
      if (@_buffer.offset + fbe_bytes_offset + 4 + fbe_bytes_size) > @_buffer.size
        return false
      end

      true
    end

    # Get the bytes value
    def get(defaults = '')
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      fbe_bytes_offset = read_uint32(fbe_offset)
      if fbe_bytes_offset == 0
        return defaults
      end

      if (@_buffer.offset + fbe_bytes_offset + 4) > @_buffer.size
        return defaults
      end

      fbe_bytes_size = read_uint32(fbe_bytes_offset)
      if (@_buffer.offset + fbe_bytes_offset + 4 + fbe_bytes_size) > @_buffer.size
        return defaults
      end

      read_bytes(fbe_bytes_offset + 4, fbe_bytes_size)
    end

    # Set the bytes value
    def set(value)
      raise ArgumentError, "Invalid bytes value!" if value.nil? || !value.is_a?(String)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      fbe_bytes_size = value.length
      fbe_bytes_offset = @_buffer.allocate(4 + fbe_bytes_size) - @_buffer.offset
      if (fbe_bytes_offset <= 0) or ((@_buffer.offset + fbe_bytes_offset + 4 + fbe_bytes_size) > @_buffer.size)
        return
      end

      write_uint32(fbe_offset, fbe_bytes_offset)
      write_uint32(fbe_bytes_offset, fbe_bytes_size)
      write_bytes(fbe_bytes_offset + 4, value)
    end
  end

  # Fast Binary Encoding string field model
  class FieldModelString < FieldModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the field extra size
    def fbe_extra
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_string_offset = read_uint32(fbe_offset)
      if (fbe_string_offset == 0) or ((@_buffer.offset + fbe_string_offset + 4) > @_buffer.size)
        return 0
      end

      fbe_string_size = read_uint32(fbe_string_offset)
      4 + fbe_string_size
    end

    # Is the string value valid?
    def valid?
      verify
    end

    # Check if the string value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_string_offset = read_uint32(fbe_offset)
      if fbe_string_offset == 0
        return true
      end

      if (@_buffer.offset + fbe_string_offset + 4) > @_buffer.size
        return false
      end

      fbe_string_size = read_uint32(fbe_string_offset)
      if (@_buffer.offset + fbe_string_offset + 4 + fbe_string_size) > @_buffer.size
        return false
      end

      true
    end

    # Get the string value
    def get(defaults = '')
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return defaults
      end

      fbe_string_offset = read_uint32(fbe_offset)
      if fbe_string_offset == 0
        return defaults
      end

      if (@_buffer.offset + fbe_string_offset + 4) > @_buffer.size
        return defaults
      end

      fbe_string_size = read_uint32(fbe_string_offset)
      if (@_buffer.offset + fbe_string_offset + 4 + fbe_string_size) > @_buffer.size
        return defaults
      end

      data = read_bytes(fbe_string_offset + 4, fbe_string_size)
      data.encode!('utf-8')
    end

    # Set the string value
    def set(value)
      raise ArgumentError, "Invalid string value!" if value.nil? || !value.is_a?(String)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      data = value.encode('utf-8')

      fbe_string_size = data.length
      fbe_string_offset = @_buffer.allocate(4 + fbe_string_size) - @_buffer.offset
      if (fbe_string_offset <= 0) || ((@_buffer.offset + fbe_string_offset + 4 + fbe_string_size) > @_buffer.size)
        return
      end

      write_uint32(fbe_offset, fbe_string_offset)
      write_uint32(fbe_string_offset, fbe_string_size)
      write_bytes(fbe_string_offset + 4, data)
    end
  end

  # Fast Binary Encoding optional field model
  class FieldModelOptional < FieldModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
      @_model.fbe_offset = 0
    end

    # Get the field size
    def fbe_size
      1 + 4
    end

    # Get the field extra size
    def fbe_extra
      unless has_value?
        return 0
      end

      fbe_optional_offset = read_uint32(fbe_offset + 1)
      if (fbe_optional_offset == 0) || ((@_buffer.offset + fbe_optional_offset + 4) > @_buffer.size)
        return 0
      end

      @_buffer.shift(fbe_optional_offset)
      fbe_result = value.fbe_size + value.fbe_extra
      @_buffer.unshift(fbe_optional_offset)
      fbe_result
    end

    # Checks if the object contains a value
    def empty?
      has_value?
    end

    # Checks if the object contains a value
    def has_value?
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return false
      end

      fbe_has_value = read_uint8(fbe_offset)
      fbe_has_value != 0
    end

    # Get the base field model value
    def value
      @_model
    end

    # Is the optional value valid?
    def valid?
      verify
    end

    # Check if the optional value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_has_value = read_uint8(fbe_offset)
      if fbe_has_value == 0
        return true
      end

      fbe_optional_offset = read_uint32(fbe_offset + 1)
      if fbe_optional_offset == 0
        return false
      end

      @_buffer.shift(fbe_optional_offset)
      fbe_result = value.verify
      @_buffer.unshift(fbe_optional_offset)
      fbe_result
    end

    # Get the optional value (being phase)
    def get_begin
      unless has_value?
        return 0
      end

      fbe_optional_offset = read_uint32(fbe_offset + 1)
      if fbe_optional_offset <= 0
        return 0
      end

      @_buffer.shift(fbe_optional_offset)
      fbe_optional_offset
    end

    # Get the optional value (end phase)
    def get_end(fbe_begin)
      @_buffer.unshift(fbe_begin)
    end

    # Get the optional value
    def get(defaults = nil)
      fbe_begin = get_begin
      if fbe_begin == 0
        return defaults
      end
      optional = value.get
      get_end(fbe_begin)
      optional
    end

    # Set the optional value (begin phase)
    def set_begin(has_value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_has_value = has_value ? 1 : 0
      write_uint8(fbe_offset, fbe_has_value)
      if fbe_has_value == 0
        return 0
      end

      fbe_optional_size = value.fbe_size
      fbe_optional_offset = @_buffer.allocate(fbe_optional_size) - @_buffer.offset
      if (fbe_optional_offset <= 0) || ((@_buffer.offset + fbe_optional_offset + fbe_optional_size) > @_buffer.size)
        return 0
      end

      write_uint32(fbe_offset + 1, fbe_optional_offset)

      @_buffer.shift(fbe_optional_offset)
      fbe_optional_offset
    end

    # Set the optional value (end phase)
    def set_end(fbe_begin)
      @_buffer.unshift(fbe_begin)
    end

    # Set the optional value
    def set(optional)
      fbe_begin = set_begin(!optional.nil?)
      if fbe_begin == 0
        return
      end
      value.set(optional)
      set_end(fbe_begin)
    end
  end

  # Fast Binary Encoding array field model
  class FieldModelArray < FieldModel
    def initialize(model, buffer, offset, size)
        super(buffer, offset)
        @_model = model
        @_size = size
    end

    # Get the field size
    def fbe_size
      @_size * @_model.fbe_size
    end

    # Get the field extra size
    def fbe_extra
      0
    end

    # Get the array offset
    def offset
      0
    end

    # Get the array size
    def size
      @_size
    end

    # Array index operator
    def [](index)
      raise RuntimeError, "Model is broken!" if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
      raise IndexError, "Index is out of bounds!" if index >= @_size

      @_model.fbe_offset = fbe_offset
      @_model.fbe_shift(index * @_model.fbe_size)
      @_model
    end

    # Is the array valid?
    def valid?
      verify
    end

    # Check if the array is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return false
      end

      @_model.fbe_offset = fbe_offset
      @_size.times do
        unless @_model.verify
          return false
        end
        @_model.fbe_shift(@_model.fbe_size)
      end

      true
    end

    # Get the array
    def get(values = Array.new)
      values.clear

      fbe_model = self[0]
      @_size.times do
        value = fbe_model.get
        values.append(value)
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end

      values
    end

    # Set the array
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Array)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      fbe_model = self[0]
      (0...[values.length, @_size].min).each do |i|
        fbe_model.set(values[i])
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end
    end
  end

  # Fast Binary Encoding vector field model
  class FieldModelVector < FieldModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the field extra size
    def fbe_extra
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_vector_offset = read_uint32(fbe_offset)
      if (fbe_vector_offset == 0) || ((@_buffer.offset + fbe_vector_offset + 4) > @_buffer.size)
        return 0
      end

      fbe_vector_size = read_uint32(fbe_vector_offset)

      fbe_result = 4
      @_model.fbe_offset = fbe_vector_offset + 4
      fbe_vector_size.times do
        fbe_result += @_model.fbe_size + @_model.fbe_extra
        @_model.fbe_shift(@_model.fbe_size)
      end
      fbe_result
    end

    # Get the vector offset
    def offset
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      read_uint32(fbe_offset)
    end

    # Get the vector size
    def size
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_vector_offset = read_uint32(fbe_offset)
      if (fbe_vector_offset == 0) || ((@_buffer.offset + fbe_vector_offset + 4) > @_buffer.size)
        return 0
      end

      read_uint32(fbe_vector_offset)
    end

    # Vector index operator
    def [](index)
      raise RuntimeError, "Model is broken!" if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
      fbe_vector_offset = read_uint32(fbe_offset)
      raise RuntimeError, "Model is broken!" if (fbe_vector_offset <= 0) || ((@_buffer.offset + fbe_vector_offset + 4) > @_buffer.size)
      fbe_vector_size = read_uint32(fbe_vector_offset)
      raise IndexError, "Index is out of bounds!" if index >= fbe_vector_size

      @_model.fbe_offset = fbe_vector_offset + 4
      @_model.fbe_shift(index * @_model.fbe_size)
      @_model
    end

    # Resize the vector and get its first model
    def resize(size)
      fbe_vector_size = size * @_model.fbe_size
      fbe_vector_offset = @_buffer.allocate(4 + fbe_vector_size) - @_buffer.offset
      raise RuntimeError, "Model is broken!" if (fbe_vector_offset <= 0) || ((@_buffer.offset + fbe_vector_offset + 4) > @_buffer.size)

      write_uint32(fbe_offset, fbe_vector_offset)
      write_uint32(fbe_vector_offset, size)
      write_count(fbe_vector_offset + 4, 0, fbe_vector_size)

      @_model.fbe_offset = fbe_vector_offset + 4
      @_model
    end

    # Is the vector valid?
    def valid?
      verify
    end

    # Check if the vector is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_vector_offset = read_uint32(fbe_offset)
      if fbe_vector_offset == 0
        return true
      end

      if (@_buffer.offset + fbe_vector_offset + 4) > @_buffer.size
        return false
      end

      fbe_vector_size = read_uint32(fbe_vector_offset)

      @_model.fbe_offset = fbe_vector_offset + 4
      fbe_vector_size.times do
        unless @_model.verify
          return false
        end
        @_model.fbe_shift(@_model.fbe_size)
      end

      true
    end

    # Get the vector
    def get(values = Array.new)
      values.clear

      fbe_vector_size = size
      if fbe_vector_size == 0
        return values
      end

      fbe_model = self[0]
      fbe_vector_size.times do
        value = fbe_model.get
        values.append(value)
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end

      values
    end

    # Set the vector
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Array)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      fbe_model = resize(values.length)
      values.each do |value|
        fbe_model.set(value)
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end
    end
  end

  # Fast Binary Encoding set field model
  class FieldModelSet < FieldModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the field extra size
    def fbe_extra
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_set_offset = read_uint32(fbe_offset)
      if (fbe_set_offset == 0) || ((@_buffer.offset + fbe_set_offset + 4) > @_buffer.size)
        return 0
      end

      fbe_set_size = read_uint32(fbe_set_offset)

      fbe_result = 4
      @_model.fbe_offset = fbe_set_offset + 4
      fbe_set_size.times do
        fbe_result += @_model.fbe_size + @_model.fbe_extra
        @_model.fbe_shift(@_model.fbe_size)
      end
      fbe_result
    end

    # Get the set value offset
    def offset
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      read_uint32(fbe_offset)
    end

    # Get the set value size
    def size
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_set_offset = read_uint32(fbe_offset)
      if (fbe_set_offset == 0) || ((@_buffer.offset + fbe_set_offset + 4) > @_buffer.size)
        return 0
      end

      read_uint32(fbe_set_offset)
    end

    # Set index operator
    def [](index)
      raise RuntimeError, "Model is broken!" if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
      fbe_set_offset = read_uint32(fbe_offset)
      raise RuntimeError, "Model is broken!" if (fbe_set_offset <= 0) || ((@_buffer.offset + fbe_set_offset + 4) > @_buffer.size)
      fbe_set_size = read_uint32(fbe_set_offset)
      raise IndexError, "Index is out of bounds!" if index >= fbe_set_size

      @_model.fbe_offset = fbe_set_offset + 4
      @_model.fbe_shift(index * @_model.fbe_size)
      @_model
    end

    # Resize the set and get its first model
    def resize(size)
      fbe_set_size = size * @_model.fbe_size
      fbe_set_offset = @_buffer.allocate(4 + fbe_set_size) - @_buffer.offset
      raise RuntimeError, "Model is broken!" if (fbe_set_offset <= 0) || ((@_buffer.offset + fbe_set_offset + 4) > @_buffer.size)

      write_uint32(fbe_offset, fbe_set_offset)
      write_uint32(fbe_set_offset, size)
      write_count(fbe_set_offset + 4, 0, fbe_set_size)

      @_model.fbe_offset = fbe_set_offset + 4
      @_model
    end

    # Is the set valid?
    def valid?
      verify
    end

    # Check if the set value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_set_offset = read_uint32(fbe_offset)
      if fbe_set_offset == 0
        return true
      end

      if (@_buffer.offset + fbe_set_offset + 4) > @_buffer.size
        return false
      end

      fbe_set_size = read_uint32(fbe_set_offset)

      @_model.fbe_offset = fbe_set_offset + 4
      fbe_set_size.times do
        unless @_model.verify
          return false
        end
        @_model.fbe_shift(@_model.fbe_size)
      end

      true
    end

    # Get the set value
    def get(values = Set.new)
      values.clear

      fbe_set_size = size
      if fbe_set_size == 0
        return values
      end

      fbe_model = self[0]
      fbe_set_size.times do
        value = fbe_model.get
        values.add(value)
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end

      values
    end

    # Set the set value
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Set)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      fbe_model = resize(values.length)
      values.each do |value|
        fbe_model.set(value)
        fbe_model.fbe_shift(fbe_model.fbe_size)
      end
    end
  end

  # Fast Binary Encoding map field model
  class FieldModelMap < FieldModel
    def initialize(model_key, model_value, buffer, offset)
      super(buffer, offset)
      @_model_key = model_key
      @_model_value = model_value
    end

    # Get the field size
    def fbe_size
      4
    end

    # Get the field extra size
    def fbe_extra
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_map_offset = read_uint32(fbe_offset)
      if (fbe_map_offset == 0) || ((@_buffer.offset + fbe_map_offset + 4) > @_buffer.size)
        return 0
      end

      fbe_map_size = read_uint32(fbe_map_offset)

      fbe_result = 4
      @_model_key.fbe_offset = fbe_map_offset + 4
      @_model_value.fbe_offset = fbe_map_offset + 4 + @_model_key.fbe_size
      fbe_map_size.times do
        fbe_result += @_model_key.fbe_size + @_model_key.fbe_extra
        @_model_key.fbe_shift(@_model_key.fbe_size + @_model_value.fbe_size)
        fbe_result += @_model_value.fbe_size + @_model_value.fbe_extra
        @_model_value.fbe_shift(@_model_key.fbe_size + @_model_value.fbe_size)
      end
      fbe_result
    end

    # Get the map offset
    def offset
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      read_uint32(fbe_offset)
    end

    # Get the map size
    def size
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      fbe_map_offset = read_uint32(fbe_offset)
      if (fbe_map_offset == 0) || ((@_buffer.offset + fbe_map_offset + 4) > @_buffer.size)
        return 0
      end

      read_uint32(fbe_map_offset)
    end

    # Map index operator
    def [](index)
      raise RuntimeError, "Model is broken!" if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
      fbe_map_offset = read_uint32(fbe_offset)
      raise RuntimeError, "Model is broken!" if (fbe_map_offset <= 0) || ((@_buffer.offset + fbe_map_offset + 4) > @_buffer.size)
      fbe_map_size = read_uint32(fbe_map_offset)
      raise IndexError, "Index is out of bounds!" if index >= fbe_map_size

      @_model_key.fbe_offset = fbe_map_offset + 4
      @_model_value.fbe_offset = fbe_map_offset + 4 + @_model_key.fbe_size
      @_model_key.fbe_shift(index * (@_model_key.fbe_size + @_model_value.fbe_size))
      @_model_value.fbe_shift(index * (@_model_key.fbe_size + @_model_value.fbe_size))
      [@_model_key, @_model_value]
    end

    # Resize the map and get its first model
    def resize(size)
      fbe_map_size = size * (@_model_key.fbe_size + @_model_value.fbe_size)
      fbe_map_offset = @_buffer.allocate(4 + fbe_map_size) - @_buffer.offset
      raise RuntimeError, "Model is broken!" if (fbe_map_offset <= 0) || ((@_buffer.offset + fbe_map_offset + 4) > @_buffer.size)

      write_uint32(fbe_offset, fbe_map_offset)
      write_uint32(fbe_map_offset, size)
      write_count(fbe_map_offset + 4, 0, fbe_map_size)

      @_model_key.fbe_offset = fbe_map_offset + 4
      @_model_value.fbe_offset = fbe_map_offset + 4 + @_model_key.fbe_size
      [@_model_key, @_model_value]
    end

    # Is the map valid?
    def valid?
      verify
    end

    # Check if the map is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return true
      end

      fbe_map_offset = read_uint32(fbe_offset)
      if fbe_map_offset == 0
        return true
      end

      if (@_buffer.offset + fbe_map_offset + 4) > @_buffer.size
        return false
      end

      fbe_map_size = read_uint32(fbe_map_offset)

      @_model_key.fbe_offset = fbe_map_offset + 4
      @_model_value.fbe_offset = fbe_map_offset + 4 + @_model_key.fbe_size
      fbe_map_size.times do
        unless @_model_key.verify
          return false
        end
        @_model_key.fbe_shift(@_model_key.fbe_size + @_model_value.fbe_size)
        unless @_model_value.verify
          return false
        end
        @_model_value.fbe_shift(@_model_key.fbe_size + @_model_value.fbe_size)
      end

      true
    end

    # Get the map
    def get(values = Hash.new)
      values.clear

      fbe_map_size = size
      if fbe_map_size == 0
        return values
      end

      fbe_model_key, fbe_model_value = self[0]
      fbe_map_size.times do
        key = fbe_model_key.get
        value = fbe_model_value.get
        values[key] = value
        fbe_model_key.fbe_shift(fbe_model_key.fbe_size + fbe_model_value.fbe_size)
        fbe_model_value.fbe_shift(fbe_model_key.fbe_size + fbe_model_value.fbe_size)
      end

      values
    end

    # Set the map
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Hash)

      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return
      end

      fbe_model_key, fbe_model_value = resize(values.length)
      values.each do |key, value|
        fbe_model_key.set(key)
        fbe_model_key.fbe_shift(fbe_model_key.fbe_size + fbe_model_value.fbe_size)
        fbe_model_value.set(value)
        fbe_model_value.fbe_shift(fbe_model_key.fbe_size + fbe_model_value.fbe_size)
      end
    end
  end

  # Fast Binary Encoding final model
  class FinalModel < FieldModelBase
    def initialize(buffer, offset)
        super(buffer, offset)
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      raise NotImplementedError, "verify() method not implemented!"
    end
  end

  # Fast Binary Encoding bool final model
  class FinalModelBool < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      1
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [false, 0]
      end

      [read_bool(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_bool(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding byte final model
  class FinalModelByte < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      1
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_byte(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_byte(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding char final model
  class FinalModelChar < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      1
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return ["\0", 0]
      end

      [read_char(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_char(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding wchar final model
  class FinalModelWChar < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      4
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return ["\0", 0]
      end

      [read_wchar(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_wchar(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding int8 final model
  class FinalModelInt8 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      1
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_int8(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_int8(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding uint8 final model
  class FinalModelUInt8 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      1
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_uint8(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_uint8(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding int16 final model
  class FinalModelInt16 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      2
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_int16(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_int16(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding uint16 final model
  class FinalModelUInt16 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      2
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_uint16(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_uint16(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding int32 final model
  class FinalModelInt32 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      4
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_int32(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_int32(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding uint32 final model
  class FinalModelUInt32 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      4
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_uint32(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_uint32(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding int64 final model
  class FinalModelInt64 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      8
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_int64(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_int64(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding uint64 final model
  class FinalModelUInt64 < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      8
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_uint64(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_uint64(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding float final model
  class FinalModelFloat < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      4
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0.0, 0]
      end

      [read_float(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_float(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding double final model
  class FinalModelDouble < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      8
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0.0, 0]
      end

      [read_double(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_double(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding timestamp final model
  class FinalModelTimestamp < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      8
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [0, 0]
      end

      [read_timestamp(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_timestamp(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding uuid final model
  class FinalModelUUID < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      16
    end

    # Is the value valid?
    def valid?
      verify
    end

    # Check if the value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [UUIDTools::UUID.parse_int(0), 0]
      end

      [read_uuid(fbe_offset), fbe_size]
    end

    # Set the value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      write_uuid(fbe_offset, value)
      fbe_size
    end
  end

  # Fast Binary Encoding decimal final model
  class FinalModelDecimal < FieldModel
    def initialize(buffer, offset)
        super(buffer, offset)
    end

    # Get the allocation size
    # noinspection RubyUnusedLocalVariable
    def fbe_allocation_size(value)
      fbe_size
    end

    # Get the final size
    def fbe_size
      16
    end

    # Is the decimal value valid?
    def valid?
      verify
    end

    # Check if the decimal value is valid
    def verify
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return Integer::MAX
      end

      fbe_size
    end

    # Get the decimal value
    def get
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return [BigDecimal(0), 0]
      end

      # Read decimal parts
      low = read_uint32(fbe_offset)
      mid = read_uint32(fbe_offset + 4)
      high = read_uint32(fbe_offset + 8)
      flags = read_uint32(fbe_offset + 12)

      # Calculate decimal value
      negative = (flags & 0x80000000) != 0
      scale = (flags & 0x7FFFFFFF) >> 16
      result = BigDecimal(high) * 18446744073709551616
      result += BigDecimal(mid) * 4294967296
      result += BigDecimal(low)
      result /= 10 ** scale
      result = -result if negative

      # Return decimal value
      [result, fbe_size]
    end

    # Set the decimal value
    def set(value)
      if (@_buffer.offset + fbe_offset + fbe_size) > @_buffer.size
        return 0
      end

      # Extract decimal parts
      sign, significant_digits, _, exponent = value.split
      number = significant_digits.to_i(10)
      if exponent > significant_digits.length
        number *= 10 ** (exponent - 1)
        scale = 0
      else
        scale = significant_digits.length - exponent
      end

      # Check for decimal number overflow
      bits = number.bit_length
      if (bits < 0) || (bits > 96)
        # Value too big for .NET Decimal (bit length is limited to [0, 96])
        write_count(fbe_offset, 0, fbe_size)
        return
      end

      # Check for decimal scale overflow
      if (scale < 0) || (scale > 28)
        # Value scale exceeds .NET Decimal limit of [0, 28]
        write_count(fbe_offset, 0, fbe_size)
        return
      end

      # Write unscaled value to bytes 0-11
      bytes = []
      while number > 0
        bytes.insert(-1, number & 0xFF)
        number >>= 8
      end
      bytes = bytes.pack("C*")
      write_bytes(fbe_offset, bytes)
      write_count(fbe_offset + bytes.length, 0, 12 - bytes.length)

      # Write scale at byte 14
      write_byte(fbe_offset + 14, scale)

      # Write signum at byte 15
      write_byte(fbe_offset + 15, (sign == -1) ? 0x80 : 0)

      fbe_size
    end
  end

  # Fast Binary Encoding bytes final model
  class FinalModelBytes < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    def fbe_allocation_size(value)
      4 + value.length
    end

    # Is the bytes value valid?
    def valid?
      verify
    end

    # Check if the bytes value is valid
    def verify
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return Integer::MAX
      end

      fbe_bytes_size = read_uint32(fbe_offset)
      if (@_buffer.offset + fbe_offset + 4 + fbe_bytes_size) > @_buffer.size
        return Integer::MAX
      end

      4 + fbe_bytes_size
    end

    # Get the bytes value
    def get
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return ['', 0]
      end

      fbe_bytes_size = read_uint32(fbe_offset)
      if (@_buffer.offset + fbe_offset + 4 + fbe_bytes_size) > @_buffer.size
        return ['', 4]
      end

      [read_bytes(fbe_offset + 4, fbe_bytes_size), (4 + fbe_bytes_size)]
    end

    # Set the bytes value
    def set(value)
      raise ArgumentError, "Invalid bytes value!" if value.nil? || !value.is_a?(String)

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return 0
      end

      fbe_bytes_size = value.length
      if (@_buffer.offset + fbe_offset + 4 + fbe_bytes_size) > @_buffer.size
        return 4
      end

      write_uint32(fbe_offset, fbe_bytes_size)
      write_bytes(fbe_offset + 4, value)
      4 + fbe_bytes_size
    end
  end

  # Fast Binary Encoding string final model
  class FinalModelString < FinalModel
    def initialize(buffer, offset)
      super(buffer, offset)
    end

    # Get the allocation size
    def fbe_allocation_size(value)
      4 + 3 * (value.length + 1)
    end

    # Is the string value valid?
    def valid?
      verify
    end

    # Check if the string value is valid
    def verify
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return Integer::MAX
      end

      fbe_string_size = read_uint32(fbe_offset)
      if (@_buffer.offset + fbe_offset + 4 + fbe_string_size) > @_buffer.size
        return Integer::MAX
      end

      4 + fbe_string_size
    end

    # Get the string value
    def get
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return ['', 0]
      end

      fbe_string_size = read_uint32(fbe_offset)
      if (@_buffer.offset + fbe_offset + 4 + fbe_string_size) > @_buffer.size
        return ['', 4]
      end

      data = read_bytes(fbe_offset + 4, fbe_string_size)
      [data.encode!('utf-8'), (4 + fbe_string_size)]
    end

    # Set the string value
    def set(value)
      raise ArgumentError, "Invalid string value!" if value.nil? || !value.is_a?(String)

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return 0
      end

      data = value.encode('utf-8')

      fbe_string_size = data.length
      if (@_buffer.offset + fbe_offset + 4 + fbe_string_size) > @_buffer.size
        return 4
      end

      write_uint32(fbe_offset, fbe_string_size)
      write_bytes(fbe_offset + 4, data)
      4 + fbe_string_size
    end
  end

  # Fast Binary Encoding optional final model
  class FinalModelOptional < FinalModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
      @_model.fbe_offset = 0
    end

    # Get the allocation size
    def fbe_allocation_size(optional)
      1 + (optional ? value.fbe_allocation_size(optional) : 0)
    end

    # Checks if the object contains a value
    def empty?
      has_value?
    end

    # Checks if the object contains a value
    def has_value?
      if (@_buffer.offset + fbe_offset + 1) > @_buffer.size
        return false
      end

      fbe_has_value = read_uint8(fbe_offset)
      fbe_has_value != 0
    end

    # Get the base final model value
    def value
      @_model
    end

    # Is the optional value valid?
    def valid?
      verify
    end

    # Check if the optional value is valid
    def verify
      if (@_buffer.offset + fbe_offset + 1) > @_buffer.size
        return Integer::MAX
      end

      fbe_has_value = read_uint8(fbe_offset)
      if fbe_has_value == 0
        return 1
      end

      @_buffer.shift(fbe_offset + 1)
      fbe_result = value.verify
      @_buffer.unshift(fbe_offset + 1)
      1 + fbe_result
    end

    # Get the optional value
    def get
      if (@_buffer.offset + fbe_offset + 1) > @_buffer.size
        return [nil, 0]
      end

      unless has_value?
        return [nil, 1]
      end

      @_buffer.shift(fbe_offset + 1)
      optional = value.get
      @_buffer.unshift(fbe_offset + 1)
      [optional[0], (1 + optional[1])]
    end

    # Set the optional value
    def set(optional)
      if (@_buffer.offset + fbe_offset + 1) > @_buffer.size
        return 0
      end

      fbe_has_value = optional ? 1 : 0
      write_uint8(fbe_offset, fbe_has_value)
      if fbe_has_value == 0
        return 1
      end

      @_buffer.shift(fbe_offset + 1)
      size = value.set(optional)
      @_buffer.unshift(fbe_offset + 1)
      1 + size
    end
  end

  # Fast Binary Encoding array final model
  class FinalModelArray < FinalModel
    def initialize(model, buffer, offset, size)
      super(buffer, offset)
      @_model = model
      @_size = size
    end

    # Get the allocation size
    def fbe_allocation_size(values)
      size = 0
      [values.length, @_size].min.times do |i|
        size += @_model.fbe_allocation_size(values[i])
      end
      size
    end

    # Is the array valid?
    def valid?
      verify
    end

    # Check if the array is valid
    def verify
      if (@_buffer.offset + fbe_offset) > @_buffer.size
        return Integer::MAX
      end

      size = 0
      @_model.fbe_offset = fbe_offset
      @_size.times do
        offset = @_model.verify
        if offset == Integer::MAX
          return Integer::MAX
        end
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end

    # Get the array
    def get(values = Array.new)
      values.clear

      if (@_buffer.offset + fbe_offset) > @_buffer.size
        return [values, 0]
      end

      size = 0
      @_model.fbe_offset = fbe_offset
      @_size.times do
        value = @_model.get
        values.append(value[0])
        @_model.fbe_shift(value[1])
        size += value[1]
      end
      [values, size]
    end

    # Set the array
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Array)

      if (@_buffer.offset + fbe_offset) > @_buffer.size
        return 0
      end

      size = 0
      @_model.fbe_offset = fbe_offset
      [values.length, @_size].min.times do |i|
        offset = @_model.set(values[i])
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end
  end

  # Fast Binary Encoding vector final model
  class FinalModelVector < FinalModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
    end

    # Get the allocation size
    def fbe_allocation_size(values)
      size = 4
      values.each do |value|
        size += @_model.fbe_allocation_size(value)
      end
      size
    end

    # Is the vector valid?
    def valid?
      verify
    end

    # Check if the vector is valid
    def verify
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return Integer::MAX
      end

      fbe_vector_size = read_uint32(fbe_offset)

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      fbe_vector_size.times do
        offset = @_model.verify
        if offset == Integer::MAX
          return Integer::MAX
        end
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end

    # Get the vector
    def get(values = Array.new)
      values.clear

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return [values, 0]
      end

      fbe_vector_size = read_uint32(fbe_offset)
      if fbe_vector_size == 0
        return [values, 4]
      end

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      fbe_vector_size.times do
        value = @_model.get
        values.append(value[0])
        @_model.fbe_shift(value[1])
        size += value[1]
      end
      [values, size]
    end

    # Set the vector
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Array)

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return 0
      end

      write_uint32(fbe_offset, values.length)

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      values.each do |value|
        offset = @_model.set(value)
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end
  end

  # Fast Binary Encoding set final model
  class FinalModelSet < FinalModel
    def initialize(model, buffer, offset)
      super(buffer, offset)
      @_model = model
    end

    # Get the allocation size
    def fbe_allocation_size(values)
      size = 4
      values.each do |value|
        size += @_model.fbe_allocation_size(value)
      end
      size
    end

    # Is the set valid?
    def valid?
      verify
    end

    # Check if the set value is valid
    def verify
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return Integer::MAX
      end

      fbe_set_size = read_uint32(fbe_offset)

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      fbe_set_size.times do
        offset = @_model.verify
        if offset == Integer::MAX
          return Integer::MAX
        end
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end

    # Get the set value
    def get(values = Set.new)
      values.clear

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return [values, 0]
      end

      fbe_set_size = read_uint32(fbe_offset)
      if fbe_set_size == 0
        return [values, 4]
      end

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      fbe_set_size.times do
        value = @_model.get
        values.add(value[0])
        @_model.fbe_shift(value[1])
        size += value[1]
      end
      [values, size]
    end

    # Set the set value
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Set)

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return 0
      end

      write_uint32(fbe_offset, values.length)

      size = 4
      @_model.fbe_offset = fbe_offset + 4
      values.each do |value|
        offset = @_model.set(value)
        @_model.fbe_shift(offset)
        size += offset
      end
      size
    end
  end

  # Fast Binary Encoding map final model
  class FinalModelMap < FinalModel
    def initialize(model_key, model_value, buffer, offset)
      super(buffer, offset)
      @_model_key = model_key
      @_model_value = model_value
    end

    # Get the allocation size
    def fbe_allocation_size(values)
      size = 4
      values.each do |key, value|
        size += @_model_key.fbe_allocation_size(key)
        size += @_model_value.fbe_allocation_size(value)
      end
      size
    end

    # Is the map valid?
    def valid?
      verify
    end

    # Check if the map is valid
    def verify
      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return Integer::MAX
      end

      fbe_map_size = read_uint32(fbe_offset)

      size = 4
      @_model_key.fbe_offset = fbe_offset + 4
      @_model_value.fbe_offset = fbe_offset + 4
      fbe_map_size.times do
        offset_key = @_model_key.verify
        if offset_key == Integer::MAX
          return Integer::MAX
        end
        @_model_key.fbe_shift(offset_key)
        @_model_value.fbe_shift(offset_key)
        size += offset_key
        offset_value = @_model_value.verify
        if offset_value == Integer::MAX
          return Integer::MAX
        end
        @_model_key.fbe_shift(offset_value)
        @_model_value.fbe_shift(offset_value)
        size += offset_value
      end
      size
    end

    # Get the map
    def get(values = Hash.new)
      values.clear

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return [values, 0]
      end

      fbe_map_size = read_uint32(fbe_offset)
      if fbe_map_size == 0
        return [values, 4]
      end

      size = 4
      @_model_key.fbe_offset = fbe_offset + 4
      @_model_value.fbe_offset = fbe_offset + 4
      fbe_map_size.times do
        key = @_model_key.get
        @_model_key.fbe_shift(key[1])
        @_model_value.fbe_shift(key[1])
        size += key[1]
        value = @_model_value.get
        @_model_key.fbe_shift(value[1])
        @_model_value.fbe_shift(value[1])
        size += value[1]
        values[key[0]] = value[0]
      end
      [values, size]
    end

    # Set the map
    def set(values)
      raise ArgumentError, "Invalid values parameter!" if values.nil? || !values.is_a?(Hash)

      if (@_buffer.offset + fbe_offset + 4) > @_buffer.size
        return 0
      end

      write_uint32(fbe_offset, values.length)

      size = 4
      @_model_key.fbe_offset = fbe_offset + 4
      @_model_value.fbe_offset = fbe_offset + 4
      values.each do |key, value|
        offset_key = @_model_key.set(key)
        @_model_key.fbe_shift(offset_key)
        @_model_value.fbe_shift(offset_key)
        size += offset_key
        offset_value = @_model_value.set(value)
        @_model_key.fbe_shift(offset_value)
        @_model_value.fbe_shift(offset_value)
        size += offset_value
      end
      size
    end
  end

  # Fast Binary Encoding base sender
  class Sender
    def initialize(buffer = WriteBuffer.new, final = false)
      @_buffer = buffer
      @_logging = false
      @_final = final
    end

    # Get the bytes buffer
    def buffer
      @_buffer
    end

    # Get the final protocol flag
    def final?
      @_final
    end

    # Get the logging flag
    def logging?
      @_logging
    end

    # Set the logging flag
    def logging=(logging)
      @_logging = logging
    end

    # Reset the sender buffer
    def reset
        @_buffer.reset
    end

    # Send serialized buffer.
    # Direct call of the method requires knowledge about internals of FBE models serialization.
    # Use it with care!
    def send_serialized(serialized)
      if serialized <= 0
        return 0
      end

      # Shift the send buffer
      @_buffer.shift(serialized)

      # Send the value
      sent = on_send(@_buffer.buffer, 0, @_buffer.size)
      @_buffer.remove(0, sent)
      sent
    end

    protected

    # Send message handler
    # noinspection RubyUnusedLocalVariable
    def on_send(buffer, offset, size)
      raise NotImplementedError, "Abstract method call!"
    end

    # Send log message handler
    # noinspection RubyUnusedLocalVariable
    def on_send_log(message)
    end
  end

  # Fast Binary Encoding base receiver
  class Receiver
    def initialize(buffer = WriteBuffer.new, final = false)
      @_buffer = buffer
      @_logging = false
      @_final = final
    end

    # Get the bytes buffer
    def buffer
      @_buffer
    end

    # Get the final protocol flag
    def final?
      @_final
    end

    # Get the logging flag
    def logging?
      @_logging
    end

    # Set the logging flag
    def logging=(logging)
      @_logging = logging
    end

    # Reset the receiver buffer
    def reset
        @_buffer.reset
    end

    # Receive data
    def receive(buffer, offset = 0, size = nil)
      raise ArgumentError, "Invalid buffer!" if buffer.nil?

      if buffer.is_a?(ReadBuffer) || buffer.is_a?(WriteBuffer)
        buffer = buffer.buffer
      end

      if size.nil?
        size = buffer.length
      end

      raise ArgumentError, "Invalid offset & size!" if (offset + size) > buffer.length

      if size == 0
        return
      end

      # Storage buffer
      offset0 = @_buffer.offset
      offset1 = @_buffer.size
      size1 = @_buffer.size

      # Receive buffer
      offset2 = 0
      size2 = size

      # While receive buffer is available to handle...
      while offset2 < size2
        message_buffer = nil
        message_offset = 0
        message_size = 0

        # Try to receive message size
        message_size_copied = false
        message_size_found = false
        until message_size_found
          # Look into the storage buffer
          if offset0 < size1
            count = [size1 - offset0, 4].min
            if count == 4
              message_size_copied = true
              message_size_found = true
              message_size = Receiver.read_uint32(@_buffer.buffer, @_buffer.offset + offset0)
              offset0 += 4
              break
            else
              # Fill remaining data from the receive buffer
              if offset2 < size2
                count = [size2 - offset2, 4 - count].min

                # Allocate and refresh the storage buffer
                @_buffer.allocate(count)
                size1 += count

                @_buffer.buffer[offset1, count] = buffer[offset + offset2, count]
                offset1 += count
                offset2 += count
                next
              else
                break
              end
            end
          end

          # Look into the receive buffer
          if offset2 < size2
            count = [size2 - offset2, 4].min
            if count == 4
              message_size_found = true
              message_size = Receiver.read_uint32(buffer, offset + offset2)
              offset2 += 4
              break
            else
              # Allocate and refresh the storage buffer
              @_buffer.allocate(count)
              size1 += count

              @_buffer.buffer[offset1, count] = buffer[offset + offset2, count]
              offset1 += count
              offset2 += count
              next
            end
          else
            break
          end
        end

        unless message_size_found
          return
        end

        # Check the message full size
        min_size = @_final ? (4 + 4) : (4 + 4 + 4 + 4)
        if message_size < min_size
          return
        end

        # Try to receive message body
        message_found = false
        until message_found
          # Look into the storage buffer
          if offset0 < size1
            count = [size1 - offset0, message_size - 4].min
            if count == (message_size - 4)
              message_found = true
              message_buffer = @_buffer.buffer
              message_offset = offset0 - 4
              offset0 += message_size - 4
              break
            else
              # Fill remaining data from the receive buffer
              if offset2 < size2
                # Copy message size into the storage buffer
                unless message_size_copied
                  # Allocate and refresh the storage buffer
                  @_buffer.allocate(4)
                  size1 += 4

                  Receiver.write_uint32(@_buffer.buffer, @_buffer.offset + offset0, message_size)
                  offset0 += 4
                  offset1 += 4

                  message_size_copied = true
                end

                count = [size2 - offset2, message_size - 4 - count].min

                # Allocate and refresh the storage buffer
                @_buffer.allocate(count)
                size1 += count

                @_buffer.buffer[offset1, count] = buffer[offset + offset2, count]
                offset1 += count
                offset2 += count
                next
              else
                break
              end
            end
          end

          # Look into the receive buffer
          if offset2 < size2
            count = [size2 - offset2, message_size - 4].min
            if !message_size_copied && (count == (message_size - 4))
              message_found = true
              message_buffer = buffer
              message_offset = offset + offset2 - 4
              offset2 += message_size - 4
              break
            else
              # Copy message size into the storage buffer
              unless message_size_copied
                # Allocate and refresh the storage buffer
                @_buffer.allocate(4)
                size1 += 4

                Receiver.write_uint32(@_buffer.buffer, @_buffer.offset + offset0, message_size)
                offset0 += 4
                offset1 += 4

                message_size_copied = true
              end

              # Allocate and refresh the storage buffer
              @_buffer.allocate(count)
              size1 += count

              @_buffer.buffer[offset1, count] = buffer[offset + offset2, count]
              offset1 += count
              offset2 += count
              next
            end
          else
            break
          end
        end

        unless message_found
          # Copy message size into the storage buffer
          unless message_size_copied
            # Allocate and refresh the storage buffer
            @_buffer.allocate(4)
            # noinspection RubyUnusedLocalVariable
            size1 += 4

            Receiver.write_uint32(@_buffer.buffer, @_buffer.offset + offset0, message_size)
            # noinspection RubyUnusedLocalVariable
            offset0 += 4
            # noinspection RubyUnusedLocalVariable
            offset1 += 4

            # noinspection RubyUnusedLocalVariable
            message_size_copied = true
          end
          return
        end

        # Read the message parameters
        if @_final
          # noinspection RubyUnusedLocalVariable
          fbe_struct_size = Receiver.read_uint32(message_buffer, message_offset)
          fbe_struct_type = Receiver.read_uint32(message_buffer, message_offset + 4)
        else
          fbe_struct_offset = Receiver.read_uint32(message_buffer, message_offset + 4)
          # noinspection RubyUnusedLocalVariable
          fbe_struct_size = Receiver.read_uint32(message_buffer, message_offset + fbe_struct_offset)
          fbe_struct_type = Receiver.read_uint32(message_buffer, message_offset + fbe_struct_offset + 4)
        end

        # Handle the message
        on_receive(fbe_struct_type, message_buffer, message_offset, message_size)

        # Reset the storage buffer
        @_buffer.reset

        # Refresh the storage buffer
        offset0 = @_buffer.offset
        offset1 = @_buffer.size
        size1 = @_buffer.size
      end
    end

    protected

    # Receive message handler
    # noinspection RubyUnusedLocalVariable
    def on_receive(type, buffer, offset, size)
      raise NotImplementedError, "Abstract method call!"
    end

    # Receive log message handler
    # noinspection RubyUnusedLocalVariable
    def on_receive_log(message)
    end

    private

    # Buffer I/O methods

    def self.read_uint32(buffer, offset)
      buffer.slice(offset, 4).unpack('L<')[0]
    end

    def self.write_uint32(buffer, offset, value)
      buffer[offset, 4] = [value].pack('L<')
    end
  end

end

# rubocop:enable all
