Skip to content

Commit e5a31f6

Browse files
committed
feat:refactor cid
Signed-off-by: Chen Kai <281165273grape@gmail.com>
1 parent 1f638b0 commit e5a31f6

File tree

3 files changed

+155
-31
lines changed

3 files changed

+155
-31
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,6 @@
77
[submodule "src/spec/rust-multiaddr"]
88
path = src/spec/rust-multiaddr
99
url = https://github.com/multiformats/rust-multiaddr
10+
[submodule "src/spec/rust-cid"]
11+
path = src/spec/rust-cid
12+
url = https://github.com/multiformats/rust-cid

src/cid.zig

Lines changed: 151 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,44 @@
11
const std = @import("std");
22
const Allocator = std.mem.Allocator;
3-
const testing = std.testing;
3+
4+
pub const Error = error{
5+
UnknownCodec,
6+
InputTooShort,
7+
ParsingError,
8+
InvalidCidVersion,
9+
InvalidCidV0Codec,
10+
InvalidCidV0Multihash,
11+
InvalidCidV0Base,
12+
VarIntDecodeError,
13+
InvalidExplicitCidV0,
14+
};
415

516
pub const CidVersion = enum(u64) {
617
V0 = 0,
718
V1 = 1,
19+
20+
pub fn isV0Str(data: []const u8) bool {
21+
return data.len == 46 and std.mem.startsWith(u8, data, "Qm");
22+
}
23+
24+
pub fn isV0Binary(data: []const u8) bool {
25+
return data.len == 34 and std.mem.startsWith(u8, data, &[_]u8{ 0x12, 0x20 });
26+
}
827
};
928

1029
pub const Codec = enum(u64) {
1130
Raw = 0x55,
1231
DagPb = 0x70,
1332
DagCbor = 0x71,
33+
34+
pub fn fromInt(value: u64) Error!Codec {
35+
return switch (value) {
36+
0x55 => .Raw,
37+
0x70 => .DagPb,
38+
0x71 => .DagCbor,
39+
else => Error.UnknownCodec,
40+
};
41+
}
1442
};
1543

1644
pub const Cid = struct {
@@ -20,6 +48,10 @@ pub const Cid = struct {
2048
allocator: Allocator,
2149

2250
pub fn init(allocator: Allocator, version: CidVersion, codec: Codec, hash: []const u8) !Cid {
51+
if (version == .V0 and codec != .DagPb) {
52+
return Error.InvalidCidV0Codec;
53+
}
54+
2355
const hash_copy = try allocator.dupe(u8, hash);
2456
return Cid{
2557
.version = version,
@@ -29,56 +61,144 @@ pub const Cid = struct {
2961
};
3062
}
3163

64+
pub fn fromBytes(allocator: Allocator, bytes: []const u8) !Cid {
65+
if (CidVersion.isV0Binary(bytes)) {
66+
return Cid.init(allocator, .V0, .DagPb, bytes[2..]);
67+
}
68+
69+
if (bytes.len < 2) {
70+
return Error.InputTooShort;
71+
}
72+
73+
const version = try std.meta.intToEnum(CidVersion, bytes[0]);
74+
const codec = try Codec.fromInt(bytes[1]);
75+
76+
return Cid.init(allocator, version, codec, bytes[2..]);
77+
}
78+
3279
pub fn deinit(self: *Cid) void {
3380
self.allocator.free(self.hash);
3481
}
3582

36-
pub fn format(self: Cid, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void {
37-
_ = fmt;
38-
_ = options;
83+
pub fn toBytes(self: Cid) ![]u8 {
84+
var buf = try self.allocator.alloc(u8, 2 + self.hash.len);
3985

4086
switch (self.version) {
4187
.V0 => {
42-
if (self.codec != .DagPb) {
43-
return error.InvalidV0Codec;
44-
}
45-
try writer.writeAll(try std.fmt.allocPrint(self.allocator, "Qm{}", .{std.fmt.fmtSliceHexLower(self.hash)}));
88+
buf[0] = 0x12;
89+
buf[1] = 0x20;
4690
},
4791
.V1 => {
48-
try writer.writeAll(try std.fmt.allocPrint(
49-
self.allocator,
50-
"b{}{}{}",
51-
.{
52-
@intFromEnum(self.version),
53-
@intFromEnum(self.codec),
54-
std.fmt.fmtSliceHexLower(self.hash),
55-
},
56-
));
92+
buf[0] = @as(u8, @intCast(@intFromEnum(self.version)));
93+
buf[1] = @as(u8, @intCast(@intFromEnum(self.codec)));
5794
},
5895
}
96+
97+
std.mem.copyForwards(u8, buf[2..], self.hash);
98+
return buf;
5999
}
60100
};
61101

62-
test "CID v0" {
102+
test "CID basic operations" {
103+
const testing = std.testing;
63104
const allocator = testing.allocator;
64105

65-
var hash = [_]u8{ 1, 2, 3, 4, 5 };
66-
var cid = try Cid.init(allocator, .V0, .DagPb, &hash);
67-
defer cid.deinit();
106+
// Test CIDv0
107+
{
108+
var hash = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
109+
var cid = try Cid.init(allocator, .V0, .DagPb, hash[2..]);
110+
defer cid.deinit();
111+
112+
try testing.expect(cid.version == .V0);
113+
try testing.expect(cid.codec == .DagPb);
114+
try testing.expectEqualSlices(u8, hash[2..], cid.hash);
115+
116+
// Test toBytes
117+
const bytes = try cid.toBytes();
118+
defer allocator.free(bytes);
119+
try testing.expectEqualSlices(u8, &hash, bytes);
120+
}
121+
122+
// Test CIDv1
123+
{
124+
var hash = [_]u8{1} ** 32;
125+
var cid = try Cid.init(allocator, .V1, .DagCbor, &hash);
126+
defer cid.deinit();
68127

69-
try testing.expect(cid.version == .V0);
70-
try testing.expect(cid.codec == .DagPb);
71-
try testing.expectEqualSlices(u8, &hash, cid.hash);
128+
try testing.expect(cid.version == .V1);
129+
try testing.expect(cid.codec == .DagCbor);
130+
try testing.expectEqualSlices(u8, &hash, cid.hash);
131+
}
72132
}
73133

74-
test "CID v1" {
134+
test "CID fromBytes" {
135+
const testing = std.testing;
75136
const allocator = testing.allocator;
76137

77-
var hash = [_]u8{ 1, 2, 3, 4, 5 };
78-
var cid = try Cid.init(allocator, .V1, .DagCbor, &hash);
79-
defer cid.deinit();
138+
// Test CIDv0 parsing
139+
{
140+
var input = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
141+
var cid = try Cid.fromBytes(allocator, &input);
142+
defer cid.deinit();
143+
144+
try testing.expect(cid.version == .V0);
145+
try testing.expect(cid.codec == .DagPb);
146+
try testing.expectEqualSlices(u8, input[2..], cid.hash);
147+
}
148+
149+
// Test CIDv1 parsing
150+
{
151+
var input = [_]u8{ 1, @intFromEnum(Codec.DagCbor) } ++ [_]u8{1} ** 32;
152+
var cid = try Cid.fromBytes(allocator, &input);
153+
defer cid.deinit();
154+
155+
try testing.expect(cid.version == .V1);
156+
try testing.expect(cid.codec == .DagCbor);
157+
try testing.expectEqualSlices(u8, input[2..], cid.hash);
158+
}
159+
}
160+
161+
test "CID error cases" {
162+
const testing = std.testing;
163+
const allocator = testing.allocator;
164+
165+
// Test invalid V0 codec
166+
{
167+
var hash = [_]u8{1} ** 32;
168+
try testing.expectError(Error.InvalidCidV0Codec, Cid.init(allocator, .V0, .DagCbor, &hash));
169+
}
170+
171+
// Test input too short
172+
{
173+
var input = [_]u8{1};
174+
try testing.expectError(Error.InputTooShort, Cid.fromBytes(allocator, &input));
175+
}
176+
177+
// Test unknown codec
178+
{
179+
var input = [_]u8{ 1, 0xFF } ++ [_]u8{1} ** 32;
180+
try testing.expectError(Error.UnknownCodec, Cid.fromBytes(allocator, &input));
181+
}
182+
}
183+
184+
test "CID version checks" {
185+
const testing = std.testing;
80186

81-
try testing.expect(cid.version == .V1);
82-
try testing.expect(cid.codec == .DagCbor);
83-
try testing.expectEqualSlices(u8, &hash, cid.hash);
187+
// Test V0 string detection
188+
{
189+
const v0_str = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n";
190+
try testing.expect(CidVersion.isV0Str(v0_str));
191+
192+
const invalid_str = "invalid";
193+
try testing.expect(!CidVersion.isV0Str(invalid_str));
194+
}
195+
196+
// Test V0 binary detection
197+
{
198+
var valid_bytes = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
199+
try testing.expect(CidVersion.isV0Binary(&valid_bytes));
200+
201+
var invalid_bytes = [_]u8{ 0x00, 0x00 } ++ [_]u8{1} ** 32;
202+
try testing.expect(!CidVersion.isV0Binary(&invalid_bytes));
203+
}
84204
}

src/spec/rust-cid

Submodule rust-cid added at eb03f56

0 commit comments

Comments
 (0)