Update for a recent Zig master.

Tested against 0.12.0-dev.3193+4ba4f94c
6 files changed, 99 insertions(+), 77 deletions(-)

M build.zig
M src/crypto_box.zig
M src/crypto_sign.zig
M src/mem.zig
M src/randombytes.zig
M src/secretstream.zig
M build.zig +19 -10
@@ 1,19 1,28 @@ 
-const Builder = @import("std").build.Builder;
+const Build = @import("std").Build;
 const std = @import("std");
 
-pub fn build(b: *Builder) void {
+pub fn build(b: *Build) void {
     const target = b.standardTargetOptions(.{});
-    const mode = b.standardReleaseOptions();
-    const lib = b.addStaticLibrary("sodium", "src/sodium.zig");
-    lib.setBuildMode(mode);
-    lib.install();
+    const mode = b.standardOptimizeOption(.{});
+    const lib = b.addStaticLibrary(.{
+        .name = "sodium",
+        .root_source_file = .{ .path = "src/sodium.zig" },
+        .target = target,
+        .optimize = mode,
+    });
+    b.installArtifact(lib);
 
-    var tests = b.addTest("src/sodium.zig");
-    tests.setBuildMode(mode);
-    tests.setTarget(target);
+    var tests = b.addTest(.{
+        .root_source_file = .{ .path = "src/sodium.zig" },
+        .target = target,
+        .optimize = mode,
+    });
+
     tests.linkSystemLibrary("c");
     tests.linkSystemLibrary("sodium");
 
+    const run_tests = b.addRunArtifact(tests);
+
     const test_step = b.step("test", "Run library tests");
-    test_step.dependOn(&tests.step);
+    test_step.dependOn(&run_tests.step);
 }

          
M src/crypto_box.zig +9 -9
@@ 156,11 156,11 @@ pub fn open_easy(
 pub const Sealer = struct {
     const Self = @This();
     pubkey: [PUBLICKEYBYTES]u8,
-    allocator: *Allocator,
+    allocator: Allocator,
 
     /// Initialize and return an instance. Note that pubkey is not a
     /// pointer.
-    pub fn init(allocator: *Allocator, pubkey: [PUBLICKEYBYTES]u8) Self {
+    pub fn init(allocator: Allocator, pubkey: [PUBLICKEYBYTES]u8) Self {
         return Self{
             .pubkey = pubkey,
             .allocator = allocator,

          
@@ 171,7 171,7 @@ pub const Sealer = struct {
     /// ciphertext is a pointer to memory allocated with the
     /// allocator, and is owned by the caller.
     pub fn encrypt(self: Self, msg: []const u8) ![]u8 {
-        var ctext = try self.allocator.alloc(u8, SEALBYTES + msg.len);
+        const ctext = try self.allocator.alloc(u8, SEALBYTES + msg.len);
         try seal(ctext, msg, &self.pubkey);
         return ctext;
     }

          
@@ 189,11 189,11 @@ pub const Unsealer = struct {
     const Self = @This();
     pubkey: [PUBLICKEYBYTES]u8,
     privkey: [SECRETKEYBYTES]u8,
-    allocator: *Allocator,
+    allocator: Allocator,
 
     /// Initialize and return an instance.
     pub fn init(
-        allocator: *Allocator,
+        allocator: Allocator,
         pubkey: [PUBLICKEYBYTES]u8,
         privkey: [SECRETKEYBYTES]u8,
     ) Self {

          
@@ 208,7 208,7 @@ pub const Unsealer = struct {
     /// caller owns the returned value and is responsible for freeing
     /// it. It was allocated with the Unsealer's allocator.
     pub fn decrypt(self: Self, ciphertext: []const u8) ![]u8 {
-        var msg = try self.allocator.alloc(u8, ciphertext.len - SEALBYTES);
+        const msg = try self.allocator.alloc(u8, ciphertext.len - SEALBYTES);
         try sealOpen(msg, ciphertext, &self.pubkey, &self.privkey);
         return msg;
     }

          
@@ 228,7 228,7 @@ test "seal" {
 
     try sealOpen(clear[0..], ciphertext[0..], &pk, &sk);
 
-    testing.expectEqualSlices(u8, msg, clear[0..]);
+    try testing.expectEqualSlices(u8, msg, clear[0..]);
 }
 
 test "sealer" {

          
@@ 247,7 247,7 @@ test "sealer" {
     const clear = try unsealer.decrypt(cipher);
     defer malloc.free(clear);
 
-    testing.expectEqualSlices(u8, msg, clear);
+    try testing.expectEqualSlices(u8, msg, clear);
 }
 
 test "authenticated encryption" {

          
@@ 272,5 272,5 @@ test "authenticated encryption" {
     // the nonce.
     var rcvd: [encrypted.len - MACBYTES]u8 = undefined;
     try open_easy(rcvd[0..], encrypted[0..], &nonce, &bob_pub, &alice_priv);
-    testing.expectEqualSlices(u8, rcvd[0..], msg);
+    try testing.expectEqualSlices(u8, rcvd[0..], msg);
 }

          
M src/crypto_sign.zig +1 -1
@@ 89,5 89,5 @@ test "signature" {
     try sign(signed[0..], msg, &secret_key);
     var received: [signed.len - BYTES]u8 = undefined;
     try open(received[0..], signed[0..], &pub_key);
-    testing.expectEqualSlices(u8, msg, received[0..]);
+    try testing.expectEqualSlices(u8, msg, received[0..]);
 }

          
M src/mem.zig +44 -35
@@ 34,61 34,70 @@ pub fn zero(arr: []u8) void {
 /// won't free any memory if you try to make a buffer smaller. It
 /// does, however, mlock allocated pages and zero freed memory.
 pub const sodium_allocator: *Allocator = &sodium_allocator_state;
+const vtable = .{
+        .alloc = sodiumAlloc,
+        .resize = sodiumResize,
+    .free = sodiumFree,
+};
+
+var ctxt: void = {};
+
 var sodium_allocator_state = Allocator{
-    .allocFn = sodiumAlloc,
-    .resizeFn = sodiumResize,
+    .vtable = &vtable,
+    .ptr = &ctxt,
 };
 
+fn sodiumFree(ctx: *anyopaque, buf: []u8, buf_align: u8, ret_addr: usize) void {
+    _ = ctx;
+    _ = buf_align;
+    _ = ret_addr;
+    c.sodium_free(buf.ptr);
+}
+
 fn sodiumAlloc(
-    self: *Allocator,
+    ctx: *anyopaque,
     len: usize,
-    ptr_align: u29,
-    len_align: u29,
+    ptr_align: u8,
     ret_addr: usize,
-) Error![]u8 {
+) ?[*]u8 {
+    _ = ctx;
+    _ = ret_addr;
     var bytes = len;
-    const alignment = std.math.max(ptr_align, len_align);
+    const alignment = ptr_align;
     if (alignment != 0) {
-        bytes = std.mem.alignForward(len, alignment);
+        bytes = std.mem.alignForward(usize, len, alignment);
     }
 
     // Zig helps prevent access past the end of the slice, but that's
     // the only mechanism in cases where len_align < ptr_align. Sodium
     // will catch writes past the end of the allocated buffer.
-    const slice_len = if (len_align >= ptr_align)
-        bytes
-    else if (len_align == 0)
-        len
-    else
-        std.mem.alignForward(len, len_align);
-
     const allocated = c.sodium_malloc(bytes);
     if (allocated) |ptr| {
-        return @ptrCast([*]u8, ptr)[0..slice_len];
+        return @as([*]u8, @ptrCast(ptr));
     } else {
-        return Error.OutOfMemory;
+        return null;
     }
 }
 
 fn sodiumResize(
-    self: *Allocator,
+    ctx: *anyopaque,
     buf: []u8,
-    buf_align: u29,
+    buf_align: u8,
     new_len: usize,
-    len_align: u29,
     ret_addr: usize,
-) Error!usize {
+) bool {
+    _ = ret_addr;
+    _ = buf_align;
+    _ = ctx;
     if (new_len > buf.len) {
-        return Error.OutOfMemory;
+        return false;
     }
     if (new_len == 0) {
         c.sodium_free(buf.ptr);
-        return 0;
+        return true;
     }
-    const real_new_len = std.mem.alignAllocLen(buf.len, new_len, len_align);
-
-    zero(buf[real_new_len..]);
-    return real_new_len;
+    zero(buf[new_len..]);
+    return true;
 }
 
 test "mem lock" {

          
@@ 97,7 106,7 @@ test "mem lock" {
     try sodium.init();
     try mlock(data[0..]);
     try munlock(data[0..]);
-    testing.expectEqual(data[0], 0);
+    try testing.expectEqual(data[0], 0);
 }
 
 test "mem zero" {

          
@@ 106,7 115,7 @@ test "mem zero" {
 
     try sodium.init();
     zero(data[0..]);
-    testing.expectEqualSlices(u8, data[0..], zeroes[0..]);
+    try testing.expectEqualSlices(u8, data[0..], zeroes[0..]);
 }
 
 test "sodium allocator" {

          
@@ 117,15 126,15 @@ test "sodium allocator" {
     var other_slice = try sodium_allocator.alloc(u8, buf_size);
     defer sodium_allocator.free(other_slice);
 
-    for (slice) |_, idx| {
-        slice[idx] = @intCast(u8, idx);
-        other_slice[idx] = @intCast(u8, idx) ^ 0xff;
+    for (slice, 0..) |_, idx| {
+        slice[idx] = @as(u8, @intCast(idx));
+        other_slice[idx] = @as(u8, @intCast(idx)) ^ 0xff;
     }
     var sum: usize = 0;
     for (slice) |val| {
         sum += val;
     }
-    testing.expectEqual(sum, 1225);
+    try testing.expectEqual(sum, 1225);
 }
 
 // expectIllegalBehavior Stolen from https://github.com/fengb/zee_alloc

          
@@ 145,7 154,7 @@ fn expectIllegalBehavior(context: anytyp
 
     const child_pid = try std.os.fork();
     if (child_pid == 0) {
-        const null_fd = std.os.openZ("/dev/null", std.os.O_RDWR, 0) catch {
+        const null_fd = std.posix.openZ("/dev/null", .{.ACCMODE=.RDWR}, 0) catch {
             std.debug.print("Cannot open /dev/null\n", .{});
             std.os.exit(0);
         };

          
@@ 166,7 175,7 @@ fn expectIllegalBehavior(context: anytyp
 }
 
 fn accessPastEnd(slice: []u8) void {
-    const ptr = @ptrCast([*]u8, slice.ptr);
+    const ptr = @as([*]u8, @ptrCast(slice.ptr));
     ptr[slice.len] = 1;
 }
 

          
M src/randombytes.zig +3 -3
@@ 37,13 37,13 @@ pub fn bufDeterministic(buffer: []u8, se
 
 /// Deallocate global resources used by the prng. Probably don't call
 /// this.
-pub fn close(void) void {
+pub fn close() void {
     c.randombytes_close();
 }
 
 /// Reseed the prng, if it supports this operation. This should not be
 /// necessary, even after fork().
-pub fn stir(void) void {
+pub fn stir() void {
     c.randombytes_stir();
 }
 

          
@@ 56,5 56,5 @@ test "random numbers" {
     const seed: [SEEDBYTES]u8 = [_]u8{0} ** SEEDBYTES;
     try bufDeterministic(buffer[0..], &seed);
     const nonrandom = [_]u8{ 0xa1, 0x1f, 0x8f, 0x12, 0xd0 };
-    testing.expectEqualSlices(u8, buffer[0..], nonrandom[0..]);
+    try testing.expectEqualSlices(u8, buffer[0..], nonrandom[0..]);
 }

          
M src/secretstream.zig +23 -19
@@ 9,7 9,7 @@ const c = @cImport({
     @cInclude("sodium.h");
 });
 
-const Tag = enum {
+const Tag = enum(u8) {
     MESSAGE = c.crypto_secretstream_xchacha20poly1305_TAG_MESSAGE,
     PUSH = c.crypto_secretstream_xchacha20poly1305_TAG_PUSH,
     REKEY = c.crypto_secretstream_xchacha20poly1305_TAG_REKEY,

          
@@ 65,7 65,7 @@ pub fn push(
         message.len,
         if (additional_data) |ad| ad.ptr else null,
         if (additional_data) |ad| ad.len else 0,
-        @enumToInt(tag),
+        @intFromEnum(tag),
     );
     if (clen > ciphertext.len) {
         // I don't trust guarantees that aren't in the source code.

          
@@ 107,7 107,7 @@ pub fn pull(
         state,
         message.ptr,
         &mlen,
-        if (tag) |t| @ptrCast(*u8, t) else null,
+        if (tag) |t| @as(*u8, @ptrCast(t)) else null,
         ciphertext.ptr,
         ciphertext.len,
         if (additional_data) |ad| ad.ptr else null,

          
@@ 156,7 156,7 @@ pub fn ChunkEncrypter(chunk_size: usize,
             // There's probably a better way to do this than to copy
             // the whole array over to the stack, byte by byte.
             var m: [chunk_size]u8 = [_]u8{0} ** chunk_size;
-            for (msg) |val, idx| {
+            for (msg, 0..) |val, idx| {
                 m[idx] = val;
             }
             var ctxt: [chunk_size + ABYTES]u8 = undefined;

          
@@ 171,7 171,9 @@ pub fn ChunkEncrypter(chunk_size: usize,
         }
 
         /// Free up all held resources.
-        pub fn deinit(self: *Self) void {}
+        pub fn deinit(self: *Self) void {
+            _ = self;
+        }
     };
 }
 

          
@@ 229,7 231,9 @@ pub fn ChunkDecrypter(
         }
 
         /// Free all held resources.
-        pub fn deinit(self: *Self) void {}
+        pub fn deinit(self: *Self) void {
+            _ = self;
+        }
     };
 }
 

          
@@ 252,24 256,23 @@ test "stream" {
     var clear2: [ciphertext2.len - ABYTES]u8 = undefined;
     try initPull(&state, &hdr, &key);
     const len = try pull(&state, clear[0..], null, ciphertext[0..cl], null);
-    testing.expectEqual(msg.len, len);
+    try testing.expectEqual(msg.len, len);
     const len2 = try pull(&state, clear2[0..], null, ciphertext2[0..c2], null);
-    testing.expectEqual(msg2.len, len2);
-    testing.expectEqualSlices(u8, msg[0..], clear[0..]);
-    testing.expectEqualSlices(u8, msg2[0..], clear2[0..]);
+    try testing.expectEqual(msg2.len, len2);
+    try testing.expectEqualSlices(u8, msg[0..], clear[0..]);
+    try testing.expectEqualSlices(u8, msg2[0..], clear2[0..]);
 }
 
 test "chunks" {
     try sodium.init();
     const msg = "This message is longer than my chunk size!!";
-    const malloc = std.heap.c_allocator;
     var key: [KEYBYTES]u8 = undefined;
 
     keygen(&key);
     const chunk_size = 4;
     var buf: [chunk_size * 1024]u8 = undefined;
     var stream = std.io.fixedBufferStream(&buf);
-    const out = stream.outStream();
+    const out = stream.writer();
     const Encrypter = ChunkEncrypter(chunk_size, @TypeOf(out));
     var encrypter = try Encrypter.init(out, key);
     defer encrypter.deinit();

          
@@ 283,27 286,28 @@ test "chunks" {
     }
 
     const cipherlen = try stream.getPos();
-    var cipherstream = std.io.fixedBufferStream(buf[0..cipherlen]).inStream();
+    var cipherstream = std.io.fixedBufferStream(buf[0..cipherlen]);
+    const in = cipherstream.reader();
     var decrypted: [chunk_size * 1024]u8 = undefined;
     var cleartext = std.io.fixedBufferStream(&decrypted);
-    var clearstream = cleartext.outStream();
+    const clearstream = cleartext.writer();
     const Decrypter = ChunkDecrypter(
         chunk_size,
-        @TypeOf(cipherstream),
+        @TypeOf(in),
         @TypeOf(clearstream),
     );
-    var decrypter = try Decrypter.init(cipherstream, clearstream, key);
+    var decrypter = try Decrypter.init(in, clearstream, key);
     defer decrypter.deinit();
 
     while (chunk_count > 0) {
         decrypter.pullChunk() catch |err| {
-            std.debug.warn("Unexpected error: {}\n", .{err});
+            std.log.warn("Unexpected error: {}\n", .{err});
             return err;
         } orelse break;
         chunk_count -= 1;
     }
 
     const len = try cleartext.getPos();
-    testing.expectEqual(len, msg.len);
-    testing.expectEqualSlices(u8, msg[0..], decrypted[0..len]);
+    try testing.expectEqual(len, msg.len);
+    try testing.expectEqualSlices(u8, msg[0..], decrypted[0..len]);
 }