diff --git a/lib/std/crypto/codecs.zig b/lib/std/crypto/codecs.zig index 7cf92b1117ff..21be2dda186e 100644 --- a/lib/std/crypto/codecs.zig +++ b/lib/std/crypto/codecs.zig @@ -1,3 +1,8 @@ pub const asn1 = @import("codecs/asn1.zig"); pub const base64 = @import("codecs/base64_hex_ct.zig").base64; pub const hex = @import("codecs/base64_hex_ct.zig").hex; + +test { + _ = @import("codecs/asn1.zig"); + _ = @import("codecs/base64_hex_ct.zig"); +} diff --git a/lib/std/crypto/codecs/asn1.zig b/lib/std/crypto/codecs/asn1.zig index 968ef1161a18..0b4c50644c30 100644 --- a/lib/std/crypto/codecs/asn1.zig +++ b/lib/std/crypto/codecs/asn1.zig @@ -90,7 +90,7 @@ pub const Tag = struct { }; } - pub fn encode(self: Tag, writer: *std.Io.Writer) @TypeOf(writer).Error!void { + pub fn encode(self: Tag, writer: *std.Io.Writer) std.Io.Writer.Error!void { var tag1 = FirstTag{ .number = undefined, .constructed = self.constructed, @@ -98,7 +98,7 @@ pub const Tag = struct { }; var buffer: [3]u8 = undefined; - var writer2: std.Io.Writer = .init(&buffer); + var writer2: std.Io.Writer = .fixed(&buffer); switch (@intFromEnum(self.number)) { 0...std.math.maxInt(u5) => |n| { @@ -183,7 +183,7 @@ pub const Element = struct { } }; - pub const DecodeError = error{ InvalidLength, EndOfStream }; + pub const DecodeError = error{ InvalidLength, EndOfStream, ReadFailed }; /// Safely decode a DER/BER/CER element at `index`: /// - Ensures length uses shortest form diff --git a/lib/std/crypto/codecs/asn1/Oid.zig b/lib/std/crypto/codecs/asn1/Oid.zig index be5bc7e7ff8b..4f3f36236290 100644 --- a/lib/std/crypto/codecs/asn1/Oid.zig +++ b/lib/std/crypto/codecs/asn1/Oid.zig @@ -47,7 +47,7 @@ test fromDot { } } -pub fn toDot(self: Oid, writer: anytype) @TypeOf(writer).Error!void { +pub fn toDot(self: Oid, writer: anytype) std.Io.Writer.Error!void { const encoded = self.encoded; const first = @divTrunc(encoded[0], 40); const second = encoded[0] - first * 40; @@ -81,7 +81,7 @@ test toDot { for (test_cases) |t| { var stream: std.Io.Writer = .fixed(&buf); try toDot(Oid{ .encoded = t.encoded }, &stream); - try std.testing.expectEqualStrings(t.dot_notation, stream.written()); + try std.testing.expectEqualStrings(t.dot_notation, stream.buffered()); } } diff --git a/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig b/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig index b761a93345d0..c61b108d095c 100644 --- a/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig +++ b/lib/std/crypto/codecs/asn1/der/ArrayListReverse.zig @@ -7,18 +7,28 @@ const std = @import("std"); const Allocator = std.mem.Allocator; +const Io = std.Io; const assert = std.debug.assert; const testing = std.testing; data: []u8, capacity: usize, allocator: Allocator, +writer: Io.Writer, const ArrayListReverse = @This(); const Error = Allocator.Error; pub fn init(allocator: Allocator) ArrayListReverse { - return .{ .data = &.{}, .capacity = 0, .allocator = allocator }; + return .{ + .data = &.{}, + .capacity = 0, + .allocator = allocator, + .writer = .{ + .buffer = &.{}, + .vtable = &vtable, + }, + }; } pub fn deinit(self: *ArrayListReverse) void { @@ -67,6 +77,67 @@ pub fn clearAndFree(self: *ArrayListReverse) void { self.capacity = 0; } +const vtable: Io.Writer.VTable = .{ + .drain = &drain, +}; + +fn drain(w: *Io.Writer, data: []const []const u8, splat: usize) Io.Writer.Error!usize { + const self: *ArrayListReverse = @fieldParentPtr("writer", w); + + assert(w.buffered().len == 0); + + if (data.len == 1 and splat == 1) { + @branchHint(.likely); + self.prependSlice(data[0]) catch return error.WriteFailed; + return data[0].len; + } + + const count = Io.Writer.countSplat(data, splat); + self.ensureCapacity(self.data.len + count) catch return error.WriteFailed; + const end = self.data.ptr; + const begin = end - count; + var slice = begin[0..count]; + + for (data[0 .. data.len - 1]) |bytes| { + @memcpy(slice[0..bytes.len], bytes); + slice = slice[bytes.len..]; + } + for (0..splat) |_| { + const bytes = data[data.len - 1]; + @memcpy(slice[0..bytes.len], bytes); + slice = slice[bytes.len..]; + } + + self.data.ptr = begin; + self.data.len += count; + return count; +} + +test drain { + var b: ArrayListReverse = .init(std.testing.allocator); + defer b.deinit(); + + var n = try b.writer.write(&.{ 1, 2, 3 }); + try std.testing.expectEqual(3, n); + try std.testing.expectEqualSlices(u8, &.{ 1, 2, 3 }, b.data); + + n = try b.writer.writeSplat(&.{ + &.{ 4, 5, 6 }, + &.{ 7, 8, 9 }, + }, 2); + try std.testing.expectEqual(9, n); + try std.testing.expectEqualSlices( + u8, + &.{ + 4, 5, 6, + 7, 8, 9, + 7, 8, 9, + 1, 2, 3, + }, + b.data, + ); +} + /// The caller owns the returned memory. /// Capacity is cleared, making deinit() safe but unnecessary to call. pub fn toOwnedSlice(self: *ArrayListReverse) Error![]u8 { diff --git a/lib/std/crypto/codecs/asn1/der/Encoder.zig b/lib/std/crypto/codecs/asn1/der/Encoder.zig index 82b7e62910ab..3151cac3ad19 100644 --- a/lib/std/crypto/codecs/asn1/der/Encoder.zig +++ b/lib/std/crypto/codecs/asn1/der/Encoder.zig @@ -117,8 +117,8 @@ pub fn tagBytes(self: *Encoder, tag_: Tag, bytes: []const u8) !void { } /// Warning: This writer writes backwards. `fn print` will NOT work as expected. -pub fn writer(self: *Encoder) ArrayListReverse.Writer { - return self.buffer.writer(); +pub fn writer(self: *Encoder) *std.Io.Writer { + return &self.buffer.writer; } fn int(self: *Encoder, comptime T: type, value: T) !void {