# 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