diff --git a/build.zig.zon b/build.zig.zon index a7e00b27f9..412c9c099b 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -12,5 +12,5 @@ }, }, .paths = .{""}, - .nonce = 0xc1ce10810000f013, + .nonce = 0xc1ce108124179e16, } diff --git a/doc/build.zig.zon.md b/doc/build.zig.zon.md index a48094b6b8..541408d30e 100644 --- a/doc/build.zig.zon.md +++ b/doc/build.zig.zon.md @@ -22,9 +22,11 @@ Zig package namespace. Must be a valid bare Zig identifier (don't `@` me), limited to 32 bytes. +Together with `nonce`, this represents a globally unique package identifier. + ### `nonce` -Together with name, this represents a globally unique package identifier. This +Together with `name`, this represents a globally unique package identifier. This field is auto-initialized by the toolchain when the package is first created, and then *never changes*. This allows Zig to unambiguously detect when one package is an updated version of another. @@ -34,14 +36,14 @@ project is still maintained. Otherwise, the fork is *hostile*, attempting to take control over the original project's identity. The nonce can be regenerated by deleting the field and running `zig build`. -This 64-bit integer is the combination of a 16-bit id component, a 32-bit -checksum, and 16 bits of reserved zeroes. +This 64-bit integer is the combination of a 32-bit id component and a 32-bit +checksum. The id component within the nonce has these restrictions: -`0x0000` is reserved for legacy packages. +`0x00000000` is reserved for legacy packages. -`0xffff` is reserved to represent "naked" packages. +`0xffffffff` is reserved to represent "naked" packages. The checksum is computed from `name` and serves to protect Zig users from accidental id collisions. diff --git a/src/Package.zig b/src/Package.zig index e5513bd349..145678291a 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -11,20 +11,19 @@ pub const multihash_hex_digest_len = 2 * multihash_len; pub const MultiHashHexDigest = [multihash_hex_digest_len]u8; pub const Nonce = packed struct(u64) { - id: u16, - reserved: u16 = 0, + id: u32, checksum: u32, pub fn generate(name: []const u8) Nonce { return .{ - .id = std.crypto.random.intRangeLessThan(u16, 0x0001, 0xffff), + .id = std.crypto.random.intRangeLessThan(u32, 1, 0xffffffff), .checksum = std.hash.Crc32.hash(name), }; } pub fn validate(n: Nonce, name: []const u8) bool { switch (n.id) { - 0x0000, 0xffff => return false, + 0x00000000, 0xffffffff => return false, else => return std.hash.Crc32.hash(name) == n.checksum, } } @@ -54,8 +53,8 @@ pub const Hash = struct { pub const Algo = std.crypto.hash.sha2.Sha256; pub const Digest = [Algo.digest_length]u8; - /// Example: "nnnn-vvvv-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" - pub const max_len = 32 + 1 + 32 + 1 + (16 + 32 + 192) / 6; + /// Example: "nnnn-vvvv-hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh" + pub const max_len = 32 + 1 + 32 + 1 + (32 + 32 + 200) / 6; pub fn fromSlice(s: []const u8) Hash { assert(s.len <= max_len); @@ -96,14 +95,12 @@ pub const Hash = struct { /// bytes and assumed be a valid zig identifier /// * semver is the version field from build.zig.zon, asserted to be at /// most 32 bytes - /// * hashplus is the following 39-byte array, base64 encoded using -_ to make + /// * hashplus is the following 33-byte array, base64 encoded using -_ to make /// it filesystem safe: - /// - (2 bytes) LE u16 Package ID + /// - (4 bytes) LE u32 Package ID /// - (4 bytes) LE u32 total decompressed size in bytes, overflow saturated - /// - (24 bytes) truncated SHA-256 digest of hashed files of the package - /// - /// example: "nasm-2.16.1-3-AAD_ZlwACpGU-c3QXp_yNyn07Q5U9Rq-Cb1ur2G1" - pub fn init(digest: Digest, name: []const u8, ver: []const u8, id: u16, size: u32) Hash { + /// - (25 bytes) truncated SHA-256 digest of hashed files of the package + pub fn init(digest: Digest, name: []const u8, ver: []const u8, id: u32, size: u32) Hash { assert(name.len <= 32); assert(ver.len <= 32); var result: Hash = undefined; @@ -112,11 +109,11 @@ pub const Hash = struct { buf.appendAssumeCapacity('-'); buf.appendSliceAssumeCapacity(ver); buf.appendAssumeCapacity('-'); - var hashplus: [30]u8 = undefined; - std.mem.writeInt(u16, hashplus[0..2], id, .little); - std.mem.writeInt(u32, hashplus[2..6], size, .little); - hashplus[6..].* = digest[0..24].*; - _ = std.base64.url_safe_no_pad.Encoder.encode(buf.addManyAsArrayAssumeCapacity(40), &hashplus); + var hashplus: [33]u8 = undefined; + std.mem.writeInt(u32, hashplus[0..4], id, .little); + std.mem.writeInt(u32, hashplus[4..8], size, .little); + hashplus[8..].* = digest[0..25].*; + _ = std.base64.url_safe_no_pad.Encoder.encode(buf.addManyAsArrayAssumeCapacity(44), &hashplus); @memset(buf.unusedCapacitySlice(), 0); return result; } @@ -194,8 +191,8 @@ test Hash { 0xc7, 0xf5, 0x71, 0xb7, 0xb4, 0xe7, 0x6f, 0x3c, 0xdb, 0x87, 0x7a, 0x7f, 0xdd, 0xf9, 0x77, 0x87, 0x9d, 0xd3, 0x86, 0xfa, 0x73, 0x57, 0x9a, 0xf7, 0x9d, 0x1e, 0xdb, 0x8f, 0x3a, 0xd9, 0xbd, 0x9f, }; - const result: Hash = .init(example_digest, "nasm", "2.16.1-2", 0xcafe, 10 * 1024 * 1024); - try std.testing.expectEqualStrings("nasm-2.16.1-2-_soAAKAAx_Vxt7Tnbzzbh3p_3fl3h53ThvpzV5r3", result.toSlice()); + const result: Hash = .init(example_digest, "nasm", "2.16.1-3", 0xcafebabe, 10 * 1024 * 1024); + try std.testing.expectEqualStrings("nasm-2.16.1-3-vrr-ygAAoADH9XG3tOdvPNuHen_d-XeHndOG-nNXmved", result.toSlice()); } test { diff --git a/src/Package/Manifest.zig b/src/Package/Manifest.zig index fb8f0cff2b..7e15dde225 100644 --- a/src/Package/Manifest.zig +++ b/src/Package/Manifest.zig @@ -36,7 +36,7 @@ pub const ErrorMessage = struct { }; name: []const u8, -id: u16, +id: u32, version: std.SemanticVersion, version_node: Ast.Node.Index, dependencies: std.StringArrayHashMapUnmanaged(Dependency), @@ -149,7 +149,7 @@ const Parse = struct { errors: std.ArrayListUnmanaged(ErrorMessage), name: []const u8, - id: u16, + id: u32, version: std.SemanticVersion, version_node: Ast.Node.Index, dependencies: std.StringArrayHashMapUnmanaged(Dependency),