Skip to content

Commit bf968f4

Browse files
committed
feat:add more function in cid
Signed-off-by: Chen Kai <281165273grape@gmail.com>
1 parent a83eaf4 commit bf968f4

File tree

2 files changed

+161
-123
lines changed

2 files changed

+161
-123
lines changed

src/cid.zig

Lines changed: 154 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
33
const Multicodec = @import("multicodec.zig").Multicodec;
44
const Multihash = @import("multihash.zig").Multihash;
55
const varint = @import("unsigned_varint.zig");
6+
const MultiBaseCodec = @import("multibase.zig").MultiBaseCodec;
67

78
pub const Error = error{
89
UnknownCodec,
@@ -48,29 +49,31 @@ pub fn Cid(comptime S: usize) type {
4849
hash: Multihash(S),
4950
allocator: Allocator,
5051

51-
pub fn newV0(allocator: Allocator, hash: Multihash(32)) !Cid {
52+
const Self = @This();
53+
54+
pub fn newV0(allocator: Allocator, hash: Multihash(32)) !Self {
5255
if (hash.getCode() != Multicodec.SHA2_256 or hash.getSize() != 32) {
5356
return Error.InvalidCidV0Multihash;
5457
}
5558

56-
return Cid{
59+
return Cid(32){
5760
.version = .V0,
5861
.codec = Multicodec.DAG_PB.getCode(),
5962
.hash = hash,
6063
.allocator = allocator,
6164
};
6265
}
6366

64-
pub fn newV1(allocator: Allocator, codec: u64, hash: Multihash(S)) !Cid {
65-
return Cid{
67+
pub fn newV1(allocator: Allocator, codec: u64, hash: Multihash(S)) !Self {
68+
return Cid(S){
6669
.version = .V1,
6770
.codec = codec,
6871
.hash = hash,
6972
.allocator = allocator,
7073
};
7174
}
7275

73-
pub fn init(allocator: Allocator, version: CidVersion, codec: u64, hash: Multihash(S)) !@This() {
76+
pub fn init(allocator: Allocator, version: CidVersion, codec: u64, hash: Multihash(S)) !Self {
7477
switch (version) {
7578
.V0 => {
7679
if (codec != Multicodec.DAG_PB.getCode()) {
@@ -85,15 +88,43 @@ pub fn Cid(comptime S: usize) type {
8588
}
8689
}
8790

88-
pub fn readBytes(allocator: Allocator, reader: anytype) !Cid {
91+
/// Checks if two CIDs are equal by comparing version, codec and hash
92+
pub fn isEqual(self: *const Self, other: *const Self) bool {
93+
return self.version == other.version and
94+
self.codec == other.codec and
95+
std.mem.eql(u8, self.hash.getDigest(), other.hash.getDigest());
96+
}
97+
98+
pub fn writeBytesV1(self: *const Self, writer: anytype) !usize {
99+
const version_written = try varint.encode_stream(writer, u64, self.version.toInt());
100+
const codec_written = try varint.encode_stream(writer, u64, self.codec);
101+
102+
var written: usize = version_written + codec_written;
103+
written += try self.hash.write(writer);
104+
return written;
105+
}
106+
107+
pub fn intoV1(self: Self) !Self {
108+
return switch (self.version) {
109+
.V0 => {
110+
if (self.codec != @intFromEnum(Multicodec.DAG_PB)) {
111+
return Error.InvalidCidV0Codec;
112+
}
113+
return newV1(self.allocator, self.codec, self.hash);
114+
},
115+
.V1 => self,
116+
};
117+
}
118+
119+
pub fn readBytes(allocator: Allocator, reader: anytype) !Self {
89120
const version = try varint.decode_stream(reader, u64);
90121
const codec = try varint.decode_stream(reader, u64);
91122

92-
// CIDv0 has the fixed `0x12 0x20` prefix
93123
if (version == 0x12 and codec == 0x20) {
94124
var digest: [32]u8 = undefined;
95125
try reader.readNoEof(&digest);
96-
const mh = try Multihash(32).wrap(version, &digest);
126+
const version_codec = try Multicodec.fromCode(version);
127+
const mh = try Multihash(32).wrap(version_codec, &digest);
97128
return newV0(allocator, mh);
98129
}
99130

@@ -102,123 +133,129 @@ pub fn Cid(comptime S: usize) type {
102133
.V0 => return Error.InvalidExplicitCidV0,
103134
.V1 => {
104135
const mh = try Multihash(32).read(reader);
105-
return Cid.init(allocator, ver, codec, mh.getDigest());
136+
return Self.init(allocator, ver, codec, mh);
106137
},
107138
}
108139
}
109140

110-
fn writeBytesV1(self: *const Cid, writer: anytype) !usize {
111-
const version_written=try varint.encode_stream(writer,u64,self.version.toInt());
112-
const codec_written=try varint.encode_stream(writer,u64,self.codec);
141+
pub fn writeBytes(self: *const Self, writer: anytype) !usize {
142+
return switch (self.version) {
143+
.V0 => try self.hash.write(writer),
144+
.V1 => try self.writeBytesV1(writer),
145+
};
146+
}
113147

114-
const written: usize = version_written + codec_written;
148+
pub fn encodedLen(self: Self) usize {
149+
return switch (self.version) {
150+
.V0 => self.hash.encodedLen(),
151+
.V1 => {
152+
var version_buf: [varint.bufferSize(u64)]u8 = undefined;
153+
const version = varint.encode(u64, self.version.toInt(), &version_buf);
115154

116-
return written;
155+
var codec_buf: [varint.bufferSize(u64)]u8 = undefined;
156+
const codec = varint.encode(u64, self.codec, &codec_buf);
157+
158+
return version.len + codec.len + self.hash.encodedLen();
159+
},
160+
};
161+
}
162+
163+
pub fn toBytes(self: *const Self) ![]u8 {
164+
var bytes = std.ArrayList(u8).init(self.allocator);
165+
errdefer bytes.deinit();
166+
167+
const written = try self.writeBytes(bytes.writer());
168+
std.debug.assert(written == bytes.items.len);
169+
170+
return bytes.toOwnedSlice();
171+
}
172+
173+
pub fn getHash(self: *const Self) []const u8 {
174+
return self.hash.getDigest();
117175
}
118176

177+
pub fn getCodec(self: *const Self) u64 {
178+
return self.codec;
179+
}
180+
181+
pub fn getVersion(self: *const Self) CidVersion {
182+
return self.version;
183+
}
184+
185+
fn toStringV0(self: *const Self) ![]const u8 {
186+
const hash_bytes = try self.hash.toBytes();
187+
var bytes = std.ArrayList(u8).init(self.allocator);
188+
errdefer bytes.deinit();
189+
return MultiBaseCodec.Base58Btc.encode(bytes.items, hash_bytes);
190+
}
191+
192+
fn to_string_v1(self: *const Self) ![]u8 {
193+
const bytes = try self.toBytes(self.allocator);
194+
defer self.allocator.free(bytes);
195+
196+
const dest = std.ArrayList(u8).init(self.allocator);
197+
return MultiBaseCodec.Base32Lower.encode(dest.items, bytes);
198+
}
119199
};
120200
}
121201

122-
// test "CID basic operations" {
123-
// const testing = std.testing;
124-
// const allocator = testing.allocator;
125-
//
126-
// // Test CIDv0
127-
// {
128-
// var hash = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
129-
// var cid = try Cid.init(allocator, .V0, .DagPb, hash[2..]);
130-
// defer cid.deinit();
131-
//
132-
// try testing.expect(cid.version == .V0);
133-
// try testing.expect(cid.codec == .DagPb);
134-
// try testing.expectEqualSlices(u8, hash[2..], cid.hash);
135-
//
136-
// // Test toBytes
137-
// const bytes = try cid.toBytes();
138-
// defer allocator.free(bytes);
139-
// try testing.expectEqualSlices(u8, &hash, bytes);
140-
// }
141-
//
142-
// // Test CIDv1
143-
// {
144-
// var hash = [_]u8{1} ** 32;
145-
// var cid = try Cid.init(allocator, .V1, .DagCbor, &hash);
146-
// defer cid.deinit();
147-
//
148-
// try testing.expect(cid.version == .V1);
149-
// try testing.expect(cid.codec == .DagCbor);
150-
// try testing.expectEqualSlices(u8, &hash, cid.hash);
151-
// }
152-
// }
153-
//
154-
// test "CID fromBytes" {
155-
// const testing = std.testing;
156-
// const allocator = testing.allocator;
157-
//
158-
// // Test CIDv0 parsing
159-
// {
160-
// var input = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
161-
// var cid = try Cid.fromBytes(allocator, &input);
162-
// defer cid.deinit();
163-
//
164-
// try testing.expect(cid.version == .V0);
165-
// try testing.expect(cid.codec == .DagPb);
166-
// try testing.expectEqualSlices(u8, input[2..], cid.hash);
167-
// }
168-
//
169-
// // Test CIDv1 parsing
170-
// {
171-
// var input = [_]u8{ 1, @intFromEnum(Codec.DagCbor) } ++ [_]u8{1} ** 32;
172-
// var cid = try Cid.fromBytes(allocator, &input);
173-
// defer cid.deinit();
174-
//
175-
// try testing.expect(cid.version == .V1);
176-
// try testing.expect(cid.codec == .DagCbor);
177-
// try testing.expectEqualSlices(u8, input[2..], cid.hash);
178-
// }
179-
// }
180-
//
181-
// test "CID error cases" {
182-
// const testing = std.testing;
183-
// const allocator = testing.allocator;
184-
//
185-
// // Test invalid V0 codec
186-
// {
187-
// var hash = [_]u8{1} ** 32;
188-
// try testing.expectError(Error.InvalidCidV0Codec, Cid.init(allocator, .V0, .DagCbor, &hash));
189-
// }
190-
//
191-
// // Test input too short
192-
// {
193-
// var input = [_]u8{1};
194-
// try testing.expectError(Error.InputTooShort, Cid.fromBytes(allocator, &input));
195-
// }
196-
//
197-
// // Test unknown codec
198-
// {
199-
// var input = [_]u8{ 1, 0xFF } ++ [_]u8{1} ** 32;
200-
// try testing.expectError(Error.UnknownCodec, Cid.fromBytes(allocator, &input));
201-
// }
202-
// }
203-
//
204-
// test "CID version checks" {
205-
// const testing = std.testing;
206-
//
207-
// // Test V0 string detection
208-
// {
209-
// const v0_str = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n";
210-
// try testing.expect(CidVersion.isV0Str(v0_str));
211-
//
212-
// const invalid_str = "invalid";
213-
// try testing.expect(!CidVersion.isV0Str(invalid_str));
214-
// }
215-
//
216-
// // Test V0 binary detection
217-
// {
218-
// var valid_bytes = [_]u8{ 0x12, 0x20 } ++ [_]u8{1} ** 32;
219-
// try testing.expect(CidVersion.isV0Binary(&valid_bytes));
220-
//
221-
// var invalid_bytes = [_]u8{ 0x00, 0x00 } ++ [_]u8{1} ** 32;
222-
// try testing.expect(!CidVersion.isV0Binary(&invalid_bytes));
223-
// }
224-
// }
202+
test "Cid" {
203+
const testing = std.testing;
204+
const allocator = testing.allocator;
205+
206+
// Test CIDv0
207+
{
208+
const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32);
209+
const cid = try Cid(32).newV0(allocator, hash);
210+
try testing.expectEqual(cid.version, .V0);
211+
try testing.expectEqual(cid.codec, Multicodec.DAG_PB.getCode());
212+
}
213+
214+
// Test CIDv1
215+
{
216+
const hash = try Multihash(64).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32);
217+
const cid = try Cid(64).newV1(allocator, Multicodec.RAW.getCode(), hash);
218+
try testing.expectEqual(cid.version, .V1);
219+
try testing.expectEqual(cid.codec, Multicodec.RAW.getCode());
220+
}
221+
222+
// Test encoding/decoding
223+
{
224+
const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32);
225+
const original = try Cid(32).newV1(allocator, Multicodec.RAW.getCode(), hash);
226+
227+
const bytes = try original.toBytes();
228+
defer allocator.free(bytes);
229+
230+
var fbs = std.io.fixedBufferStream(bytes);
231+
const decoded = try Cid(32).readBytes(allocator, fbs.reader());
232+
233+
try testing.expect(original.isEqual(&decoded));
234+
}
235+
}
236+
237+
test "Cid conversion and comparison" {
238+
const testing = std.testing;
239+
const allocator = testing.allocator;
240+
241+
// Test V0 to V1 conversion
242+
{
243+
const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32);
244+
const v0 = try Cid(32).newV0(allocator, hash);
245+
const v1 = try v0.intoV1();
246+
247+
try testing.expectEqual(v1.version, .V1);
248+
try testing.expectEqual(v1.codec, v0.codec);
249+
try testing.expect(std.mem.eql(u8, v1.getHash(), v0.getHash()));
250+
}
251+
252+
// Test encoded length
253+
{
254+
const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32);
255+
const cid = try Cid(32).newV1(allocator, Multicodec.RAW.getCode(), hash);
256+
const bytes = try cid.toBytes();
257+
defer allocator.free(bytes);
258+
259+
try testing.expectEqual(cid.encodedLen(), bytes.len);
260+
}
261+
}

src/multihash.zig

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,21 @@ pub fn Multihash(comptime S: usize) type {
6363
}
6464

6565
pub fn encodedLen(self: Self) usize {
66-
var code_buf: [10]u8 = undefined;
66+
var code_buf: [varint.bufferSize(u64)]u8 = undefined;
6767
const code_encoded = varint.encode(u64, self.code.getCode(), &code_buf);
6868

69-
var size_buf: [1]u8 = undefined;
69+
var size_buf: [varint.bufferSize(u8)]u8 = undefined;
7070
const size_encoded = varint.encode(u8, self.size, &size_buf);
7171

7272
return code_encoded.len + size_encoded.len + self.size;
7373
}
7474

7575
pub fn write(self: Self, writer: anytype) !usize {
76-
var code_buf: [10]u8 = undefined;
76+
var code_buf: [varint.bufferSize(u64)]u8 = undefined;
7777
const code_encoded = varint.encode(u64, self.code.getCode(), &code_buf);
7878
try writer.writeAll(code_encoded);
7979

80-
var size_buf: [1]u8 = undefined;
80+
var size_buf: [varint.bufferSize(u8)]u8 = undefined;
8181
const size_encoded = varint.encode(u8, self.size, &size_buf);
8282
try writer.writeAll(size_encoded);
8383

@@ -105,9 +105,10 @@ pub fn Multihash(comptime S: usize) type {
105105
}
106106

107107
pub fn toBytes(self: Self, allocator: std.mem.Allocator) ![]u8 {
108-
const bytes = try allocator.alloc(u8, self.encodedLen());
108+
const bytes = try allocator.alloc(u8, self.size);
109109
var stream = std.io.fixedBufferStream(bytes);
110-
_ = try self.write(stream.writer());
110+
const written = try self.write(stream.writer());
111+
std.debug.assert(written == bytes.len);
111112
return bytes;
112113
}
113114
};

0 commit comments

Comments
 (0)