Class: SML::BinaryEncoding

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby-sml/encoding-binary.rb

Class Method Summary collapse

Class Method Details

.decode_file(file) ⇒ Object


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# 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) ⇒ Object


155
156
157
158
159
160
161
162
163
164
# 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) ⇒ Object


234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# 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) ⇒ Object


166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# 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) ⇒ Object


256
257
258
259
# 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