|
1 | 1 | const std = @import("std"); |
2 | 2 | const Allocator = std.mem.Allocator; |
3 | 3 | const Multicodec = @import("multicodec.zig").Multicodec; |
4 | | -const Multihash = @import("multihash.zig").Multihash; |
| 4 | +const multihash = @import("multihash.zig"); |
| 5 | +const Multihash = multihash.Multihash; |
5 | 6 | const varint = @import("unsigned_varint.zig"); |
6 | | -const MultiBaseCodec = @import("multibase.zig").MultiBaseCodec; |
| 7 | +const multibase = @import("multibase.zig"); |
| 8 | +const MultiBaseCodec = multibase.MultiBaseCodec; |
7 | 9 |
|
8 | 10 | pub const Error = error{ |
9 | 11 | UnknownCodec, |
@@ -183,18 +185,69 @@ pub fn Cid(comptime S: usize) type { |
183 | 185 | } |
184 | 186 |
|
185 | 187 | 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); |
| 188 | + const bytes = try self.toBytes(); |
| 189 | + defer self.allocator.free(bytes); |
| 190 | + |
| 191 | + const needed_size = MultiBaseCodec.Base58Btc.calcSize(bytes) - 1; // -1 for remove the multibase prefix 'z' |
| 192 | + const dest = try self.allocator.alloc(u8, needed_size); |
| 193 | + const encoded = MultiBaseCodec.base58.encodeBtc(dest, bytes); |
| 194 | + |
| 195 | + if (encoded.len < dest.len) { |
| 196 | + // Shrink allocation to exact size if needed |
| 197 | + return self.allocator.realloc(dest, encoded.len); |
| 198 | + } |
| 199 | + return dest; |
190 | 200 | } |
191 | 201 |
|
192 | | - fn to_string_v1(self: *const Self) ![]u8 { |
193 | | - const bytes = try self.toBytes(self.allocator); |
| 202 | + fn toStringV1(self: *const Self) ![]const u8 { |
| 203 | + const bytes = try self.toBytes(); |
194 | 204 | defer self.allocator.free(bytes); |
195 | 205 |
|
196 | | - const dest = std.ArrayList(u8).init(self.allocator); |
197 | | - return MultiBaseCodec.Base32Lower.encode(dest.items, bytes); |
| 206 | + const needed_size = MultiBaseCodec.Base32Lower.calcSize(bytes); |
| 207 | + const dest = try self.allocator.alloc(u8, needed_size); |
| 208 | + const encoded = MultiBaseCodec.Base32Lower.encode(dest, bytes); |
| 209 | + if (encoded.len < dest.len) { |
| 210 | + // Shrink allocation to exact size if needed |
| 211 | + return self.allocator.realloc(dest, encoded.len); |
| 212 | + } |
| 213 | + return dest; |
| 214 | + } |
| 215 | + |
| 216 | + pub fn toString(self: Self) ![]const u8 { |
| 217 | + switch (self.version) { |
| 218 | + .V0 => { |
| 219 | + // For V0, always use Base58BTC |
| 220 | + return try self.toStringV0(); |
| 221 | + }, |
| 222 | + .V1 => { |
| 223 | + // For V1, use Base32Lower |
| 224 | + return try self.toStringV1(); |
| 225 | + }, |
| 226 | + } |
| 227 | + } |
| 228 | + |
| 229 | + pub fn toStringOfBase(self: *const Self, base: MultiBaseCodec) ![]const u8 { |
| 230 | + return switch (self.version) { |
| 231 | + .V0 => { |
| 232 | + if (base != .Base58Btc) { |
| 233 | + return Error.InvalidCidV0Base; |
| 234 | + } |
| 235 | + return self.toStringV0(); |
| 236 | + }, |
| 237 | + .V1 => { |
| 238 | + const bytes = try self.toBytes(); |
| 239 | + defer self.allocator.free(bytes); |
| 240 | + |
| 241 | + const needed_size = base.calcSize(bytes); |
| 242 | + const dest = try self.allocator.alloc(u8, needed_size); |
| 243 | + const encoded=base.encode(dest, bytes); |
| 244 | + if (encoded.len < dest.len) { |
| 245 | + // Shrink allocation to exact size if needed |
| 246 | + return self.allocator.realloc(dest, encoded.len); |
| 247 | + } |
| 248 | + return dest; |
| 249 | + }, |
| 250 | + }; |
198 | 251 | } |
199 | 252 | }; |
200 | 253 | } |
@@ -259,3 +312,72 @@ test "Cid conversion and comparison" { |
259 | 312 | try testing.expectEqual(cid.encodedLen(), bytes.len); |
260 | 313 | } |
261 | 314 | } |
| 315 | + |
| 316 | +test "to_string_of_base32" { |
| 317 | + const testing = std.testing; |
| 318 | + const allocator = testing.allocator; |
| 319 | + |
| 320 | + const expected_cid = "bafkreibme22gw2h7y2h7tg2fhqotaqjucnbc24deqo72b6mkl2egezxhvy"; |
| 321 | + const hash = try multihash.MultihashCodecs.SHA2_256.digest("foo"); |
| 322 | + const cid = try Cid(32).newV1(allocator, Multicodec.RAW.getCode(), hash); |
| 323 | + |
| 324 | + const result = try cid.toStringOfBase(.Base32Lower); |
| 325 | + defer allocator.free(result); |
| 326 | + |
| 327 | + try testing.expectEqualStrings(expected_cid, result); |
| 328 | +} |
| 329 | + |
| 330 | +test "Cid string representations" { |
| 331 | + const testing = std.testing; |
| 332 | + const allocator = testing.allocator; |
| 333 | + |
| 334 | + // Test V0 string representation with Base58BTC |
| 335 | + { |
| 336 | + const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{1} ** 32); |
| 337 | + const cid = try Cid(32).newV0(allocator, hash); |
| 338 | + const str = try cid.toString(); |
| 339 | + defer allocator.free(str); |
| 340 | + std.debug.print("V0 string: {s}\n", .{str}); |
| 341 | + try testing.expect(CidVersion.isV0Str(str)); |
| 342 | + } |
| 343 | + |
| 344 | + // Test V1 string representation with different bases |
| 345 | + { |
| 346 | + const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{1} ** 32); |
| 347 | + const cid = try Cid(32).newV1(allocator, Multicodec.RAW.getCode(), hash); |
| 348 | + |
| 349 | + const str_default = try cid.toString(); |
| 350 | + defer allocator.free(str_default); |
| 351 | + |
| 352 | + const str_base58 = try cid.toStringOfBase(.Base58Btc); |
| 353 | + defer allocator.free(str_base58); |
| 354 | + |
| 355 | + try testing.expect(!std.mem.eql(u8, str_default, str_base58)); |
| 356 | + } |
| 357 | +} |
| 358 | + |
| 359 | +test "Cid error cases" { |
| 360 | + const testing = std.testing; |
| 361 | + const allocator = testing.allocator; |
| 362 | + |
| 363 | + { |
| 364 | + const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32); |
| 365 | + try testing.expectError(Error.InvalidCidV0Codec, Cid(32).init(allocator, .V0, Multicodec.RAW.getCode(), hash)); |
| 366 | + } |
| 367 | + |
| 368 | + { |
| 369 | + const hash = try Multihash(32).wrap(Multicodec.SHA2_512, &[_]u8{0} ** 32); |
| 370 | + try testing.expectError(Error.InvalidCidV0Multihash, Cid(32).newV0(allocator, hash)); |
| 371 | + } |
| 372 | + |
| 373 | + { |
| 374 | + const hash = try Multihash(32).wrap(Multicodec.SHA2_256, &[_]u8{0} ** 32); |
| 375 | + var cid = try Cid(32).newV0(allocator, hash); |
| 376 | + defer { |
| 377 | + if (cid.toStringOfBase(.Base32Lower)) |str| { |
| 378 | + allocator.free(str); |
| 379 | + } else |_| {} |
| 380 | + } |
| 381 | + try testing.expectError(Error.InvalidCidV0Base, cid.toStringOfBase(.Base32Lower)); |
| 382 | + } |
| 383 | +} |
0 commit comments