tlv 0.0.3

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.
@@ -0,0 +1,31 @@
1
+ module TLV
2
+ class TLV
3
+ class Raw < Field
4
+ def initialize clazz, desc=nil, name=nil, len=0
5
+ desc ||= "Value"
6
+ super
7
+ end
8
+ def define_accessor clazz
9
+ super
10
+ name = @name
11
+ clazz.instance_eval{
12
+ define_method("#{name}="){|val|
13
+ val ||= ""
14
+ raise("must be a String #{val}") unless val.is_a?(String)
15
+ self.instance_variable_set("@#{name}", val)
16
+ }
17
+
18
+ define_method("#{name}") {
19
+ self.instance_variable_get("@#{name}") || ""
20
+ }
21
+ }
22
+ end
23
+ def parse tlv, bytes, length
24
+ val = bytes[0, length]
25
+ rest = bytes[length, bytes.length]
26
+ tlv.send("#{name}=", val)
27
+ rest
28
+ end
29
+ end
30
+ end
31
+ end # module
@@ -0,0 +1,51 @@
1
+ module TLV
2
+ class TLV
3
+
4
+ class << self
5
+ CLASS_MASK = 0xC0
6
+ UNIVERSAL_CLASS = 0x00
7
+ APPLICATION = 0x40
8
+ CTX_SPECIFIC = 0x80
9
+ PRIVATE = 0xC0
10
+
11
+ BYTE6 = 0x20
12
+
13
+ def universal?
14
+ ((@tag & CLASS_MASK) == UNIVERSAL_CLASS) if @tag
15
+ end
16
+ def application?
17
+ ((@tag & CLASS_MASK) == APPLICATION) if @tag
18
+ end
19
+ def context_specific?
20
+ ((@tag & CLASS_MASK) == CTX_SPECIFIC) if @tag
21
+ end
22
+ def private?
23
+ ((@tag & CLASS_MASK) == PRIVATE) if @tag
24
+ end
25
+ def primitive?
26
+ if @tag
27
+ ((@tag & BYTE6) == 0x00)
28
+ else
29
+ true # `tagless` datastructs are by default primitive
30
+ end
31
+ end
32
+ def constructed?
33
+ ((@tag & BYTE6) == BYTE6) if @tag
34
+ end
35
+ # check the tag is approriate length
36
+ def check_tag
37
+ if (tag & 0x1f) == 0x1f # last 5 bits set, 2 byte tag
38
+ raise "Tag too short: #{b2s(tag)} should be 2 bytes" unless tag.length > 1
39
+ if (tag[1]&0x80) == 0x80
40
+ raise "Tag length incorrect: #{b2s(tag)} should be 3 bytes" unless tag.length == 3
41
+ else
42
+ raise "Tag too long: #{b2s(tag)} should be 2 bytes" if tag.length > 2
43
+ end
44
+ else
45
+ raise "Tag too long: #{b2s(tag)} should be 1 bytes" if tag.length > 1
46
+ end
47
+ end
48
+
49
+ end
50
+ end
51
+ end # module
@@ -0,0 +1,144 @@
1
+
2
+ module TLV
3
+
4
+ def self.b2s bytestr
5
+ return "" unless bytestr
6
+ r = bytestr.unpack("H*")[0]
7
+ r.length > 1 ? r : " "
8
+ end
9
+ def self.s2b string
10
+ return "" unless string
11
+ string = string.gsub(/\s+/, "")
12
+ string = "0" + string unless (string.length % 2 == 0)
13
+ [string].pack("H*")
14
+ end
15
+
16
+ class TLV
17
+
18
+ def self.s2b str
19
+ ::TLV.s2b str
20
+ end
21
+ def self.b2s bytes
22
+ ::TLV.b2s bytes
23
+ end
24
+ # def self.register tag, clazz
25
+ # @tlv_classes ||= {}
26
+ # @tlv_classes[tag] = clazz
27
+ # end
28
+
29
+
30
+ DEBUG = ENV["DEBUG"]
31
+
32
+ # Outputs a warning in case the enironment
33
+ # variable `DEBUG` is set.
34
+ def self.warn mes
35
+ STDERR.puts "[warn] #{mes}" if ENV["DEBUG"]
36
+ end
37
+
38
+
39
+ #
40
+ # class A < Field
41
+ # end
42
+ #
43
+ # class AN < Field
44
+ # end
45
+ #
46
+ # class ANS < Field
47
+ # end
48
+ #
49
+ #
50
+ # class CN < Field
51
+ # end
52
+ #
53
+ # class N < Field
54
+ # end
55
+
56
+ class << self
57
+ attr_accessor :tag
58
+ attr_accessor :display_name
59
+ # If this TLV is placed into another as a subfield, this will be
60
+ # the name of the accessor, default is the rubyfied display_name
61
+ attr_accessor :accessor_name
62
+ def tlv tag, display_name, accessor_name=nil
63
+ @tag = case tag
64
+ when String
65
+ TLV.s2b(tag)
66
+ when Fixnum
67
+ TLV::s2b("%x" % tag)
68
+ end
69
+ def @tag.& flag
70
+ self[0] & flag
71
+ end
72
+ check_tag
73
+ @display_name = display_name
74
+ @accessor_name = accessor_name || rubify_a(display_name)
75
+ TLV.register self
76
+ end
77
+
78
+
79
+
80
+ def fields
81
+ @fields ||= (self == TLV ? [] : superclass.fields.dup)
82
+ end
83
+
84
+ def b len, desc, name=nil
85
+ raise "invalid len #{len}" unless (len%8 == 0)
86
+ fields << B.new(self, desc, name, len)
87
+ end
88
+
89
+ def raw desc=nil, name=nil
90
+ @is_raw = true
91
+ fields << Raw.new(self, desc, name)
92
+ end
93
+
94
+ def is_raw?
95
+ @is_raw == true
96
+ end
97
+
98
+ # for constructed tlv's, add subtags that must be present
99
+ end # meta class thingie
100
+
101
+ def display_name
102
+ self.class.display_name
103
+ end
104
+ def to_s
105
+ longest = 0
106
+ fields.each { |field|
107
+ longest = field.display_name.length if field.display_name.length > longest
108
+ }
109
+ fmt = "%#{longest}s : %s\n"
110
+ str = "#{display_name}"
111
+ str << " (0x#{TLV.b2s(tag)})" if tag
112
+ str << "\n"
113
+
114
+ str << "-" * (str.length-1) << "\n"
115
+ fields.each { |field|
116
+ str << (fmt % [field.display_name, TLV.b2s(self.send(field.name))])
117
+ }
118
+ (mandatory+optional).each { |tlv_class|
119
+ temp_tlv = self.send(tlv_class.accessor_name)
120
+ temp = temp_tlv.to_s
121
+ temp.gsub!(/^/, " ")
122
+ str << temp
123
+ }
124
+ str
125
+ end
126
+
127
+
128
+ def fields
129
+ self.class.fields
130
+ end
131
+
132
+ def mandatory
133
+ self.class.mand_tags
134
+ end
135
+ def optional
136
+ self.class.opt_tags
137
+ end
138
+
139
+ def tag
140
+ self.class.tag
141
+ end
142
+ end
143
+
144
+ end # module
@@ -0,0 +1,76 @@
1
+ module TLV
2
+ class TLV
3
+
4
+ def get_bytes
5
+ if self.class.primitive? || (self.is_a?(DGI) && fields.length!=0) || self.class.is_raw?
6
+ bytes = get_bytes_primitive
7
+ else
8
+ bytes = get_bytes_constructed
9
+ end
10
+ end
11
+ def get_bytes_primitive
12
+ bytes = ""
13
+ fields.each { |field|
14
+ bytes << self.send(field.name)
15
+ }
16
+ bytes
17
+ end
18
+
19
+ def get_bytes_constructed
20
+ bytes = ""
21
+ mandatory.each {|t|
22
+ tlv = self.send(t.accessor_name)
23
+ raise "Mandatory subtag #{t} not set!" unless tlv
24
+ bytes << tlv.to_b
25
+ }
26
+ optional.each { |t|
27
+ tlv = self.send(t.accessor_name)
28
+ bytes << tlv.to_b if tlv
29
+ }
30
+ bytes
31
+ end
32
+
33
+ def get_len_bytes len
34
+
35
+ # one byte max = 127
36
+ # two = 255 (2**8)-1
37
+ # three = 65535 (2 ** 16) -1
38
+ # four = 16777215 (2 ** 32) -1
39
+ num_len_bytes = case len
40
+ when 0..127 : 1
41
+ when 128..255 : 2
42
+ when 256..65535 : 3 # short
43
+ when 65536..4294967295 : 5 # long, skip 3 byte len, too difficult :)
44
+ else
45
+ raise "Don't be silly"
46
+ end
47
+ len_bytes = case num_len_bytes
48
+ when 1 : "" << len
49
+ when 2 : "\x81" << len
50
+ when 3 : "\x82" << [len].pack("n")
51
+ when 5 : "\x84" << [len].pack("N")
52
+ else
53
+ raise "Can't happen"
54
+ end
55
+ return len_bytes
56
+ end
57
+
58
+ # this provides an opportunity to manipulate the payload
59
+ # before it is assembled. Expects a proc taking the bytes of the
60
+ # payload and returning the new version of the bytes.
61
+ def payload_hook= hook
62
+ @hook = hook
63
+ end
64
+
65
+ def to_b
66
+ bytes = get_bytes
67
+ bytes = @hook.call(bytes) if @hook
68
+ if tag
69
+ bytes.insert 0, get_len_bytes(bytes.length)
70
+ bytes.insert 0, tag
71
+ end
72
+ bytes
73
+ end
74
+ end
75
+ end # module
76
+
@@ -0,0 +1,106 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/tlv'
3
+
4
+ class TestTLVConstructed < Test::Unit::TestCase
5
+ include TLV
6
+ def setup
7
+ end
8
+
9
+ class TLVTagTest < TLV
10
+ tlv "41", "Test TLV"
11
+ b 8, "first field", :first
12
+ b 8, "second field", :second
13
+ end
14
+ class TLVTestCons < TLV
15
+ tlv "32", "Test Constructed"
16
+ mandatory TLVTagTest, "tlv_tag_test"
17
+ mandatory :tag => "43",
18
+ :display_name => "Another Test"
19
+ end
20
+
21
+ class TLVTestCons2 < TLV
22
+ tlv "32", "Test Constructed 32 2"
23
+ optional TLVTagTest, "tlv_tag_test"
24
+ optional :tag => "43",
25
+ :display_name => "Another Test"
26
+ end
27
+
28
+ class DGITest < DGI
29
+ tlv "9102", "Random Test Data"
30
+ mandatory TLVTagTest, :test
31
+ optional :tag => "43",
32
+ :display_name => "Another Test"
33
+ end
34
+
35
+ def basics t
36
+ assert(t.methods.include?("tlv_tag_test" ))
37
+ assert(t.methods.include?("tlv_tag_test="))
38
+ assert(t.methods.include?("another_test" ))
39
+ assert(t.methods.include?("another_test="))
40
+ end
41
+ def test_basics
42
+ t = TLVTestCons.new
43
+ basics t
44
+ assert_nothing_raised {
45
+ t = TLVTestCons::AnotherTest.new
46
+ }
47
+ t = TLVTestCons2.new
48
+ basics t
49
+ assert_nothing_raised {
50
+ t = TLVTestCons2::AnotherTest.new
51
+ }
52
+ end
53
+
54
+ def test_const_to_b
55
+ t = TLVTagTest.new
56
+ t.first = "\x01"
57
+ t.second = "\x02"
58
+
59
+ t2 = TLVTestCons::AnotherTest.new
60
+ t2.value = "\x34\x56"
61
+
62
+ t3 = TLVTestCons.new
63
+ t3.tlv_tag_test= t
64
+ t3.another_test= t2
65
+
66
+ assert_equal(TLV.s2b("32084102010243023456"), t3.to_b)
67
+
68
+ bytes = t3.to_b
69
+ t, rest = TLVTestCons._parse bytes*2
70
+ assert_equal bytes, rest
71
+ assert_equal "\x01", t.tlv_tag_test.first
72
+ assert_equal "\x02", t.tlv_tag_test.second
73
+ assert_equal "\x34\x56", t.another_test.value
74
+
75
+ t, rest = TLV._parse rest
76
+ assert_equal "\x01", t.tlv_tag_test.first
77
+ assert_equal "\x02", t.tlv_tag_test.second
78
+ assert_equal "\x34\x56", t.another_test.value
79
+ end
80
+
81
+ def test_const_direct_value
82
+ t = TLVTagTest.new
83
+ t.first = "\x01"
84
+ t.second = "\x02"
85
+
86
+ t2 = TLVTestCons.new
87
+ t2.tlv_tag_test=t
88
+ t2.another_test="\x34\x56"
89
+
90
+ assert_equal(TLV.s2b("32084102010243023456"), t2.to_b)
91
+
92
+ end
93
+
94
+ def test_const_dgi
95
+ te = TLVTagTest.new
96
+ te.first= "\x01"
97
+ te.second= "\x02"
98
+ dgi = DGITest.new
99
+ dgi.test= te
100
+ dgi.another_test="\x34\x56"
101
+
102
+ assert_equal(TLV.s2b("9102084102010243023456"), dgi.to_b)
103
+ end
104
+
105
+
106
+ end
@@ -0,0 +1,122 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/tlv'
3
+
4
+ class TestDGI < Test::Unit::TestCase
5
+ include TLV
6
+ def setup
7
+ end
8
+
9
+ class TLVTest < DGI
10
+ tlv "0101", "Test TLV"
11
+ b 8, "first field", :first
12
+ b 8, "second field", :second
13
+ end
14
+
15
+ class TLVTest2 < DGI
16
+ tlv "0102", "Test Rubify"
17
+ b 8, "My Test"
18
+ b 8, "Oh M@i!"
19
+ end
20
+
21
+ class TLVTest3 < DGI
22
+ tlv "9F00", "Test Raw"
23
+ raw
24
+ end
25
+ class TLVTestNoTag < DGI
26
+ b 8, "first field", :first
27
+ b 8, "second field", :second
28
+ end
29
+
30
+ def basics tlv
31
+ tlv.first="\x01"
32
+ tlv.second="\xAA"
33
+ assert_equal "\x01", tlv.first
34
+ assert_equal "\xaa", tlv.second
35
+
36
+ assert_raise(RuntimeError) {
37
+ tlv.first="\x02\x03"
38
+ }
39
+ assert_raise(RuntimeError) {
40
+ tlv.first=Time.new
41
+ }
42
+ assert_raise(RuntimeError) {
43
+ tlv.second=1
44
+ }
45
+ end
46
+
47
+ def test_basics
48
+ t = TLVTest.new
49
+ basics t
50
+ assert_equal "\x01\x01\x02\x01\xaa", t.to_b
51
+
52
+ end
53
+
54
+ def test_parse_tag
55
+
56
+ end
57
+
58
+ def test_length
59
+ t = TLVTest3.new
60
+ t.value = ""
61
+ assert_equal "\x00", t.to_b[2,1]
62
+ t.value = "1"
63
+ assert_equal "\x01\x31", t.to_b[2,2]
64
+ t.value = "1"*127
65
+ assert_equal "\x7F\x31", t.to_b[2,2]
66
+ t.value = "1"*128
67
+ assert_equal "\x80\x31", t.to_b[2,2]
68
+ t.value = "1"*255
69
+ assert_equal "\xFf\x00\xff\x31", t.to_b[2,4]
70
+ t.value = "1"*256
71
+ assert_equal "\xFF\x01\x00\x31", t.to_b[2,4]
72
+
73
+ assert_raises (RuntimeError) {
74
+ t.value = "1"*65535
75
+ assert_equal "\x82\xFF\xFF\x31", t.to_b[2,4]
76
+ }
77
+
78
+ o = Object.new
79
+ def o.length
80
+ return 4294967296
81
+ end
82
+
83
+ assert_raises (RuntimeError) {
84
+ t.value=o
85
+ t.to_b
86
+ }
87
+ end
88
+
89
+ def test_parse
90
+ t = TLVTest.new
91
+ assert_equal "\x00", t.first
92
+ t.first="\x01"
93
+ t.second="\xAA"
94
+
95
+ assert "\x01", t.first
96
+ bytes = t.to_b
97
+ # t, rest = TLVTest.parse bytes
98
+ # assert_equal TLVTest, t.class
99
+ # assert_equal "\x01", t.first
100
+ # assert_equal "\xAA", t.second
101
+ end
102
+ def test_rubify
103
+ t = TLVTest2.new
104
+ t.my_test = "\x01"
105
+ assert_equal "\x01", t.my_test
106
+ t.oh_mi = "\x02"
107
+ assert_equal "\x02", t.oh_mi
108
+ end
109
+ def test_raw
110
+ t = TLVTest3.new
111
+ #puts t.methods.sort
112
+ t.value= "bumsi"
113
+ assert_equal "Test Raw", TLVTest3.display_name
114
+ assert_equal "bumsi", t.value
115
+ bytes = t.to_b
116
+ # t, rest = TLVTest3.parse bytes
117
+ # assert_equal "bumsi", t.value
118
+ # assert_equal TLVTest3, t.class
119
+ end
120
+
121
+
122
+ end