The ID of a Git object.
pub const Oid = union(Format)
pub const Oid = union(Format) {
sha1: [Sha1.digest_length]u8,
sha256: [Sha256.digest_length]u8,
pub const max_formatted_length = len: {
var max: usize = 0;
for (std.enums.values(Format)) |f| {
max = @max(max, f.formattedLength());
}
break :len max;
};
pub const Format = enum {
sha1,
sha256,
pub fn byteLength(f: Format) usize {
return switch (f) {
.sha1 => Sha1.digest_length,
.sha256 => Sha256.digest_length,
};
}
pub fn formattedLength(f: Format) usize {
return 2 * f.byteLength();
}
};
const Hasher = union(Format) {
sha1: Sha1,
sha256: Sha256,
fn init(oid_format: Format) Hasher {
return switch (oid_format) {
.sha1 => .{ .sha1 = Sha1.init(.{}) },
.sha256 => .{ .sha256 = Sha256.init(.{}) },
};
}
// Must be public for use from HashedReader and HashedWriter.
pub fn update(hasher: *Hasher, b: []const u8) void {
switch (hasher.*) {
inline else => |*inner| inner.update(b),
}
}
fn finalResult(hasher: *Hasher) Oid {
return switch (hasher.*) {
inline else => |*inner, tag| @unionInit(Oid, @tagName(tag), inner.finalResult()),
};
}
};
const Hashing = union(Format) {
sha1: Io.Writer.Hashing(Sha1),
sha256: Io.Writer.Hashing(Sha256),
fn init(oid_format: Format, buffer: []u8) Hashing {
return switch (oid_format) {
.sha1 => .{ .sha1 = .init(buffer) },
.sha256 => .{ .sha256 = .init(buffer) },
};
}
fn writer(h: *@This()) *Io.Writer {
return switch (h.*) {
inline else => |*inner| &inner.writer,
};
}
fn final(h: *@This()) Oid {
switch (h.*) {
inline else => |*inner, tag| {
inner.writer.flush() catch unreachable; // hashers cannot fail
return @unionInit(Oid, @tagName(tag), inner.hasher.finalResult());
},
}
}
};
pub fn fromBytes(oid_format: Format, bytes: []const u8) Oid {
assert(bytes.len == oid_format.byteLength());
return switch (oid_format) {
inline else => |tag| @unionInit(Oid, @tagName(tag), bytes[0..comptime tag.byteLength()].*),
};
}
pub fn readBytes(oid_format: Format, reader: *Io.Reader) !Oid {
return switch (oid_format) {
inline else => |tag| @unionInit(Oid, @tagName(tag), (try reader.takeArray(tag.byteLength())).*),
};
}
pub fn parse(oid_format: Format, s: []const u8) error{InvalidOid}!Oid {
switch (oid_format) {
inline else => |tag| {
if (s.len != tag.formattedLength()) return error.InvalidOid;
var bytes: [tag.byteLength()]u8 = undefined;
for (&bytes, 0..) |*b, i| {
b.* = std.fmt.parseUnsigned(u8, s[2 * i ..][0..2], 16) catch return error.InvalidOid;
}
return @unionInit(Oid, @tagName(tag), bytes);
},
}
}
test parse {
try testing.expectEqualSlices(
u8,
&.{ 0xCE, 0x91, 0x9C, 0xCF, 0x45, 0x95, 0x18, 0x56, 0xA7, 0x62, 0xFF, 0xDB, 0x8E, 0xF8, 0x50, 0x30, 0x1C, 0xD8, 0xC5, 0x88 },
&(try parse(.sha1, "ce919ccf45951856a762ffdb8ef850301cd8c588")).sha1,
);
try testing.expectError(error.InvalidOid, parse(.sha256, "ce919ccf45951856a762ffdb8ef850301cd8c588"));
try testing.expectError(error.InvalidOid, parse(.sha1, "7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a"));
try testing.expectEqualSlices(
u8,
&.{ 0x7F, 0x44, 0x4A, 0x92, 0xBD, 0x45, 0x72, 0xEE, 0x4A, 0x28, 0xB2, 0xC6, 0x30, 0x59, 0x92, 0x4A, 0x9C, 0xA1, 0x82, 0x91, 0x38, 0x55, 0x3E, 0xF3, 0xE7, 0xC4, 0x1E, 0xE1, 0x59, 0xAF, 0xAE, 0x7A },
&(try parse(.sha256, "7f444a92bd4572ee4a28b2c63059924a9ca1829138553ef3e7c41ee159afae7a")).sha256,
);
try testing.expectError(error.InvalidOid, parse(.sha1, "ce919ccf"));
try testing.expectError(error.InvalidOid, parse(.sha256, "ce919ccf"));
try testing.expectError(error.InvalidOid, parse(.sha1, "master"));
try testing.expectError(error.InvalidOid, parse(.sha256, "master"));
try testing.expectError(error.InvalidOid, parse(.sha1, "HEAD"));
try testing.expectError(error.InvalidOid, parse(.sha256, "HEAD"));
}
pub fn parseAny(s: []const u8) error{InvalidOid}!Oid {
return for (std.enums.values(Format)) |f| {
if (s.len == f.formattedLength()) break parse(f, s);
} else error.InvalidOid;
}
pub fn format(oid: Oid, writer: *Io.Writer) Io.Writer.Error!void {
try writer.print("{x}", .{oid.slice()});
}
pub fn slice(oid: *const Oid) []const u8 {
return switch (oid.*) {
inline else => |*bytes| bytes,
};
}
}