SHA1 digest (Node) - 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 create the basic skeleton:
mkdir sha1
cd sha1
npm init -y
npm install node-zigar
mkdir src 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);
We make the same changes to package.json as we had in
the previous example:
"type": "module",
"scripts": {
"start": "node --loader=node-zigar --no-warnings src/index.js"
},
Then we run it:
npm run start
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 the hello world example. First we change the import statement:
import { sha1 } from '../lib/sha1.zigar';
console.log(sha1('hello world'));
Then we create node-zigar.config.json:
{
"optimize": "ReleaseSmall",
"modules": {
"lib/sha1.zigar": {
"source": "zig/sha1.zig"
}
},
"targets": [
{ "platform": "linux", "arch": "x64" },
{ "platform": "linux", "arch": "arm64" },
{ "platform": "linux-musl", "arch": "x64" },
{ "platform": "linux-musl", "arch": "arm64" }
]
}
Finally we build the necessary libraries for platforms we intend to support:
npx node-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.