lib/sypex_geo/pack.rb in sypex_geo-0.1.0 vs lib/sypex_geo/pack.rb in sypex_geo-0.2.0

- old
+ new

@@ -1,62 +1,112 @@ module SypexGeo - module Pack + class Pack def self.parse(pack, data) - result = {} - pos = 0 + new(pack).parse(data) + end - pack.split('/').each do |p| - type, name = p.split(':') + def initialize(pack) + @pack = pack + end - if data.nil? or data.empty? - val = type[0] =~ /b|c/ ? '' : 0 - else - if type[0] == 'b' - len = data.index("\0", pos) - pos + 1 - val = data[pos, len - 1].force_encoding('UTF-8') - else - len = type_length(type) - val = unpack(type, data[pos, len]) - val = val[0] if val.is_a?(Array) - end + def parse(data) + @data = data + @pos = 0 + result = {} - pos += len - end - - result[name.to_sym] = val + @pack.split('/').each do |part| + chunk, name = part.split(':') + result[name.to_sym] = parse_chunk(chunk) end result end protected - def self.type_length(type) - case type[0] - when /t|T/ then 1 - when /s|S|n/ then 2 - when /m|M/ then 3 - when 'd' then 8 - when 'c' then type[1..-1].to_i - else 4 + def parse_chunk(chunk) + case chunk[0] + when 't' then read_int8(chunk) + when 'T' then read_uint8(chunk) + when 's' then read_int16(chunk) + when 'S' then read_uint16(chunk) + when 'm' then read_int24(chunk) + when 'M' then read_uint24(chunk) + when 'i' then read_int32(chunk) + when 'I' then read_uint32(chunk) + when 'f' then read_float(chunk) + when 'd' then read_double(chunk) + when 'n' then read_decimal16(chunk) + when 'N' then read_decimal32(chunk) + when 'c' then read_chars(chunk) + when 'b' then read_blob(chunk) end end - def self.unpack(type, val) - case type[0] - when 't' then val.unpack('c') - when 'T' then val.unpack('C') - when 's' then val.unpack('s') - when 'S' then val.unpack('S') - when 'm' then (val + (val[2].ord >> 7) > 0 ? "\xFF" : "\0").unpack('l') - when 'M' then (val + "\0").unpack('L') - when 'i' then val.unpack('l') - when 'I' then val.unpack('L') - when 'f' then val.unpack('f') - when 'd' then val.unpack('d') - when 'n' then val.unpack('s')[0] / (10 ** type[1].to_i) - when 'N' then val.unpack('l')[0] / (10 ** type[1].to_i) - when 'c' then val.rstrip - end + def read(len) + @pos += len + @data[@pos - len, len] + end + + def read_string(len) + read(len).strip.force_encoding('UTF-8') + end + + def read_int8(chunk) + read(1).unpack('c')[0] + end + + def read_uint8(chunk) + read(1).unpack('C')[0] + end + + def read_int16(chunk) + read(2).unpack('s')[0] + end + + def read_uint16(chunk) + read(2).unpack('S')[0] + end + + def read_int24(chunk) + raw = read(3) + raw = raw + (raw[2].ord >> 7) > 0 ? "\xFF" : "\0" + raw.unpack('l')[0] + end + + def read_uint24(chunk) + (read(3) + "\0").unpack('L')[0] + end + + def read_int32(chunk) + read(4).unpack('l')[0] + end + + def read_uint32(chunk) + read(4).unpack('L')[0] + end + + def read_float(chunk) + read(4).unpack('f')[0] + end + + def read_double(chunk) + read(8).unpack('d')[0] + end + + def read_decimal16(chunk) + read(2).unpack('s')[0].to_f / (10 ** chunk[1].to_i) + end + + def read_decimal32(chunk) + read(4).unpack('l')[0].to_f / (10 ** chunk[1].to_i) + end + + def read_chars(chunk) + read_string(chunk[1..-1].to_i) + end + + def read_blob(chunk) + read_string(@data.index("\0", @pos) - @pos + 1) end end end