diff options
Diffstat (limited to 'lib/asn1kit/types/bit_string.rb')
-rw-r--r-- | lib/asn1kit/types/bit_string.rb | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/lib/asn1kit/types/bit_string.rb b/lib/asn1kit/types/bit_string.rb new file mode 100644 index 0000000..53426dd --- /dev/null +++ b/lib/asn1kit/types/bit_string.rb @@ -0,0 +1,112 @@ +# coding: ASCII-8BIT + +class ASN1Kit::BitString < ASN1Kit::Type + asn1_tag :IMPLICIT, :UNIVERSAL, 3 + asn1_alias "BIT STRING" + + NAMED_BITS = {} + NAMED_BITS_INVERTED = {} + + class << self + def [](*named_bits) + return self if named_bits.empty? + ret = Class.new(self) + hash = named_bits.to_h + hash_inverted = hash.invert + raise "duplicate name(s) in named bit list" if named_bits.size != hash.size + raise "duplicate value(s) in named bit list" if hash.size != hash_inverted.size + ret.const_set(:NAMED_BITS, hash) + ret.const_set(:NAMED_BITS_INVERTED, hash.invert) + ret + end + + def named_bit(name) + self::NAMED_BITS[name] + end + + def named_bits + self::NAMED_BITS.keys + end + + private def inspect_inner + return nil if self::NAMED_BITS.empty? + nns = self::NAMED_BITS.map { |name, value| + "#{name}(#{value})" + }.join(", ") + "{ #{nns} }" + end + + def new_with_names(*list) + if self::NAMED_BITS.empty? + raise TypeError, "BIT STRING without NamedBits defined cannot be " \ + "instantiated by #from_names" + end + return new(String.new, 0) if list.empty? + + bits = list.map { |name| + named_bit(name) or + raise ArgumentError, "invalid name: %p" % name + } + bit_length = bits.max + 1 + str = "0".b * bit_length + bits.each { |pos| str[pos] = "1" } + new([str].pack("B*"), bit_length) + end + end + + attr_reader :string, :bit_length + + def initialize(str, bit_length) + if bit_length <= (str.bytesize - 1) * 8 || bit_length > str.bytesize * 8 + raise ArgumentError, "invalid bit_length" + end + @string = str + @bit_length = bit_length + end + + def set?(name_or_pos) + if name_or_pos.is_a?(String) + name_or_pos = self.class.named_bit(name_or_pos) + end + unless name_or_pos.is_a?(::Integer) + raise TypeError, "invalid argument type: %p" % name_or_pos + end + byte = @string.getbyte(name_or_pos / 8) + byte[7 - name_or_pos % 8] == 1 + end + + def to_der + byte_length = (bit_length / 8r).ceil + unused_bits = byte_length * 8 - bit_length + content = [unused_bits].pack("C") << @string + der_header(byte_length + 1) << content + end + + def ==(other) + return false unless other.is_a?(ASN1Kit::BitString) + return false unless self.class::NAMED_BITS.equal?(other.class::NAMED_BITS) + value == other.value && bit_length == other.bit_length + end +end + +module ASN1Kit::Internal::CompileBitString + refine ASN1Kit::BitString.singleton_class do + def _compile_fixup(state) + self::NAMED_BITS.transform_values! do |value| + if value.is_a?(ASN1Kit::Internal::UnresolvedValue) + vname = value.value.name + value = state.value_symbols[vname] || + raise("unresolved value: %s" % vname) + if !value.is_a?(ASN1Kit::Integer) + raise ASN1Kit::ParseError, "DefinedValue in NamedBit resolved to non-integer: %s" % vname + end + if value.value < 0 + raise ASN1Kit::ParseError, "DefinedValue in NamedBit resolved to negative integer: %s" % vname + end + value = value.value + end + value + end + end + end +end |