struct Tag [src]

Fields

number: Number
constructed: boolWhether this ASN.1 type contains other ASN.1 types.
class: Class

Members

Source

pub const Tag = struct { number: Number, /// Whether this ASN.1 type contains other ASN.1 types. constructed: bool, class: Class, /// These values apply to class == .universal. pub const Number = enum(u16) { // 0 is reserved by spec boolean = 1, integer = 2, bitstring = 3, octetstring = 4, null = 5, oid = 6, object_descriptor = 7, real = 9, enumerated = 10, embedded = 11, string_utf8 = 12, oid_relative = 13, time = 14, // 15 is reserved to mean that the tag is >= 32 sequence = 16, /// Elements may appear in any order. sequence_of = 17, string_numeric = 18, string_printable = 19, string_teletex = 20, string_videotex = 21, string_ia5 = 22, utc_time = 23, generalized_time = 24, string_graphic = 25, string_visible = 26, string_general = 27, string_universal = 28, string_char = 29, string_bmp = 30, date = 31, time_of_day = 32, date_time = 33, duration = 34, /// IRI = Internationalized Resource Identifier oid_iri = 35, oid_iri_relative = 36, _, }; pub const Class = enum(u2) { universal, application, context_specific, private, }; pub fn init(number: Tag.Number, constructed: bool, class: Tag.Class) Tag { return .{ .number = number, .constructed = constructed, .class = class }; } pub fn universal(number: Tag.Number, constructed: bool) Tag { return .{ .number = number, .constructed = constructed, .class = .universal }; } pub fn decode(reader: anytype) !Tag { const tag1: FirstTag = @bitCast(try reader.readByte()); var number: u14 = tag1.number; if (tag1.number == 15) { const tag2: NextTag = @bitCast(try reader.readByte()); number = tag2.number; if (tag2.continues) { const tag3: NextTag = @bitCast(try reader.readByte()); number = (number << 7) + tag3.number; if (tag3.continues) return error.InvalidLength; } } return Tag{ .number = @enumFromInt(number), .constructed = tag1.constructed, .class = tag1.class, }; } pub fn encode(self: Tag, writer: anytype) @TypeOf(writer).Error!void { var tag1 = FirstTag{ .number = undefined, .constructed = self.constructed, .class = self.class, }; var buffer: [3]u8 = undefined; var stream = std.io.fixedBufferStream(&buffer); var writer2 = stream.writer(); switch (@intFromEnum(self.number)) { 0...std.math.maxInt(u5) => |n| { tag1.number = @intCast(n); writer2.writeByte(@bitCast(tag1)) catch unreachable; }, std.math.maxInt(u5) + 1...std.math.maxInt(u7) => |n| { tag1.number = 15; const tag2 = NextTag{ .number = @intCast(n), .continues = false }; writer2.writeByte(@bitCast(tag1)) catch unreachable; writer2.writeByte(@bitCast(tag2)) catch unreachable; }, else => |n| { tag1.number = 15; const tag2 = NextTag{ .number = @intCast(n >> 7), .continues = true }; const tag3 = NextTag{ .number = @truncate(n), .continues = false }; writer2.writeByte(@bitCast(tag1)) catch unreachable; writer2.writeByte(@bitCast(tag2)) catch unreachable; writer2.writeByte(@bitCast(tag3)) catch unreachable; }, } _ = try writer.write(stream.getWritten()); } const FirstTag = packed struct(u8) { number: u5, constructed: bool, class: Tag.Class }; const NextTag = packed struct(u8) { number: u7, continues: bool }; pub fn toExpected(self: Tag) ExpectedTag { return ExpectedTag{ .number = self.number, .constructed = self.constructed, .class = self.class, }; } pub fn fromZig(comptime T: type) Tag { switch (@typeInfo(T)) { .@"struct", .@"enum", .@"union" => { if (@hasDecl(T, "asn1_tag")) return T.asn1_tag; }, else => {}, } switch (@typeInfo(T)) { .@"struct", .@"union" => return universal(.sequence, true), .bool => return universal(.boolean, false), .int => return universal(.integer, false), .@"enum" => |e| { if (@hasDecl(T, "oids")) return Oid.asn1_tag; return universal(if (e.is_exhaustive) .enumerated else .integer, false); }, .optional => |o| return fromZig(o.child), .null => return universal(.null, false), else => @compileError("cannot map Zig type to asn1_tag " ++ @typeName(T)), } } }