SHA1 digest (Bun) - chung-leong/zigar GitHub Wiki

In this example we're going to create a server-side app that calculates SHA-1 digests, using a built-in function of Zig's standard library.

Creating the app

First, we'll initialize the project:

mkdir sha1
cd sha1
bun init
bun init helps you get started with a minimal project and tries to guess sensible defaults. Press ^C anytime to quit

package name (sha1):
entry point (index.ts): src/index.js

Next, we install bun-zigar and add a sub-directory for zig files:

bun install bun-zigar
bunx bun-zigar preload
mkdir zig

Then we add sha1.zig:

const std = @import("std");

pub fn sha1(bytes: []const u8) [std.crypto.hash.Sha1.digest_length * 2]u8 {
    var digest: [std.crypto.hash.Sha1.digest_length]u8 = undefined;
    std.crypto.hash.Sha1.hash(bytes, &digest, .{});
    return std.fmt.bytesToHex(digest, .lower);
}

Followed by index.js:

import { sha1 } from '../zig/sha1.zig';

console.log(sha1('hello world').string);

Then we run it:

bun src/index.js

After a while, we get the result:

2aae6c35c94fcfb415dbe95f408b9ce91ee846ed

We would get the same digest if we calculate it using sha1sum:

echo -n "hello world" | sha1sum
2aae6c35c94fcfb415dbe95f408b9ce91ee846ed  -

Making return value more JS-friendly

In the example, sha1() returns an array of u8. On the JavaScript side it's represented by a object. To get a string, you need to access its string property.

Zigar lets you to flag certain functions as returning strings. To do so, you declare a struct type with a particular name at the root level:

const module_ns = @This();
pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime name: std.meta.DeclEnum(T)) bool {
        return switch (T) {
            module_ns => switch (name) {
                .sha1 => true,
                else => false,
            },
            else => false,
        };
    }
};

During export, isDeclString() is invoked when a function's return value is something that can be interpreted as a text string (e.g. []const u8). With the above declaration in place, we can simplify our JavaScript:

console.log(sha1('hello world'));

You can use the following to threat all occurences of u8 and u16 as text:

pub const @"meta(zigar)" = struct {
    pub fn isDeclString(comptime T: type, comptime _: std.meta.DeclEnum(T)) bool {
        return true;
    }

    pub fn isFieldString(comptime T: type, comptime _: std.meta.FieldEnum(T)) bool {
        return true;
    }
};

Configuring the app for deployment

We follow the same steps as described in the hello world example. First we change the import statement:

import { sha1 } from '../lib/sha1.zigar';

console.log(sha1('hello world').string);

Then we create bun-zigar.toml:

optimize = "ReleaseSmall"

[modules."lib/sha1.zigar"]
source = "zig/sha1.zig"

[targets](/chung-leong/zigar/wiki/targets)
platform = "linux"
arch = "x64"

[targets](/chung-leong/zigar/wiki/targets)
platform = "linux"
arch = "arm64"

[targets](/chung-leong/zigar/wiki/targets)
platform = "linux-musl"
arch = "x64"

[targets](/chung-leong/zigar/wiki/targets)
platform = "linux-musl"
arch = "arm64"

Finally we build the necessary libraries for platforms we intend to support:

bunx bun-zigar build

Source code

You can find the complete source code for this example here.

Conclusion

Okay, that wasn't much of a server-side app. At least it does something. In the next example we'll build a server-side app for real, one that actually listens for remote requests.


Image filter example