class SML::BinaryEncoding

Public Class Methods

decode_file(file) click to toggle source
# File lib/ruby-sml/encoding-binary.rb, line 5
    def self.decode_file(file)
      source = String.new(file)
      sml_file = []
      position = 0
      
      list_length = []
      list_depth = 0
      parent = []
      current_list = sml_file

      while not source.empty?
        type = nil
        value = nil
        header_length = 0
        length = 0
        tl_byte = source.slice!(0,1).unpack('C')[0]
        break if tl_byte.nil?
        
        # check for special cases
        if tl_byte == 0x00
#           puts "end of message"
#           puts "--------------"
          list_length = []
          list_depth = 0

          current_list << :end_of_message

          current_list = sml_file
          parent = []

          next
        end
        if tl_byte == 0x01
#           list_depth.times do print "\t" end
#           puts "?"

          current_list << nil

          if not list_length.empty?
            l = list_length.pop
            l -= 1
            list_length.push(l)
          end
          while list_length.last == 0
            list_length.pop
            list_depth -= 1
            current_list = parent.pop
          end

          next
        end

        # parse tl field
        type_bits = (tl_byte & 0b01110000) >> 4
        case type_bits
        when 0b000
          type = :string
        when 0b100
          type = :bool
        when 0b101
          type = :signed
        when 0b110
          type = :unsigned
        when 0b111
          type = :list
        else
          type = nil
#          puts "type: unknown (0b#{type_bits.to_s(2)})"
          exit
        end

        list_depth.times do
#           print "\t"
        end

        if not list_length.empty?
          l = list_length.pop
          l -= 1
          list_length.push(l)
        end
        
        length = tl_byte & 0b00001111
        header_length += 1
        while (tl_byte & 0b10000000) != 0
          length = length << 4
          tl_byte = source.slice!(0,1).unpack('C')[0]
          length |= (tl_byte & 0b00001111)
          header_length += 1
        end
        
        length -= header_length unless type == :list
 
        # we know what to expect, lets get it
        case type
        when :string
          value = source.slice!(0,length)
#           puts "string(#{length}): #{value}"
          current_list << value
        when :bool
          value = source.slice!(0,1).unpack('C')[0]
          if value == 0
            value = false
          else
            value = true
          end
#           puts "bool: #{value}"
          current_list << value << :bool
        when :signed
          value = 0
          bytes_left = length
          while bytes_left > 0
            byte = source.slice!(0,1).unpack('C')[0]
            value = value << 8
            value |= byte
            bytes_left -= 1
          end
          value = to_signed(value, length*8)
#           puts "int#{length*8}: #{value}"
          current_list << value << "int#{length*8}".to_sym
        when :unsigned
          value = 0
          bytes_left = length
          while bytes_left > 0
            byte = source.slice!(0,1).unpack('C')[0]
            value = value << 8
            value |= byte
            bytes_left -= 1
          end
#           puts "uint#{length*8}: #{value}"
          current_list << value << "uint#{length*8}".to_sym
        when :list
#           puts "list: #{length} elements"
          list_depth += 1
          list_length.push(length)
          new_list = []
          current_list << new_list
          parent.push(current_list)
          current_list = new_list
        end

        while list_length.last == 0
          list_length.pop
          list_depth -= 1
          current_list = parent.pop
        end
      end

      return sml_file
    end
encode_file(file) click to toggle source
# File lib/ruby-sml/encoding-binary.rb, line 155
def self.encode_file(file)
  result = String.new

  array_rep = Array.new(file)
  array_rep.each do |message|
    result << encode_value(message, :array) << 0x00
  end

  return result
end
encode_length(length, include_header) click to toggle source
# File lib/ruby-sml/encoding-binary.rb, line 234
def self.encode_length(length, include_header)
  result = []

  header_length = 1
  while (length+(include_header ? header_length : 0)) >= 2**(4*header_length)
    header_length += 1        
  end
  total_length = length + (include_header ? header_length : 0)

  while header_length > 0
    mask = 0xf << (header_length-1)*4
    if header_length > 1
      result << (0b10000000 | ((total_length & mask) >> (header_length-1)*4))
    else
      result << ((total_length & mask) >> (header_length-1)*4)          
    end
    header_length -= 1
  end

  return result
end
encode_value(value, type) click to toggle source
# File lib/ruby-sml/encoding-binary.rb, line 166
def self.encode_value(value, type)
  result = String.new

  case type
  when :array
    entries_with_type = []
    while not value.empty?
      entry = value.shift
      type = case entry
             when Array
               :array
             when String
               :string
             when NilClass
               :nil
             when Fixnum
               value.shift
             end

      entries_with_type << [entry, type]
    end
    
    tl_bytes = encode_length(entries_with_type.length, false)
    tl_bytes[0] = 0b01110000 + tl_bytes[0]
    tl_bytes.each do |byte|
      result << byte
    end
    entries_with_type.each do |entry, type|
      result << encode_value(entry,type)
    end
  when :string
    tl_bytes = encode_length(value.length, true)
    tl_bytes[0] = 0b00000000 + tl_bytes[0]
    tl_bytes.each do |byte|
      result << byte
    end
    result << value
  when :int8
    result << 0x52 << [value].pack('c')
  when :int16
    result << 0x53
    result << [value].pack('s').reverse
  when :int32
    result << 0x55
    result << [value].pack('l').reverse
  when :int64
    result << 0x59
    result << [value].pack('q').reverse
  when :uint8
    result << 0x62 << [value].pack('C')
  when :uint16
    result << 0x63 << [value].pack('n')
  when :uint32
    result << 0x65 << [value].pack('N')
  when :uint64
    result << 0x69 << [value].pack('Q').reverse
  when :bool
    if value == 0
      result << 0x42 << 0x00
    else          
      result << 0x42 << 0x01
    end
  when :nil
    result << 0x01
  end
  
  return result
end
to_signed(int, bits) click to toggle source
# File lib/ruby-sml/encoding-binary.rb, line 256
def self.to_signed(int, bits)  
  mask = (1 << (bits - 1))
  return (int & ~mask) - (int & mask)
end