1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
# coding: ASCII-8BIT
class ASN1Kit::Enumerated < ASN1Kit::Type
asn1_tag :IMPLICIT, :UNIVERSAL, 10
asn1_alias "ENUMERATED"
class << self
def [](hash, extensible: false)
ret = Class.new(self)
ret.const_set(:EXTENSIBILITY, extensible)
ret.const_set(:ENUMERATIONS, hash)
ret
end
def enumerations
self::ENUMERATIONS.keys
end
def name?(name)
self::ENUMERATIONS.key?(name)
end
def value_for(name)
self::ENUMERATIONS[name]
end
def extensible?
self::EXTENSIBILITY
end
# Will be used during the parsing as well as from #[]
private def set_enumerations(root, additional)
last = -1
root.map! do |name, value|
if value
raise TypeError, "value must be an Integer" if !value.is_a?(::Integer)
[name, last = value]
else
[name, last += 1]
end
end
additional.map! do |name, value|
if value
raise TypeError, "value must be an Integer" if !value.is_a?(::Integer)
if last >= value
raise ArgumentError, "AdditionalEnumeration must be ordered"
end
[name, last = value]
else
[name, last += 1]
end
end
hash = (root + additional).to_h
if hash.size != root.size + additional.size
raise ArgumentError, "duplicate identifier in enumerations"
end
if hash.values.uniq.size != hash.size
raise ArgumentError, "duplicate value in enumerations"
end
const_set(:ENUMERATIONS, hash)
end
end
attr_reader :name
def initialize(name)
unless defined?(self.class::ENUMERATIONS)
raise TypeError, "uninitialized ENUMERATED"
end
self.class::ENUMERATIONS[name]
@name = name
end
def value
self.class::ENUMERATIONS[@name]
end
def to_der
dig = value.digits(256)
dig << 0x00 if dig[-1] >= 0x80
s = dig.pack("C*").reverse
der_header(s.bytesize) << s
end
def ==(other)
return false unless other.is_a?(ASN1Kit::Enumerated)
return false unless other.class::ENUMERATIONS.equal?(self.class::ENUMERATIONS)
@name == other.name
end
end
module ASN1Kit::Internal::CompileEnumerated
refine ASN1Kit::Enumerated.singleton_class do
# Basically same as ASN1Kit::Enumerated::[], except this won't
# do validation of the enumerations.
def _new_internal(root, additional, extensible:)
ret = Class.new(self)
ret.const_set(:EXTENSIBILITY, extensible)
# Let #enumerations work
hash = (root + (additional ? additional : [])).to_h
if hash.size != root.size + (additional ? additional.size : 0)
raise ASN1Kit::ParseError, "duplicate identifier in Enumerations"
end
ret.const_set(:ENUMERATIONS, hash)
# For #_compile_fixup
ret.instance_variable_set(:@_compile_root, root)
ret.instance_variable_set(:@_compile_additional, additional)
ret
end
def _compile_fixup(state)
root = @_compile_root
remove_instance_variable(:@_compile_root)
additional = @_compile_additional
remove_instance_variable(:@_compile_additional)
hash = {}
values_seen = {}
unspecified = []
root.each do |name, value|
if value
v = value.unwrap_as_number(state)
if values_seen[v]
raise ASN1Kit::ParseError, "duplicate value in RootEnumeration: %p" % v
end
values_seen[v] = true
else
unspecified << name
end
hash[name] = v
end
next_value = 0
unspecified.each do |name|
while values_seen[next_value] do next_value += 1 end
hash[name] = next_value
next_value += 1
end
additional.each do |name, value|
if value
v = value.unwrap_as_number(state)
if values_seen[v]
raise ASN1Kit::ParseError, "duplicate value in AdditionalEnumeration: %p" % v
end
if next_value > v
raise ASN1Kit::ParseError, "AdditionalEnumeration must be ordered"
end
else
v = next_value + 1
end
next_value = hash[name] = v
values_seen[v] = true
end
self::ENUMERATIONS.replace(hash)
end
end
end
|