codec 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+
data/codec.gemspec CHANGED
@@ -19,4 +19,5 @@ Gem::Specification.new do |spec|
19
19
  spec.version = Codec::VERSION
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "log4r"
22
23
  end
data/lib/codec/base.rb CHANGED
@@ -6,47 +6,45 @@ module Codec
6
6
  @id = id
7
7
  end
8
8
 
9
- def build_field
9
+ def build_field(buf,length)
10
10
  f = Field.new(@id)
11
- f.set_value(@data)
11
+ f.set_value(buf[0,length])
12
12
  return f
13
13
  end
14
14
 
15
15
  def decode_with_length(buf,length)
16
- init_data(buf,length)
17
- return build_field,@remain
16
+ l = eval_length(buf,length)
17
+ return build_field(buf,l),buf[l,buf.length]
18
18
  end
19
19
 
20
20
  def decode(buf)
21
- init_data(buf,@length)
22
- return build_field,@remain
21
+ l = eval_length(buf,@length)
22
+ return build_field(buf,l),buf[l,buf.length]
23
23
  end
24
24
 
25
- def init_data(buf,length)
25
+ def eval_length(buf,length)
26
26
  length = 0 if length.nil?
27
27
  if(length != 0)
28
28
  if buf.length < length
29
29
  raise BufferUnderflow, "Not enough data for parsing #{@id} (#{length}/#{buf.length})"
30
30
  end
31
- @data = buf[0,length]
32
- @remain = buf[length,buf.length]
31
+ return length
33
32
  else
34
- @data = buf
35
- @remain = ""
33
+ return buf.length
36
34
  end
37
35
  end
38
36
 
39
- def add_subparser(id_field,parser)
40
- if parser.nil?
41
- raise InitializeException, "Invalid codec reference in subparser #{id_field} for codec #{@id}"
37
+ def add_sub_codec(id_field,codec)
38
+ if codec.nil?
39
+ raise InitializeException, "Invalid codec reference in subcodec #{id_field} for codec #{@id}"
42
40
  end
43
- if @subParsers.kind_of? Hash
44
- @subParsers[id_field] = parser
41
+ if @subCodecs.kind_of? Hash
42
+ @subCodecs[id_field] = codec
45
43
  end
46
44
  end
47
45
 
48
- def get_sub_parsers
49
- return @subParsers
46
+ def get_sub_codecs
47
+ return @subCodecs
50
48
  end
51
49
  end
52
50
  end
@@ -0,0 +1,102 @@
1
+ module Codec
2
+ class Bitmap < Base
3
+ NB_BITS_BY_BYTE = 8
4
+ def initialize(id,length)
5
+ super(id,length)
6
+ @num_extended_bitmaps=[]
7
+ @subCodecs = {}
8
+ end
9
+
10
+ def bitmap_length
11
+ @length * NB_BITS_BY_BYTE
12
+ end
13
+ def add_extended_bitmap(num_extention)
14
+ @num_extended_bitmaps << num_extention.to_i
15
+ end
16
+
17
+ def decodeBitmap(buffer,first_field_num)
18
+ fieldsList = []
19
+
20
+ bitmapBuffer = buffer[0,@length].unpack("B*").first
21
+ buf = buffer[@length,buffer.length]
22
+ field_num = first_field_num
23
+ while(bitmapBuffer.length > 0)
24
+ fieldsList << field_num if bitmapBuffer.start_with?('1')
25
+ bitmapBuffer.slice!(0)
26
+ field_num += 1
27
+ end
28
+ return fieldsList, buf
29
+ end
30
+
31
+ def encode_bitmap(fields_list,bitmap_index)
32
+ offset = bitmap_index * bitmap_length
33
+ bitmap = ""
34
+ ((offset + 1)..(offset + bitmap_length)).each do |i|
35
+ if fields_list.include?(i)
36
+ bitmap += "1"
37
+ else
38
+ bitmap += "0"
39
+ end
40
+ end
41
+ return [bitmap].pack("B*")
42
+ end
43
+
44
+ def encode(field)
45
+ fields = field.get_value
46
+ encoded_fields = []
47
+ fields_list = fields.collect{|id,sf| id.to_i}
48
+ # Add field for bitmaps
49
+ bitmap_fields = @num_extended_bitmaps[0,(fields_list.last - 1) / bitmap_length]
50
+ fields_list += bitmap_fields
51
+ fields += bitmap_fields.collect {|id| [id,nil]}
52
+ fields.sort!{|a,b| a.first.to_i <=> b.first.to_i}
53
+ # Encode first bitmap
54
+ out = encode_bitmap(fields_list,0)
55
+ bitmap_index = 1
56
+ fields.each do |id,sf|
57
+ codec = @subCodecs[id]
58
+ if @num_extended_bitmaps.include?(id)
59
+ out += encode_bitmap(fields_list,bitmap_index)
60
+ bitmap_index += 1
61
+ elsif codec.nil?
62
+ raise EncodingException, "unknown codec for subfield #{id}"
63
+ elsif encoded_fields.include?(id.to_i)
64
+ raise EncodingException, "Multiple subfield #{id} is invalid for Codec::Bitmap"
65
+ else
66
+ out += codec.encode(sf)
67
+ end
68
+ encoded_fields << id.to_i
69
+ end
70
+ return out
71
+ end
72
+
73
+ def decode(buffer)
74
+ msg = Field.new(@id)
75
+ field_num = 1
76
+ # 1. read bitmap
77
+ fields_list,buf = decodeBitmap(buffer,field_num)
78
+ field_num += bitmap_length
79
+ # 2. decode each field present
80
+ while fields_list.length > 0
81
+ # get next field number in bitmap
82
+ field_id = fields_list.slice!(0)
83
+ field_tag = field_id.to_s
84
+ if @num_extended_bitmaps.include?(field_id)
85
+ nextFields,buf = decodeBitmap(buf,field_num)
86
+ fields_list = fields_list + nextFields
87
+ elsif @subCodecs[field_tag].respond_to?(:decode)
88
+ Logger.debug "Parsing bitmap field #{field_tag}"
89
+ f,buf = @subCodecs[field_tag].decode(buf)
90
+ f.set_id(field_tag)
91
+ msg.add_sub_field(f)
92
+ else
93
+ f = Field.new("ERR")
94
+ f.set_value(buf.unpack("H*").first)
95
+ msg.add_sub_field(f)
96
+ raise ParsingException,msg.to_yaml + "\nError unknown field #{field_tag} : "
97
+ end
98
+ end
99
+ return msg,buf
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,83 @@
1
+ module Codec
2
+ class BaseComposed < Base
3
+ def initialize(id)
4
+ @id = id
5
+ @subCodecs = []
6
+ end
7
+
8
+ def decode(buf)
9
+ return build_each_field(buf,buf.length)
10
+ end
11
+
12
+ def encode(field)
13
+ subfields = field.get_value
14
+ composed_encoder = subfields.zip(@subCodecs).collect {|sf,sc|
15
+ if sf.first != sc.first
16
+ raise EncodingException, "subfield #{sf.first} not correspond to subcodec #{sc.first}"
17
+ end
18
+ [sc.last,sf.last]
19
+ }
20
+ out = ""
21
+ composed_encoder.each do |subcodec,subfield|
22
+ out += subcodec.encode(subfield)
23
+ end
24
+ return out
25
+ end
26
+
27
+ def build_each_field(buf,length)
28
+ msg = Field.new(@id)
29
+ working_buf = buf[0,length]
30
+ @subCodecs.each{|id,codec|
31
+ Logger.debug "Parsing struct field #{@id} : #{id}"
32
+ if working_buf.length == 0
33
+ Logger.debug "Not enough data to decode #{@id} : #{id}"
34
+ else
35
+ f,working_buf = codec.decode(working_buf)
36
+ f.set_id(id)
37
+ msg.add_sub_field(f)
38
+ end
39
+ }
40
+ return msg,working_buf
41
+ end
42
+
43
+ def build_field(buf,length)
44
+ msg,working_buf = build_each_field(buf,length)
45
+
46
+ if working_buf.length > 0
47
+ if @length_unknown
48
+ @remain = working_buf
49
+ else
50
+ f = Field.new("PADDING")
51
+ f.set_value(working_buf.unpack("H*").first)
52
+ msg.add_sub_field(f)
53
+ end
54
+ end
55
+ return msg
56
+ end
57
+
58
+ def add_sub_codec(id_field,codec)
59
+ if codec.nil?
60
+ raise InitializeException, "Invalid codec reference in subcodec #{id_field} for codec #{@id}"
61
+ end
62
+ @subCodecs << [id_field, codec]
63
+ end
64
+ end
65
+
66
+ class CompleteComposed < BaseComposed
67
+ def build_each_field(buf,length)
68
+ f,r = super(buf,length)
69
+ # Check if all struct's fields have been parsed
70
+ if f.get_value.size < @subCodecs.size
71
+ raise BufferUnderflow, "Not enough data for parsing Struct #{@id}"
72
+ end
73
+ return f,r
74
+ end
75
+
76
+ def encode(field)
77
+ if @subCodecs.size != field.get_value.size
78
+ raise EncodingException, "Not enough subfields to encode #{@id}"
79
+ end
80
+ super(field)
81
+ end
82
+ end
83
+ end
@@ -1,6 +1,13 @@
1
1
  module Codec
2
2
  class ParsingException < Exception
3
3
  end
4
+
5
+ class EncodingException < Exception
6
+ end
7
+
8
+ class TooLongDataException < EncodingException
9
+ end
10
+
4
11
 
5
12
  class InitializeException < Exception
6
13
  end
@@ -0,0 +1,4 @@
1
+ module Codec
2
+ # TODO : implements codec factory
3
+ # - should permit to load codec from xml,yaml or json files
4
+ end
data/lib/codec/field.rb CHANGED
@@ -8,11 +8,28 @@ module Codec
8
8
  end
9
9
 
10
10
  class Field
11
- def initialize(id="*")
11
+ def initialize(id="*",value="")
12
12
  @id = (id.nil? ? "*" : id)
13
- @value = ""
13
+ @value = value
14
14
  end
15
-
15
+
16
+ def self.from_array(id,fields_array)
17
+ f = Field.new(id)
18
+ fields_array.each do |id,value|
19
+ if value.kind_of?(Array)
20
+ sf = Field.from_array(id,value)
21
+ else
22
+ sf = Field.new(id,value)
23
+ end
24
+ f.add_sub_field(sf)
25
+ end
26
+ return f
27
+ end
28
+
29
+ def ==(other)
30
+ (@id == other.get_id && @value == other.get_value)
31
+ end
32
+
16
33
  def get_id ; @id; end
17
34
 
18
35
  def set_id id ; @id = id ; end
@@ -22,6 +39,7 @@ module Codec
22
39
  def set_value(value)
23
40
  raise "Error can not set value that is instance of Array" if value.kind_of? Array
24
41
  @value = value
42
+ return self
25
43
  end
26
44
 
27
45
  def add_sub_field(sf)
data/lib/codec/fix.rb CHANGED
@@ -1,48 +1,88 @@
1
1
  module Codec
2
2
  class Numbin < Base
3
- def build_field
3
+ def build_field(buf,length)
4
4
  f = Field.new(@id)
5
5
  res = 0
6
- @data.unpack("C*").each{ |ubyte|
6
+ buf[0,length].unpack("C*").each{ |ubyte|
7
7
  res *= 256
8
8
  res += ubyte
9
9
  }
10
10
  f.set_value(res)
11
11
  return f
12
12
  end
13
+
14
+ def encode(field)
15
+ val = field.get_value.to_i
16
+ out = Numbin.numbin(val,@length)
17
+ return out
18
+ end
19
+
20
+ def self.numbin(number,maxlength)
21
+ out = ""
22
+ while number > 0
23
+ out << (number % 256).chr
24
+ number /= 256
25
+ end
26
+
27
+ # handle length if defined
28
+ if maxlength > 0
29
+ while out.length < maxlength
30
+ out << 0.chr
31
+ end
32
+ out = out[0,maxlength]
33
+ end
34
+ return out.reverse
35
+ end
13
36
  end
14
37
 
15
-
16
-
17
- class Numstr < Base
18
- def build_field
38
+ class Numasc < Base
39
+ def build_field(buf,length)
19
40
  f = Field.new(@id)
20
- # Force conversion of ebcdic number to ascii number
21
- if IsEbcdic(@data)
22
- @data = Ebcdic2Ascii(@data)
23
- end
24
-
25
- f.set_value(@data.to_i)
41
+ f.set_value(buf[0,length].to_i)
26
42
  return f
27
43
  end
44
+
45
+ def encode(field)
46
+ out = field.get_value.to_s
47
+ if @length > 0
48
+ out = out.rjust(@length,"0")
49
+ raise TooLongDataException if out.length > @length
50
+ end
51
+ return out
52
+ end
28
53
  end
29
54
 
30
55
  class String < Base
31
- def build_field
56
+ def build_field(buf,length)
32
57
  f = Field.new(@id)
33
- if IsEbcdic(@data)
34
- @data = Ebcdic2Ascii(@data)
35
- end
36
- f.set_value(@data)
58
+ f.set_value(buf[0,length])
37
59
  return f
38
60
  end
61
+
62
+ def encode(f)
63
+ out = f.get_value
64
+ if @length > 0
65
+ raise TooLongDataException if out.length > @length
66
+ out = out.ljust(@length," ")
67
+ end
68
+ return out
69
+ end
39
70
  end
40
71
 
41
72
  class Binary < Base
42
- def build_field
73
+ def build_field(buf,length)
43
74
  f = Field.new(@id)
44
- f.set_value(@data.unpack("H*").first.upcase)
75
+ f.set_value(buf[0,length].unpack("H*").first.upcase)
45
76
  return f
46
77
  end
78
+
79
+ def encode(f)
80
+ out = [f.get_value].pack("H*")
81
+ if @length > 0
82
+ raise TooLongDataException if out.length > @length
83
+ out = out.ljust(@length,0.chr)
84
+ end
85
+ return out
86
+ end
47
87
  end
48
88
  end
@@ -0,0 +1,68 @@
1
+ module Codec
2
+ class Packed < Base
3
+ def get_pck_length(length)
4
+ ((length + 1) / 2)
5
+ end
6
+
7
+ def eval_pck_length(field)
8
+ if @length > 0
9
+ return @length
10
+ else
11
+ return field.get_value.to_s.length
12
+ end
13
+ end
14
+
15
+ def decode_with_length(buf,length)
16
+ l = eval_length(buf,get_pck_length(length))
17
+ return build_field(buf,l),buf[l,buf.length]
18
+ end
19
+
20
+ def decode(buf)
21
+ return decode_with_length(buf,@length)
22
+ end
23
+ end
24
+
25
+ class Numpck < Packed
26
+ def build_field(buf,length)
27
+ f = Field.new(@id)
28
+ f.set_value(buf[0,length].unpack("H*").first.to_i)
29
+ return f
30
+ end
31
+
32
+ def encode(field)
33
+ out = field.get_value.to_s
34
+ if @length > 0
35
+ out = out.rjust(@length,"0")
36
+ raise TooLongDataException if out.length > @length
37
+ end
38
+ l = out.length
39
+ out.prepend("0") if out.length.odd?
40
+ out = [out].pack("H*")
41
+ return out
42
+ end
43
+ end
44
+
45
+ class Strpck < Packed
46
+ def build_field(buf,length)
47
+ f = Field.new(@id)
48
+ val = buf[0,length].unpack("H*").first
49
+ # TODO : handle odd length for packed field with prefixed length
50
+ val.chop! if @length.odd?
51
+ f.set_value(val)
52
+ return f
53
+ end
54
+
55
+ def encode(field)
56
+ out = field.get_value.to_s
57
+ if @length > 0
58
+ out = out.ljust(@length,"F")
59
+ raise TooLongDataException if out.length > @length
60
+ end
61
+ l = out.length
62
+ out += "F" if out.length.odd?
63
+ out = [out].pack("H*")
64
+ return out
65
+ end
66
+ end
67
+
68
+ end
@@ -0,0 +1,136 @@
1
+ module Codec
2
+ class Prefixedlength < Base
3
+ def initialize(id,length,content)
4
+ # TODO : Check that value_codec has a null length attribute
5
+ @length_codec = length
6
+ @value_codec = content
7
+ @id = id
8
+ end
9
+
10
+ def get_length(length_field)
11
+ length_field.get_value.to_i
12
+ end
13
+
14
+ def decode(buffer)
15
+ l, buf = @length_codec.decode(buffer)
16
+ len = get_length(l)
17
+ if len == 0
18
+ f = Field.new
19
+ f.set_id(@id)
20
+ f.set_value(nil)
21
+ return f,buf
22
+ else
23
+ begin
24
+ f,remain = @value_codec.decode_with_length(buf,len)
25
+ rescue => e
26
+ Logger.error "Error in #{@id} decoder \n #{e.message}\n#{e.backtrace.join(10.chr)}"
27
+ raise ParsingException, e.message
28
+ end
29
+
30
+ f.set_id(@id)
31
+ return f,remain
32
+ end
33
+ end
34
+
35
+ def build_field(buf,length)
36
+ begin
37
+ f,r = decode(buf[0,length])
38
+ rescue ErrorBufferUnderflow => e
39
+ raise ParsingException, e.message
40
+ end
41
+ Logger.error "Error remain data in Prefixedlength" if r != ""
42
+ return f
43
+ end
44
+
45
+ def encode(field)
46
+ val = @value_codec.encode(field)
47
+ length = @length_codec.encode(Field.new.set_value(val.length))
48
+ out = length + val
49
+ return out
50
+ end
51
+ end
52
+
53
+ class Headerlength < Prefixedlength
54
+ def initialize(id,header,content,length_path)
55
+ @path = length_path # length attribute contain the path for length field in header
56
+ @separator = @path.slice!(0).chr # first character contain the separator
57
+ super(id,header,content)
58
+ end
59
+
60
+ def get_length(header_field)
61
+ return header_field.get_value.to_i if @path.length == 0 # Handle simple numeric header field
62
+ length_field = header_field.get_deep_field(@path,@separator)
63
+ if length_field.nil?
64
+ return 0
65
+ else
66
+ length_field.get_value.to_i
67
+ end
68
+ end
69
+
70
+ def decode(buffer)
71
+ f = Field.new
72
+ f.set_id(@id)
73
+ head, buf = @length_codec.decode(buffer)
74
+ head.set_id(@length_codec.id)
75
+ f.add_sub_field(head)
76
+ len = get_length(head)
77
+ if len == 0
78
+ return f,buf
79
+ else
80
+ len -= (buffer.length - buf.length) if @header_length_include
81
+ val,remain = @value_codec.decode_with_length(buf,len)
82
+ val.set_id(@value_codec.id)
83
+ f.add_sub_field(val)
84
+ return f,remain
85
+ end
86
+ end
87
+
88
+ def encode(field)
89
+ # encode content
90
+ content = @value_codec.encode(field.get_sub_field(@value_codec.id))
91
+ head_field = field.get_sub_field(@length_codec.id)
92
+ length_field = head_field.get_deep_field(@path,@separator)
93
+ if length_field.nil?
94
+ raise EncodingException,"Length field #{@path} is not present in header for encoding #{@id} =>
95
+ #{field.to_yaml}"
96
+ end
97
+ # update length field in header
98
+ length_field.set_value(content.length)
99
+ # encode header
100
+ header = @length_codec.encode(head_field)
101
+ return header + content
102
+ end
103
+ end
104
+
105
+ class Headerfulllength < Headerlength
106
+ # TODO : to implement
107
+ end
108
+
109
+ class Tagged < Base
110
+ def initialize(id,tag_codec)
111
+ @subCodecs = {}
112
+ @tag_codec = tag_codec
113
+ @id = id
114
+ end
115
+
116
+ def decode(buffer)
117
+ tag,buf = @tag_codec.decode(buffer)
118
+ if @subCodecs[tag.get_value.to_s].nil?
119
+ raise ParsingException, "Unknown tag #{tag.get_value.to_s} for #{@id} decoder"
120
+ end
121
+ f,buf = @subCodecs[tag.get_value.to_s].decode(buf)
122
+ f.set_id(tag.get_value.to_s)
123
+ return f,buf
124
+ end
125
+
126
+ def encode(field)
127
+ head = Field.new(@tag_codec.id, field.get_id)
128
+ out = @tag_codec.encode(head)
129
+ if @subCodecs[field.get_id].nil?
130
+ raise EncodingException, "Unknown tag #{field.get_id} for #{@id} encoder"
131
+ end
132
+ out += @subCodecs[field.get_id].encode(field)
133
+ return out
134
+ end
135
+ end
136
+ end