Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
.direnv/
.dfx/
zig-cache/
zig-cache
zig-out/
*.wasm*
*.wat
*.o
*.zon
*.out
124 changes: 24 additions & 100 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,116 +1,40 @@
# A Zig Canister
# Organic Zig

Ben Lynn has a blog post series about "Organic Apps", he creates canisters with
`clang`, and `lld` only.
Attempts and contempt at re-creating iconic c canister in zig

[Quint](https://github.com/q-uint) and [Enzo](https://github.com/EnzoPlayer0ne), over beers, thought it would be fun to create a canister in zig, so
we looked at
Ben's [Hello, Internet Computer](https://fxa77-fiaaa-aaaae-aaana-cai.raw.ic0.app/organic/hello.html)
blog post and decided to port it to Zig the following day.
## Organic IC
- [X] hello-ic
- [ ] qr-code

Anyways, here is the C code from Ben:
```c
#define IMPORT(m,n) __attribute__((import_module(m))) __attribute__((import_name(n)));
#define EXPORT(n) asm(n) __attribute__((visibility("default")))
## Zig

void reply_append(void*, unsigned) IMPORT("ic0", "msg_reply_data_append");
void reply (void) IMPORT("ic0", "msg_reply");

void go() EXPORT("canister_query hi");

void go() {
char msg[] = "Hello, World!\n";
reply_append(msg, sizeof(msg) - 1);
reply();
}

```

And here is our zig code:
### Snippets
```zig
extern "ic0" fn msg_reply_data_append(ptr: [*]const u8, len: usize) void;
extern "ic0" fn msg_reply() void;

export fn @"canister_query hi"() callconv(.C) void {
const msg = "Hello, World!\n";
msg_reply_data_append(msg, msg.len);
msg_reply();
}
```

Let's compile the C code:
```sh
# we compile the object file
clang --target=wasm32 -c -O3 src/main.c -o main_c.o
# we link the object file
wasm-ld --no-entry --export-dynamic --allow-undefined main_c.o -o main_c.wasm
# translate to wat
wasm2wat main_c.wasm > main_c.wat
# we strip the wasm binary
wasm-strip main_c.wasm
```

Let's compile the Zig code:
```sh
# we compile zig
zig build
# translate to wat
wasm2wat zig-out/bin/main.wasm > zig-out/bin/main.wat
```

Now let's compare the two WASMs:
```sh
enzo@merlaux:~/code/zig-cdk$ ll *.wasm
-rwxrwxr-x 1 enzo enzo 170 May 22 18:03 main.wasm*
-rwxrwxr-x 1 enzo enzo 308 May 22 18:04 main_c.wasm*
```

We have created a 170 bytes canister in Zig. Let us see if it works. First we need to create a minimal `dfx.json`:
```json
{
"canisters": {
"hello_zig": {
"type": "custom",
"build": "zig build",
"candid": "not.did",
"wasm": "zig-out/bin/main.wasm"
inline for (std.meta.fields(@TypeOf(b.*))) |f| {
std.log.debug(f.name ++ " {any}", .{@as(f.type, @field(b, f.name))});
}
}
}
```

Then let's create a minimal candid file `not.did`:
```
service: {}
```

We check if it actually works, start-up `dfx`:
```sh
dfx start
dfx deploy
```
### Issues
Interesting issues we stumbled on:
- https://github.com/ziglang/zig/issues/12726
- https://github.com/ziglang/zig/issues/17039

Now, let's call the canister:
```sh
enzo@merlaux:~/code/zig-cdk$ dfx canister call hello_zig hi --output raw | xxd -r -p
WARN: Cannot fetch Candid interface for hi, sending arguments with inferred types.
Hello, World!
```
### Learning

IT WORKS!
- [documentation](https://ziglang.org/documentation/master)
- [zig build system](https://ziglang.org/learn/build-system/)
- [std doc](https://ziglang.org/documentation/master/std/)
- [learn x in y](https://learnxinyminutes.com/docs/zig/)

## Dev
- [interesting build.zig](https://github.com/hardliner66/abps/blob/main/build.zig)

To replicate the code, install `nix` and `direnv`:
The library sometime does not have the answer

- Install nix with [Determinate Systems Nix Installer](https://github.com/DeterminateSystems/nix-installer)

```sh
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```
- [dude the builder](https://www.youtube.com/@dudethebuilder)
- [zig.guide](https://zig.guide/)
- [openmind learning zig](https://www.openmymind.net/learning_zig/)

- Install direnv - [installation page](https://direnv.net/docs/installation.html)

- [hook direnv into your shell](https://direnv.net/docs/hook.html)

Run `direnv allow`. You should be all set-up!
- [ic0 interface](https://internetcomputer.org/docs/current/references/ic-interface-spec#system-api)
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@

# zig
zig
zls

# wasm
wabt
Expand Down
116 changes: 116 additions & 0 deletions organic/hello-ic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# A Zig Canister

Ben Lynn has a blog post series about "Organic Apps", he creates canisters with
`clang`, and `lld` only.

[Quint](https://github.com/q-uint) and [Enzo](https://github.com/EnzoPlayer0ne), over beers, thought it would be fun to create a canister in zig, so
we looked at
Ben's [Hello, Internet Computer](https://fxa77-fiaaa-aaaae-aaana-cai.raw.ic0.app/organic/hello.html)
blog post and decided to port it to Zig the following day.

Anyways, here is the C code from Ben:
```c
#define IMPORT(m,n) __attribute__((import_module(m))) __attribute__((import_name(n)));
#define EXPORT(n) asm(n) __attribute__((visibility("default")))

void reply_append(void*, unsigned) IMPORT("ic0", "msg_reply_data_append");
void reply (void) IMPORT("ic0", "msg_reply");

void go() EXPORT("canister_query hi");

void go() {
char msg[] = "Hello, World!\n";
reply_append(msg, sizeof(msg) - 1);
reply();
}

```

And here is our zig code:
```zig
extern "ic0" fn msg_reply_data_append(ptr: [*]const u8, len: usize) void;
extern "ic0" fn msg_reply() void;

export fn @"canister_query hi"() callconv(.C) void {
const msg = "Hello, World!\n";
msg_reply_data_append(msg, msg.len);
msg_reply();
}
```

Let's compile the C code:
```sh
# we compile the object file
clang --target=wasm32 -c -O3 src/main.c -o main_c.o
# we link the object file
wasm-ld --no-entry --export-dynamic --allow-undefined main_c.o -o main_c.wasm
# translate to wat
wasm2wat main_c.wasm > main_c.wat
# we strip the wasm binary
wasm-strip main_c.wasm
```

Let's compile the Zig code:
```sh
# we compile zig
zig build
# translate to wat
wasm2wat zig-out/bin/main.wasm > zig-out/bin/main.wat
```

Now let's compare the two WASMs:
```sh
enzo@merlaux:~/code/zig-cdk$ ll *.wasm
-rwxrwxr-x 1 enzo enzo 170 May 22 18:03 main.wasm*
-rwxrwxr-x 1 enzo enzo 308 May 22 18:04 main_c.wasm*
```

We have created a 170 bytes canister in Zig. Let us see if it works. First we need to create a minimal `dfx.json`:
```json
{
"canisters": {
"hello_zig": {
"type": "custom",
"build": "zig build",
"candid": "not.did",
"wasm": "zig-out/bin/main.wasm"
}
}
}
```

Then let's create a minimal candid file `not.did`:
```
service: {}
```

We check if it actually works, start-up `dfx`:
```sh
dfx start
dfx deploy
```

Now, let's call the canister:
```sh
enzo@merlaux:~/code/zig-cdk$ dfx canister call hello_zig hi --output raw | xxd -r -p
WARN: Cannot fetch Candid interface for hi, sending arguments with inferred types.
Hello, World!
```

IT WORKS!

## Dev

To replicate the code, install `nix` and `direnv`:

- Install nix with [Determinate Systems Nix Installer](https://github.com/DeterminateSystems/nix-installer)

```sh
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
```

- Install direnv - [installation page](https://direnv.net/docs/installation.html)

- [hook direnv into your shell](https://direnv.net/docs/hook.html)

Run `direnv allow`. You should be all set-up!
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
88 changes: 88 additions & 0 deletions organic/qr-code/Qr-Code-generator/c/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
#
# Makefile for QR Code generator (C)
#
# Copyright (c) Project Nayuki. (MIT License)
# https://www.nayuki.io/page/qr-code-generator-library
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
# - The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# - The Software is provided "as is", without warranty of any kind, express or
# implied, including but not limited to the warranties of merchantability,
# fitness for a particular purpose and noninfringement. In no event shall the
# authors or copyright holders be liable for any claim, damages or other
# liability, whether in an action of contract, tort or otherwise, arising from,
# out of or in connection with the Software or the use or other dealings in the
# Software.
#


# ---- Configuration options ----

# External/implicit variables:
# - CC: The C compiler, such as gcc or clang.
# - CFLAGS: Any extra user-specified compiler flags (can be blank).

# Recommended compiler flags:
CFLAGS += -std=c99 -O

# Extra flags for diagnostics:
# CFLAGS += -g -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -fsanitize=undefined,address


# ---- Controlling make ----

# Clear default suffix rules
.SUFFIXES:

# Don't delete object files
.SECONDARY:

# Stuff concerning goals
.DEFAULT_GOAL = all
.PHONY: all clean


# ---- Targets to build ----

LIB = qrcodegen
LIBFILE = lib$(LIB).a
LIBOBJ = qrcodegen.o
MAINS = qrcodegen-demo qrcodegen-test

# Build all binaries
all: $(LIBFILE) $(MAINS)

# Delete build output
clean:
rm -f -- $(LIBOBJ) $(LIBFILE) $(MAINS:=.o) $(MAINS)
rm -rf .deps

# Executable files
%: %.o $(LIBFILE)
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -L . -l $(LIB)

# Special executable
qrcodegen-test: qrcodegen-test.c $(LIBOBJ:%.o=%.c)
$(CC) $(CFLAGS) $(LDFLAGS) -DQRCODEGEN_TEST -o $@ $^

# The library
$(LIBFILE): $(LIBOBJ)
$(AR) -crs $@ -- $^

# Object files
%.o: %.c .deps/timestamp
$(CC) $(CFLAGS) -c -o $@ -MMD -MF .deps/$*.d $<

# Have a place to store header dependencies automatically generated by compiler
.deps/timestamp:
mkdir -p .deps
touch .deps/timestamp

# Make use of said dependencies if available
-include .deps/*.d
Loading