codec 0.0.12 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,5 +1,5 @@
1
1
  module Codec
2
- Logger = Log4r::Logger.new 'codec_logger'
2
+ Logger = Log4r::Logger.new self.name
3
3
  Logger.outputters = Log4r::Outputter.stderr
4
4
  Logger.level=Log4r::INFO
5
- end
5
+ end
@@ -1,109 +1,58 @@
1
1
  module Codec
2
2
  class Packed < Base
3
- def get_pck_length(length)
4
- ((length + 1) / 2)
3
+ def initialize(length, isNumeric = true, isLeftPadded=false, isFPadded = false)
4
+ @length = length
5
+ @isNum = isNumeric
6
+ @fPad = isFPadded
7
+ @lPad = isLeftPadded
5
8
  end
6
9
 
7
- def get_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 encode_with_length(field)
16
- return get_length(field),encode(field)
17
- end
18
-
19
- def decode_with_length(buf,length)
20
- l = eval_length(buf,get_pck_length(length))
21
- return build_field(buf,l),buf[l,buf.length]
22
- end
23
-
24
- def decode(buf)
25
- return decode_with_length(buf,@length)
26
- end
27
- end
10
+ # No more required encode return the field length for upper codec layer
11
+ # def get_length(field)
12
+ # if @length > 0
13
+ # return @length
14
+ # else
15
+ # return field.get_value.to_s.length
16
+ # end
17
+ # end
28
18
 
29
- class Numpck < Packed
30
- def decode_with_length(buf,length)
31
- l = eval_length(buf,get_pck_length(length))
32
- f = Field.new(@id)
33
- val = buf[0,l].unpack("H*").first
34
- if @length.odd?
35
- val = val[1,val.length]
19
+ def check_length(buf,length)
20
+ raise "Length is nil" if length.nil?
21
+ if(length != 0)
22
+ if buf.length < length
23
+ raise BufferUnderflow, "Not enough data for decoding (#{length}/#{buf.length})"
24
+ end
25
+ return length
36
26
  else
37
- val = val[1,val.length] if length.odd?
27
+ return buf.length
38
28
  end
39
- f.set_value(val.to_i)
40
- return f,buf[l,buf.length]
41
- end
29
+ end
42
30
 
43
- def encode(field)
44
- out = field.get_value.to_s
45
- if @length > 0
46
- out = out.rjust(@length,"0")
47
- raise TooLongDataException if out.length > @length
48
- end
49
- l = out.length
50
- out.prepend("0") if out.length.odd?
51
- out = [out].pack("H*")
52
- return out
53
- end
54
- end
55
-
56
- class Strpck < Packed
57
- def decode_with_length(buf,length)
58
- l = eval_length(buf,get_pck_length(length))
59
- f = Field.new(@id)
60
- val = buf[0,l].unpack("H*").first
61
- if @length.odd?
62
- val.chop!
63
- else
64
- val.chop! if length.odd?
65
- end
66
- f.set_value(val)
67
- return f,buf[l,buf.length]
31
+ def decode(buf,f, length = nil)
32
+ length ||= @length
33
+ l = check_length(buf,(length + 1) / 2)
34
+ val = buf.slice!(0...l).unpack("H*").first
35
+ # remove padding if odd length
36
+ ( @lPad ? val.chop! : val.slice!(0) ) if @length.odd? || length.odd?
37
+ val = val.to_i if @isNum
38
+ f.set_value(val)
68
39
  end
69
-
70
- def encode(field)
40
+
41
+ def encode(buf, field)
71
42
  out = field.get_value.to_s
43
+ Logger.debug{ "Encode packed #{out} on #{@length} [#{@isNum}|#{@fPad}|#{@lPad}]" }
44
+ padding = (@fPad ? "F" : "0")
72
45
  if @length > 0
73
- out = out.ljust(@length,"F")
74
- raise TooLongDataException if out.length > @length
75
- end
76
- l = out.length
77
- out += "F" if out.length.odd?
78
- out = [out].pack("H*")
79
- return out
80
- end
81
- end
82
-
83
- class Nstpck < Packed
84
- def decode_with_length(buf,length)
85
- l = eval_length(buf,get_pck_length(length))
86
- f = Field.new(@id)
87
- val = buf[0,l].unpack("H*").first
88
- if @length.odd?
89
- val = val[1,val.length]
90
- else
91
- val = val[1,val.length] if length.odd?
92
- end
93
- f.set_value(val)
94
- return f,buf[l,buf.length]
95
- end
96
-
97
- def encode(field)
98
- out = field.get_value
99
- if @length > 0
100
- out = out.rjust(@length,"0")
46
+ out = (@lPad ? out.ljust(@length,padding) : out.rjust(@length,padding))
101
47
  raise TooLongDataException if out.length > @length
102
48
  end
103
49
  l = out.length
104
- out.prepend("0") if out.length.odd?
50
+ # handle padding if odd length
51
+ (@lPad ? out << padding : out.prepend(padding) )if out.length.odd?
52
+ Logger.debug{ "before packing : #{out}" }
105
53
  out = [out].pack("H*")
106
- return out
54
+ buf << out
55
+ return l
107
56
  end
108
57
  end
109
58
 
@@ -1,60 +1,51 @@
1
1
  module Codec
2
2
  class Prefixedlength < Base
3
- def initialize(id,length,content)
3
+ def initialize(length,content)
4
4
  # TODO : Check that value_codec has a null length attribute
5
5
  @length_codec = length
6
6
  @value_codec = content
7
- @id = id
8
7
  end
9
8
 
10
9
  def get_length(length_field)
11
10
  length_field.get_value.to_i
12
11
  end
13
12
 
14
- def decode(buffer)
15
- l, buf = @length_codec.decode(buffer)
16
- len = get_length(l)
13
+ def decode(buffer, f, length=nil)
14
+ Logger.warn {"Call decode with length on Prefixedlength codec should never happen"} unless length.nil?
15
+ len_field = Field.new
16
+ @length_codec.decode(buffer,len_field)
17
+ len = get_length(len_field)
17
18
  if len == 0
18
- f = Field.new
19
- f.set_id(@id)
20
- f.set_value(nil)
21
- return f,buf
19
+ f.set_value("")
22
20
  else
23
21
  begin
24
- f,remain = @value_codec.decode_with_length(buf,len)
22
+ @value_codec.decode(buffer, f, len)
25
23
  rescue => e
26
- Logger.error "Error in #{@id} decoder \n #{e.message}\n#{e.backtrace.join(10.chr)}"
24
+ Logger.error "Error when decoding field #{f.get_id} \n #{e.message}\n#{e.backtrace.join(10.chr)}"
27
25
  raise ParsingException.new e.message
28
26
  end
29
-
30
- f.set_id(@id)
31
- return f,remain
32
27
  end
33
28
  end
34
-
35
- def build_field(buf,length)
36
- begin
37
- f,r = decode(buf[0,length])
38
- rescue BufferUnderflow => e
39
- raise ParsingException.new e.message
40
- end
41
- Logger.error "Error remain data in Prefixedlength" if r != ""
42
- return f
43
- end
44
29
 
45
- def encode(field)
46
- l, val = @value_codec.encode_with_length(field)
47
- length = @length_codec.encode(Field.new.set_value(l))
48
- out = length + val
49
- return out
30
+ def encode(buf, field)
31
+ out = ""
32
+ content_buf = ""
33
+ len = @value_codec.encode(content_buf, field)
34
+ @length_codec.encode(out, Field.new.set_value(len))
35
+ out << content_buf
36
+ buf << out
37
+ return out.length
50
38
  end
51
39
  end
52
40
 
53
41
  class Headerlength < Prefixedlength
54
- def initialize(id,header,content,length_path)
42
+ def initialize(header,header_id,content,content_id,length_path,total_length = false)
43
+ @header_id = header_id
44
+ @content_id = content_id
55
45
  @path = length_path # length attribute contain the path for length field in header
56
46
  @separator = @path.slice!(0).chr # first character contain the separator
57
- super(id,header,content)
47
+ @total_length = total_length # indicate that length in header is equal to (header + content) length
48
+ super(header,content)
58
49
  end
59
50
 
60
51
  def get_length(header_field)
@@ -66,68 +57,77 @@ module Codec
66
57
  length_field.get_value.to_i
67
58
  end
68
59
  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)
60
+
61
+ def decode(buffer, f, length=nil)
62
+ f.set_value("") # reinit field value
63
+ buffer = buffer.slice!(0...length) if length && length > 0
64
+ initial_len = buffer.size
65
+ head = Field.new(@header_id)
66
+ content = Field.new(@content_id)
67
+ @length_codec.decode(buffer,head)
68
+ h_len = initial_len - buffer.size
75
69
  f.add_sub_field(head)
76
70
  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
71
+ if len > 0
72
+ len -= h_len if @total_length
73
+ @value_codec.decode(buffer, content, len)
74
+ f.add_sub_field(content)
85
75
  end
86
76
  end
87
77
 
88
- def encode(field)
78
+ def encode(buf, field)
89
79
  # encode content
90
- content_field = field.get_sub_field(@value_codec.id)
91
- length, content = @value_codec.encode_with_length(content_field)
92
- head_field = field.get_sub_field(@length_codec.id)
93
- raise EncodingException.new "Missing header for encoding #{@id}" if head_field.empty?
80
+ content_buf = ""
81
+ length = 0
82
+ content = field.get_sub_field(@content_id)
83
+ length = @value_codec.encode(content_buf, content) unless content.nil?
84
+ head = field.get_sub_field(@header_id)
85
+ raise EncodingException.new "Missing header for encoding #{@id}" if head.empty?
94
86
  # update length field in header if length !=0
95
- head_field.set_value(length,@path,@separator) if length !=0
87
+ head.set_value(length,@path,@separator) if length !=0
96
88
  # encode header
97
- header = @length_codec.encode(head_field)
98
- return header + content
89
+ head_buf = ""
90
+ h_len = @length_codec.encode(head_buf,head)
91
+ # TODO : optimize computation for header length
92
+ if length != 0 && @total_length # re-encode header with total length
93
+ length = head_buf.length + content_buf.length
94
+ head.set_value(length,@path,@separator)
95
+ head_buf = ""
96
+ @length_codec.encode(head_buf,head)
97
+ end
98
+ buf << head_buf
99
+ buf << content_buf
100
+ return head_buf.length + content_buf.length
99
101
  end
100
102
  end
101
-
102
- class Headerfulllength < Headerlength
103
- # TODO : to implement
104
- end
105
103
 
106
104
  class Tagged < Base
107
- def initialize(id,tag_codec)
105
+ def initialize(tag_codec)
108
106
  @subCodecs = {}
109
107
  @tag_codec = tag_codec
110
- @id = id
111
108
  end
112
-
113
- def decode(buffer)
114
- tag,buf = @tag_codec.decode(buffer)
109
+
110
+ def decode(buffer, field, length = nil)
111
+ buffer = buffer.slice!(0...length) if length && length > 0
112
+ tag = Field.new("TAG")
113
+ @tag_codec.decode(buffer,tag)
114
+ field.set_id(tag.get_value.to_s)
115
115
  if @subCodecs[tag.get_value.to_s].nil?
116
- raise ParsingException.new "Unknown tag #{tag.get_value.to_s} for #{@id} decoder"
116
+ raise ParsingException.new "Unknown tag #{tag.get_value.to_s} when decoding #{field.get_id}"
117
117
  end
118
- f,buf = @subCodecs[tag.get_value.to_s].decode(buf)
119
- f.set_id(tag.get_value.to_s)
120
- return f,buf
118
+ @subCodecs[tag.get_value.to_s].decode(buffer,field)
121
119
  end
122
120
 
123
- def encode(field)
124
- head = Field.new(@tag_codec.id, field.get_id)
125
- out = @tag_codec.encode(head)
121
+ def encode(buffer, field)
122
+ head = Field.new("tag", field.get_id)
123
+ out = ""
124
+ @tag_codec.encode(out, head)
126
125
  if @subCodecs[field.get_id].nil?
127
126
  raise EncodingException.new "Unknown tag #{field.get_id} for #{@id} encoder"
128
127
  end
129
- out += @subCodecs[field.get_id].encode(field)
130
- return out
128
+ @subCodecs[field.get_id].encode(out, field)
129
+ buffer << out
130
+ return out.length
131
131
  end
132
132
  end
133
133
  end
@@ -1,120 +1,162 @@
1
1
  module Codec
2
2
  class Tlv < Prefixedlength
3
- def initialize(id,length,header,content)
4
- super(id,length,content)
5
- unless header.kind_of?(Codec::Base)
6
- raise InitializeException," Invalid tag codec for Tlv class"
7
- end
3
+ def initialize(length,header,content)
4
+ super(length,content)
8
5
  @tag_codec = header
9
6
  @subCodecs = {}
10
7
  end
11
-
12
- def decode_with_length(buf,length)
13
- l = eval_length(buf,length)
14
- f,r = decode(buf[0,l])
15
- Logger.warn("Remain data in a tlv buffer :[#{r.unpack("H*").first}]") if r.nil? || r.length > 0
16
- return f,buf[l,buf.length]
8
+
9
+ def check_length(buf,length)
10
+ raise "Length is nil" if length.nil?
11
+ if(length != 0)
12
+ if buf.length < length
13
+ raise BufferUnderflow, "Not enough data for decoding (#{length}/#{buf.length})"
14
+ end
15
+ return length
16
+ else
17
+ return buf.length
18
+ end
17
19
  end
18
20
 
19
- def encode(field)
21
+ def encode(buf, field)
20
22
  out = ""
21
23
  fields = field.get_value
22
24
  fields.each do |sf|
23
- out += @tag_codec.encode(Field.new('*',sf.get_id))
25
+ @tag_codec.encode(out, Field.new('*',sf.get_id))
24
26
  if @subCodecs[sf.get_id]
25
- content = @subCodecs[sf.get_id].encode(sf)
26
- length_buffer = @length_codec.encode(Field.new('*',content.length))
27
- out += length_buffer + content
27
+ content = ""
28
+ len = @subCodecs[sf.get_id].encode(content, sf)
29
+ @length_codec.encode(out, Field.new('*',len))
30
+ out << content
28
31
  else
29
- out += super(sf)
32
+ super(out, sf)
30
33
  end
31
34
  end
32
- return out
35
+ buf << out
36
+ return out.length
33
37
  end
34
-
35
- def decode(buffer)
36
- msg = Field.new(@id)
37
- while(buffer.length > 0)
38
- begin
39
- tag,buffer = @tag_codec.decode(buffer)
40
- rescue => e
41
- val = Field.new("ERR")
42
- val.set_value("[#{buffer.unpack("H*").first}]")
43
- msg.add_sub_field(val)
44
- Logger.error("in #{@id} tlv codec: #{e.message}\n #{e.backtrace.join(10.chr)}")
45
- Logger.error("Decoding failed for #{id} : #{msg.to_yaml}")
46
- return msg,""
47
- end
48
- begin
49
- if @subCodecs[tag.get_value.to_s].nil?
50
- val,buffer = super(buffer)
38
+
39
+ def decode(buffer, msg, length = nil)
40
+ unless length.nil?
41
+ l = check_length(buffer,length)
42
+ buffer = buffer.slice!(0...l)
43
+ end
44
+
45
+ tag = Field.new
46
+ begin
47
+ until buffer.empty?
48
+ begin
49
+ @tag_codec.decode(buffer,tag)
50
+ rescue
51
+ raise ParsingException, "Tlv Codec still remaining data but failed to decode tag when decoding field #{msg.get_id}"
52
+ end
53
+ Logger.debug { "Decoding tag #{tag.get_value.to_s}"}
54
+ sf = Field.new(tag.get_value.to_s)
55
+ subcodec = @subCodecs[tag.get_value.to_s]
56
+ if subcodec.nil?
57
+ Logger.debug { "using default codec for tag #{tag.get_value.to_s}"}
58
+ super(buffer,sf)
51
59
  else
52
- l,buf = @length_codec.decode(buffer)
53
- val,buffer = @subCodecs[tag.get_value.to_s].decode_with_length(buf,l.get_value.to_i)
60
+ Logger.debug { "using predefined codec for tag #{tag.get_value.to_s}"}
61
+ len_field = Field.new("len")
62
+ @length_codec.decode(buffer,len_field)
63
+ subcodec.decode(buffer,sf,len_field.get_value.to_i)
54
64
  end
55
- rescue BufferUnderflow => e
56
- val = Field.new
57
- val.set_value("Buffer underflow when parsing this tag : #{e.message} [#{buffer.unpack("H*").first}]")
58
- buffer = ""
59
- rescue
60
- val = Field.new
61
- val.set_value("Parsing error [#{buffer.unpack("H*").first}]")
62
- buffer = ""
65
+ Logger.debug { "Add field #{sf} to tlv"}
66
+ msg.add_sub_field(sf)
63
67
  end
64
-
65
- val.set_id(tag.get_value.to_s)
66
- msg.add_sub_field(val)
68
+ rescue BufferUnderflow => e
69
+ raise ParsingException, "Tlv Codec failed to decode field #{msg.get_id}"
70
+ val = Field.new
71
+ val.set_value("Buffer underflow when parsing tlv #{msg.get_id} [#{buffer.unpack("H*").first}]")
72
+ buffer.clear
73
+ end
74
+ unless buffer.empty? || length.nil?
75
+ Logger.warn("Remain data in a tlv buffer :[#{buffer.unpack("H*").first}]")
67
76
  end
68
- return msg,buffer
69
77
  end
70
78
  end
71
79
 
72
80
  class Bertlv < Base
73
- def initialize(id)
74
- @id = id
81
+
82
+ def initialize
75
83
  @length = 0
76
84
  end
85
+
86
+ def decode(buf,msg,length=nil)
87
+ length ||= buf.length
88
+ buffer = buf.slice!(0...length)
89
+ until buffer.empty?
90
+ sf = Field.new(tag_decode(buffer))
91
+ val = value_decode(buffer, length_decode(buffer))
92
+ sf.set_value(val)
93
+ msg.add_sub_field(sf)
94
+ end
95
+ end
96
+
97
+ def encode(buf, field)
98
+ out = ""
99
+ subfields = field.get_value
100
+ unless subfields.kind_of?(Array)
101
+ raise EncodingException, "Invalid field #{field.to_yaml} for BER Tlv encoding"
102
+ end
103
+
104
+ while subfields.size > 0
105
+ subfield = subfields.shift
106
+ out << tag_encode(subfield.get_id)
107
+ # TODO : Handle value that is not String
108
+ value = value_encode(subfield.get_value)
109
+ out << length_encode(value.length)
110
+ out << value
111
+ end
112
+ buf << out
113
+ return out.length
114
+ end
115
+
116
+ private
77
117
 
78
- def tag_decode(buffer)
79
- buf = buffer.dup
118
+ def tag_decode(buf)
80
119
  # removing trailling bytes
81
- begin ; b = buf.slice!(0).getbyte(0) ; end while (b == 0 || b == 255)
120
+ b = 0
121
+ while (b == 0 || b == 255)
122
+ b = buf.slice!(0).bytes.first
123
+ end
82
124
  tag = b.chr
83
125
  if (b & 0x1F == 0x1F)
84
126
  begin
85
- nb = buf.slice!(0).getbyte(0)
86
- tag += nb.chr
127
+ nb = buf.slice!(0).bytes.first
128
+ tag << nb.chr
87
129
  end while nb & 0x80 == 0x80
88
130
  end
89
- return tag.unpack("H*").first.upcase, buf
131
+ return tag.unpack("H*").first.upcase
90
132
  end
91
133
 
92
134
  def tag_encode(tag)
93
135
  buf = [tag].pack("H*")
94
- check_tag, remain = tag_decode(buf)
95
- if tag != check_tag || remain != ""
136
+ pack_tag = buf.dup
137
+ check_tag = tag_decode(buf)
138
+ unless tag == check_tag && buf.empty?
96
139
  raise EncodingException, "Invalid BER tag [#{tag}]"
97
140
  end
98
- return buf
141
+ return pack_tag
99
142
  end
100
143
 
101
- def length_decode(buffer)
102
- buf = buffer.dup
103
- b = buf.slice!(0).getbyte(0)
144
+ def length_decode(buf)
145
+ b = buf.slice!(0).bytes.first
104
146
  if b & 0x80 == 0x80
105
- # Compute lenght of encoding length sample if first byte is 83 then lenth is encode
147
+ # Compute lenght of encoding length
148
+ # Sample if first byte is 83 then lenth is encode
106
149
  # on 3 bytes
107
150
  loencl = b & 0x7F
108
- lb = buf[0,loencl]
109
- buf = buf[loencl,buf.length]
151
+ lb = buf.slice!(0...loencl)
110
152
  length = 0
111
- while(lb.length > 0)
153
+ lb.bytes.each do |len_byte|
112
154
  length *= 256
113
- length += lb.slice!(0).getbyte(0)
155
+ length += len_byte
114
156
  end
115
- return length,buf
157
+ return length
116
158
  else
117
- return b,buf
159
+ return b
118
160
  end
119
161
  end
120
162
 
@@ -123,59 +165,22 @@ module Codec
123
165
  if out.length > 127
124
166
  raise EncodingException,"Invalid length for BER Tlv #{length}"
125
167
  elsif out.length > 1
126
- out = (128 + out.length).chr + out
168
+ out.prepend((128 + out.length).chr)
127
169
  end
128
170
  return out
129
171
  end
130
172
 
131
- def value_decode(buf,length)
173
+ def value_decode(buf, length)
132
174
  if length > buf.length
133
175
  raise BufferUnderflow.new "Not enough data for parsing BER TLV
134
176
  #{@id} length value #{length} remaining only #{buf.length}"
135
177
  end
136
- value = buf[0,length]
137
- buf = buf[length,buf.length]
138
- return value.unpack("H*").first.upcase,buf
178
+ value = buf.slice!(0...length)
179
+ return value.unpack("H*").first.upcase
139
180
  end
140
181
 
141
182
  def value_encode(unpack_buffer)
142
183
  [unpack_buffer].pack("H*")
143
184
  end
144
-
145
- def build_field(buf,length)
146
- msg = Field.new(@id)
147
- buffer = buf[0,length]
148
- while buffer.length > 0
149
- tag,buffer = tag_decode(buffer)
150
- f = Field.new(tag)
151
- value_length,buffer = length_decode(buffer)
152
- value, buffer = value_decode(buffer,value_length)
153
- f.set_value(value)
154
- msg.add_sub_field(f)
155
- end
156
- return msg
157
- end
158
-
159
- def decode(buffer)
160
- return build_field(buffer,buffer.length),""
161
- end
162
-
163
- def encode(field)
164
- out = ""
165
- subfields = field.get_value
166
- unless subfields.kind_of?(Array)
167
- raise EncodingException, "Invalid field #{field.to_yaml} for BER Tlv encoding"
168
- end
169
-
170
- while subfields.size > 0
171
- subfield = subfields.shift
172
- out += tag_encode(subfield.get_id)
173
- # TODO : Handle value that is not String
174
- value = value_encode(subfield.get_value)
175
- out += length_encode(value.length)
176
- out += value
177
- end
178
- return out
179
- end
180
185
  end
181
186
  end