diff options
Diffstat (limited to 'lib/asn1kit/types/enumerated.rb')
-rw-r--r-- | lib/asn1kit/types/enumerated.rb | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/lib/asn1kit/types/enumerated.rb b/lib/asn1kit/types/enumerated.rb new file mode 100644 index 0000000..79a26c7 --- /dev/null +++ b/lib/asn1kit/types/enumerated.rb @@ -0,0 +1,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 |