aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1kit/types/integer.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asn1kit/types/integer.rb')
-rw-r--r--lib/asn1kit/types/integer.rb87
1 files changed, 87 insertions, 0 deletions
diff --git a/lib/asn1kit/types/integer.rb b/lib/asn1kit/types/integer.rb
new file mode 100644
index 0000000..9104a5d
--- /dev/null
+++ b/lib/asn1kit/types/integer.rb
@@ -0,0 +1,87 @@
+# coding: ASCII-8BIT
+
+class ASN1Kit::Integer < ASN1Kit::Type
+ asn1_tag :IMPLICIT, :UNIVERSAL, 2
+ asn1_alias "INTEGER"
+
+ NAMED_NUMBERS = {}
+ NAMED_NUMBERS_INVERTED = {}
+
+ class << self
+ def [](*named_numbers)
+ return self if named_numbers.empty?
+ ret = Class.new(self)
+ hash = named_numbers.to_h
+ hash_inverted = hash.invert
+ raise "duplicate name(s) in named number list" if named_numbers.size != hash.size
+ raise "duplicate value(s) in named number list" if hash.size != hash_inverted.size
+ ret.const_set(:NAMED_NUMBERS, hash)
+ ret.const_set(:NAMED_NUMBERS_INVERTED, hash.invert)
+ ret
+ end
+
+ def named_number(name)
+ self::NAMED_NUMBERS[name]
+ end
+
+ def named_numbers
+ self::NAMED_NUMBERS.keys
+ end
+
+ private def inspect_inner
+ return nil if self::NAMED_NUMBERS.empty?
+ nns = self::NAMED_NUMBERS.map { |name, value| "#{name}(#{value})" }.join(", ")
+ "{ #{nns} }"
+ end
+ end
+
+ attr_reader :value
+
+ def initialize(value)
+ case value
+ when Integer
+ @value = value
+ when String
+ @value = self.class::NAMED_NUMBERS[value] or
+ raise ArgumentError, "invalid name %p" % value
+ else
+ raise TypeError, "invalid value: %p (expected an Integer or a NamedValue label)" % value
+ end
+ end
+
+ def name
+ self.class::NAMED_NUMBERS_INVERTED[@value]
+ end
+
+ def to_der
+ v = @value
+ if v >= 0
+ dig = v.digits(256)
+ dig << 0x00 if dig[-1] >= 0x80
+ s = dig.pack("C*").reverse
+ else
+ v = ~v
+ dig = v.digits(256)
+ dig << 0x00 if dig[-1] >= 0x80
+ s = dig.map!(&:~).pack("C*").reverse
+ end
+ der_header(s.bytesize) << s
+ end
+
+ def ==(other)
+ other.class <= ASN1Kit::Integer && value == other.value
+ end
+
+ def cast_to(type)
+ return type.new(@value) if type <= ASN1Kit::Integer
+ super
+ end
+
+ private def inspect_inner
+ if n = name
+ "#{@value}(#{n})"
+ else
+ "#{@value}"
+ end
+ end
+end