Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ jobs:
- name: Install dependencies
run: mix deps.get

- name: Compile deps
run: mix deps.compile

- name: Compile (warnings as errors)
run: mix compile --warnings-as-errors

Expand Down
12 changes: 7 additions & 5 deletions examples/basic_usage.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,13 @@ IO.puts("✓ fibonacci(10) = #{result}")
IO.puts("✓ factorial(5) = #{result}")

# 5. Batch calls
{:ok, results} = Firebird.call_many(instance, [
{:add, [10, 20]},
{:multiply, [3, 4]},
{:is_prime, [17]}
])
{:ok, results} =
Firebird.call_many(instance, [
{:add, [10, 20]},
{:multiply, [3, 4]},
{:is_prime, [17]}
])

IO.puts("✓ Batch results: #{inspect(results)}")

# 6. Memory operations
Expand Down
44 changes: 28 additions & 16 deletions examples/build_and_run.exs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ IO.puts("=" |> String.duplicate(60))
# --- Step 1: Create a temporary Go project ---
IO.puts("\n📝 Step 1: Creating Go WASM project...")

tmp_dir = Path.join(System.tmp_dir!(), "firebird_demo_#{:rand.uniform(100000)}")
tmp_dir = Path.join(System.tmp_dir!(), "firebird_demo_#{:rand.uniform(100_000)}")
:ok = Firebird.Builder.scaffold_go(tmp_dir, functions: [:add, :multiply, :double])

# Customize the generated code
Expand Down Expand Up @@ -53,9 +53,10 @@ IO.puts(" Files: #{File.ls!(tmp_dir) |> Enum.join(", ")}")
# --- Step 2: Build WASM ---
IO.puts("\n🔨 Step 2: Building Go → WASM...")

{build_time, {:ok, wasm_path}} = :timer.tc(fn ->
Firebird.Builder.build_go(tmp_dir)
end)
{build_time, {:ok, wasm_path}} =
:timer.tc(fn ->
Firebird.Builder.build_go(tmp_dir)
end)

wasm_size = File.stat!(wasm_path).size
IO.puts(" Built: #{wasm_path}")
Expand All @@ -65,9 +66,10 @@ IO.puts(" Time: #{div(build_time, 1000)}ms")
# --- Step 3: Load and call ---
IO.puts("\n🚀 Step 3: Loading and calling WASM functions...")

{load_time, {:ok, instance}} = :timer.tc(fn ->
Firebird.load(wasm_path, wasi: true)
end)
{load_time, {:ok, instance}} =
:timer.tc(fn ->
Firebird.load(wasm_path, wasi: true)
end)

IO.puts(" Loaded in #{div(load_time, 1000)}ms")

Expand Down Expand Up @@ -95,18 +97,24 @@ IO.puts("\n🔗 Step 4: Using Pipeline to chain calls...")

result =
Firebird.pipeline(instance)
|> Firebird.Pipeline.call(:add, [5, 3]) # 8
|> Firebird.Pipeline.call(:square, [:_]) # 64
|> Firebird.Pipeline.call(:double, [:_]) # 128
# 8
|> Firebird.Pipeline.call(:add, [5, 3])
# 64
|> Firebird.Pipeline.call(:square, [:_])
# 128
|> Firebird.Pipeline.call(:double, [:_])
|> Firebird.Pipeline.run!()

IO.puts(" add(5,3) |> square |> double = #{hd(result)}")

result2 =
Firebird.pipeline(instance)
|> Firebird.Pipeline.call(:fibonacci, [10]) # 55
|> Firebird.Pipeline.call(:add, [:_, 45]) # 100
|> Firebird.Pipeline.call(:multiply, [:_, 3]) # 300
# 55
|> Firebird.Pipeline.call(:fibonacci, [10])
# 100
|> Firebird.Pipeline.call(:add, [:_, 45])
# 300
|> Firebird.Pipeline.call(:multiply, [:_, 3])
|> Firebird.Pipeline.run!()

IO.puts(" fibonacci(10) |> add(_, 45) |> multiply(_, 3) = #{hd(result2)}")
Expand All @@ -125,8 +133,9 @@ IO.puts(" " <> String.duplicate("─", 56))

test_cases = [
{"add(100,200)", :add, [100, 200], fn -> 100 + 200 end},
{"fibonacci(20)", :fibonacci, [20], fn -> Enum.reduce(2..20, {1, 0}, fn _, {b, a} -> {a + b, b} end) |> elem(0) end},
{"factorial(10)", :factorial, [10], fn -> Enum.reduce(1..10, 1, &*/2) end},
{"fibonacci(20)", :fibonacci, [20],
fn -> Enum.reduce(2..20, {1, 0}, fn _, {b, a} -> {a + b, b} end) |> elem(0) end},
{"factorial(10)", :factorial, [10], fn -> Enum.reduce(1..10, 1, &*/2) end}
]

for {name, func, args, native_fn} <- test_cases do
Expand All @@ -137,7 +146,10 @@ for {name, func, args, native_fn} <- test_cases do
native_r = if is_tuple(native_r), do: elem(native_r, 0), else: native_r

status = if go_r == rust_r and rust_r == wat_r, do: "✅", else: "❌"
IO.puts(" #{status} #{String.pad_trailing(name, 16)} #{String.pad_leading("#{go_r}", 10)} #{String.pad_leading("#{rust_r}", 10)} #{String.pad_leading("#{wat_r}", 10)} #{String.pad_leading("#{native_r}", 10)}")

IO.puts(
" #{status} #{String.pad_trailing(name, 16)} #{String.pad_leading("#{go_r}", 10)} #{String.pad_leading("#{rust_r}", 10)} #{String.pad_leading("#{wat_r}", 10)} #{String.pad_leading("#{native_r}", 10)}"
)
end

Firebird.stop(go_instance)
Expand Down
6 changes: 5 additions & 1 deletion examples/compile_to_wasm.exs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ IO.puts("⚙️ Compiling to WebAssembly...")
IO.puts(" Module: #{result.module}")
IO.puts(" WAT size: #{byte_size(result.wat)} bytes")
IO.puts(" WASM size: #{byte_size(result.wasm)} bytes")
IO.puts(" Compression: #{Float.round(byte_size(result.wasm) / byte_size(result.wat) * 100, 1)}%")

IO.puts(
" Compression: #{Float.round(byte_size(result.wasm) / byte_size(result.wat) * 100, 1)}%"
)

IO.puts("")

# Step 3: Load and execute
Expand Down
9 changes: 6 additions & 3 deletions examples/complete_project/lib/my_app.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ defmodule MyApp do

# 5. Block API with auto-cleanup
IO.puts("\n5. Block API:")
{:ok, z} = Firebird.with_instance("wasm/math.wasm", fn wasm ->
Firebird.call_one!(wasm, :fibonacci, [20])
end)

{:ok, z} =
Firebird.with_instance("wasm/math.wasm", fn wasm ->
Firebird.call_one!(wasm, :fibonacci, [20])
end)

IO.puts(" fibonacci(20) = #{z}")

IO.puts("\n✅ All done!")
Expand Down
2 changes: 1 addition & 1 deletion examples/complete_project/lib/my_app/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule MyApp.Application do
def start(_type, _args) do
children = [
# Start the WASM math module as a supervised process
MyApp.Math,
MyApp.Math

# Or use a pool for concurrent workloads:
# {Firebird.Pool, wasm: "wasm/math.wasm", size: 4, name: :math_pool}
Expand Down
33 changes: 21 additions & 12 deletions examples/complete_wasm_demo/demo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,13 @@ IO.puts(" Firebird.run!(\"math.wasm\", :add, [999, 1]) = #{inspect(result)}")
# --- 6. With Instance (auto-cleanup) ---
IO.puts("\n━━━ 6. With Instance (auto-cleanup) ━━━\n")

{:ok, result} = Firebird.with_instance("fixtures/math.wasm", fn wasm ->
{:ok, [a]} = Firebird.call(wasm, :add, [10, 20])
{:ok, [b]} = Firebird.call(wasm, :multiply, [a, 3])
b
end)
{:ok, result} =
Firebird.with_instance("fixtures/math.wasm", fn wasm ->
{:ok, [a]} = Firebird.call(wasm, :add, [10, 20])
{:ok, [b]} = Firebird.call(wasm, :multiply, [a, 3])
b
end)

IO.puts(" (10 + 20) * 3 = #{result}")

# --- 7. Pipe API ---
Expand Down Expand Up @@ -120,12 +122,16 @@ IO.puts("\n━━━ 9. Connection Pool ━━━\n")
{:ok, pool} = Firebird.start_pool(wasm: "fixtures/math.wasm", size: 4)
IO.puts(" Started pool with 4 instances")

results = 1..10
|> Task.async_stream(fn i ->
{:ok, [r]} = Firebird.pool_call(pool, :fibonacci, [i])
{i, r}
end, max_concurrency: 4)
|> Enum.map(fn {:ok, r} -> r end)
results =
1..10
|> Task.async_stream(
fn i ->
{:ok, [r]} = Firebird.pool_call(pool, :fibonacci, [i])
{i, r}
end,
max_concurrency: 4
)
|> Enum.map(fn {:ok, r} -> r end)

for {n, fib} <- results do
IO.puts(" fibonacci(#{n}) = #{fib}")
Expand All @@ -151,7 +157,10 @@ IO.puts(" Next prime after 100: #{r}")
IO.puts(" Digit sum of 9999: #{r}")

# Sort array in WASM memory
data = <<5::little-signed-32, 3::little-signed-32, 1::little-signed-32, 4::little-signed-32, 2::little-signed-32>>
data =
<<5::little-signed-32, 3::little-signed-32, 1::little-signed-32, 4::little-signed-32,
2::little-signed-32>>

:ok = Firebird.write_memory(algo, 0, data)
Firebird.call(algo, :sort_i32, [0, 5])
{:ok, sorted} = Firebird.read_memory(algo, 0, 20)
Expand Down
57 changes: 33 additions & 24 deletions examples/comprehensive_demo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ IO.puts(" Rust exports: #{inspect(exports)}")

all = Firebird.all_exports(rust)
IO.puts(" All exports (with types):")

for {name, type} <- all do
IO.puts(" #{name}: #{type}")
end
Expand All @@ -81,15 +82,17 @@ IO.puts("")
IO.puts("4. BATCH CALLS (call_many)")
IO.puts("-" |> String.duplicate(40))

{:ok, results} = Firebird.call_many(rust, [
{:add, [1, 2]},
{:multiply, [3, 4]},
{:fibonacci, [10]},
{:factorial, [5]},
{:gcd, [48, 18]}
])
{:ok, results} =
Firebird.call_many(rust, [
{:add, [1, 2]},
{:multiply, [3, 4]},
{:fibonacci, [10]},
{:factorial, [5]},
{:gcd, [48, 18]}
])

labels = ["add(1,2)", "multiply(3,4)", "fibonacci(10)", "factorial(5)", "gcd(48,18)"]

for {label, [val]} <- Enum.zip(labels, results) do
IO.puts(" #{label} = #{val}")
end
Expand All @@ -106,7 +109,8 @@ IO.puts("-" |> String.duplicate(40))
IO.puts(" Memory size: #{size} bytes (#{div(size, 65_536)} pages)")

# Write and read back
data = <<72, 101, 108, 108, 111>> # "Hello"
# "Hello"
data = <<72, 101, 108, 108, 111>>
:ok = Firebird.write_memory(rust, 0, data)
{:ok, read_back} = Firebird.read_memory(rust, 0, 5)
IO.puts(" Wrote #{inspect(data)} -> Read #{inspect(read_back)} (\"#{read_back}\")")
Expand All @@ -132,12 +136,14 @@ IO.puts(" Pool started with 4 instances")
IO.puts(" pool_call add(100, 200) = #{result}")

# Concurrent
tasks = for i <- 1..10 do
Task.async(fn ->
{:ok, [r]} = Firebird.pool_call(pool, :fibonacci, [i])
r
end)
end
tasks =
for i <- 1..10 do
Task.async(fn ->
{:ok, [r]} = Firebird.pool_call(pool, :fibonacci, [i])
r
end)
end

results = Task.await_many(tasks)
IO.puts(" 10 concurrent fibonacci calls: #{inspect(results)}")

Expand All @@ -154,7 +160,9 @@ IO.puts("-" |> String.duplicate(40))

{:ok, str_wasm} = Firebird.load("fixtures/string_utils.wasm")

{:ok, "HELLO WORLD"} = Firebird.WasmString.call_with_string(str_wasm, :to_uppercase, "hello world")
{:ok, "HELLO WORLD"} =
Firebird.WasmString.call_with_string(str_wasm, :to_uppercase, "hello world")

IO.puts(" to_uppercase(\"hello world\") = \"HELLO WORLD\"")

{:ok, ptr, len} = Firebird.WasmString.write_string(str_wasm, "firebird")
Expand All @@ -181,18 +189,19 @@ test_cases = [
{:factorial, [10]},
{:gcd, [252, 105]},
{:power, [2, 16]},
{:is_prime, [104729]},
{:is_prime, [104_729]},
{:sum_range, [1, 1000]}
]

all_match = Enum.all?(test_cases, fn {func, args} ->
{:ok, [r]} = Firebird.call(rust, func, args)
{:ok, [g]} = Firebird.call(go, func, args)
match = r == g
status = if match, do: "✅", else: "❌"
IO.puts(" #{status} #{func}(#{Enum.join(args, ", ")}): Rust=#{r}, Go=#{g}")
match
end)
all_match =
Enum.all?(test_cases, fn {func, args} ->
{:ok, [r]} = Firebird.call(rust, func, args)
{:ok, [g]} = Firebird.call(go, func, args)
match = r == g
status = if match, do: "✅", else: "❌"
IO.puts(" #{status} #{func}(#{Enum.join(args, ", ")}): Rust=#{r}, Go=#{g}")
match
end)

IO.puts(" All results match: #{all_match}")

Expand Down
25 changes: 14 additions & 11 deletions examples/concurrent_pool.exs
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,22 @@ IO.puts("Started pool with #{Firebird.Pool.status(pool).pool_size} instances\n")
n = 100

# Sequential
{sequential_time, _} = :timer.tc(fn ->
for i <- 1..n do
{:ok, [_]} = Firebird.Pool.call(pool, :fibonacci, [30])
end
end)
{sequential_time, _} =
:timer.tc(fn ->
for i <- 1..n do
{:ok, [_]} = Firebird.Pool.call(pool, :fibonacci, [30])
end
end)

# Concurrent with tasks
{concurrent_time, _} = :timer.tc(fn ->
1..n
|> Enum.map(fn _i ->
Task.async(fn -> Firebird.Pool.call!(pool, :fibonacci, [30]) end)
{concurrent_time, _} =
:timer.tc(fn ->
1..n
|> Enum.map(fn _i ->
Task.async(fn -> Firebird.Pool.call!(pool, :fibonacci, [30]) end)
end)
|> Task.await_many()
end)
|> Task.await_many()
end)

IO.puts("#{n} fibonacci(30) calls:")
IO.puts(" Sequential: #{div(sequential_time, 1000)}ms")
Expand All @@ -39,6 +41,7 @@ IO.puts(" Speedup: #{Float.round(sequential_time / max(concurrent_time, 1),
# ── 3. Mixed workloads ──────────────────────────────────────────────────────

IO.puts("Mixed concurrent workload:")

tasks = [
Task.async(fn -> {:add, Firebird.Pool.call!(pool, :add, [100, 200])} end),
Task.async(fn -> {:mul, Firebird.Pool.call!(pool, :multiply, [7, 8])} end),
Expand Down
23 changes: 15 additions & 8 deletions examples/end_to_end_demo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,12 @@ IO.puts(" Binary search for 30 → index #{idx}")

IO.puts("\n🌍 Cross-language comparison (WAT vs Go):\n")

for {func, args} <- [{:add, [100, 200]}, {:multiply, [7, 8]}, {:fibonacci, [15]}, {:factorial, [10]}] do
for {func, args} <- [
{:add, [100, 200]},
{:multiply, [7, 8]},
{:fibonacci, [15]},
{:factorial, [10]}
] do
{:ok, [wat_r]} = Firebird.call(wat, func, args)
{:ok, [go_r]} = Firebird.call(go, func, args)
match = if wat_r == go_r, do: "✅", else: "❌"
Expand All @@ -124,13 +129,15 @@ end

IO.puts("\n📦 Batch operations:\n")

{:ok, results} = Firebird.WasmRunner.run_batch("fixtures/math.wasm", [
{:add, [1, 2]},
{:multiply, [3, 4]},
{:fibonacci, [10]},
{:factorial, [5]},
{:gcd, [48, 36]}
])
{:ok, results} =
Firebird.WasmRunner.run_batch("fixtures/math.wasm", [
{:add, [1, 2]},
{:multiply, [3, 4]},
{:fibonacci, [10]},
{:factorial, [5]},
{:gcd, [48, 36]}
])

IO.puts(" Batch results: #{inspect(results)}")

# ── 9. WasmRunner auto-WASI ──────────────────────────────────
Expand Down
Loading
Loading