codec 0.0.12 → 1.0.0

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.
@@ -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