codec 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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