codecs.zig (6857B)
1 const std = @import("std"); 2 const tree = @import("tree.zig"); 3 const Arena = @import("arena.zig").Arena; 4 const reduce = @import("reduce.zig"); 5 6 // --------------------------------------------------------------------------- 7 // Number encoding/decoding 8 // --------------------------------------------------------------------------- 9 10 pub fn ofNumber(arena: *Arena, n: u64) !u32 { 11 if (n == 0) { 12 return try arena.alloc(.leaf); 13 } 14 const bit = if (n % 2 == 1) try arena.alloc(.{ .stem = .{ .child = try arena.alloc(.leaf) } }) else try arena.alloc(.leaf); 15 const rest = try ofNumber(arena, n / 2); 16 return try arena.alloc(.{ .fork = .{ .left = bit, .right = rest } }); 17 } 18 19 pub fn toNumber(arena: *Arena, idx: u32) !?u64 { 20 const node = try reduce.reduce(idx, arena, 10_000); 21 const n = arena.get(node); 22 return switch (n.*) { 23 .leaf => 0, 24 .stem => return null, 25 .fork => |f| blk: { 26 const bit_node = try reduce.reduce(f.left, arena, 10_000); 27 const bit = arena.get(bit_node); 28 const bit_val: u64 = switch (bit.*) { 29 .leaf => 0, 30 .stem => |s| if (arena.get(s.child).* == .leaf) 1 else return null, 31 else => return null, 32 }; 33 const rest = try toNumber(arena, f.right) orelse return null; 34 break :blk bit_val + 2 * rest; 35 }, 36 .app => return null, 37 }; 38 } 39 40 // --------------------------------------------------------------------------- 41 // List encoding/decoding 42 // --------------------------------------------------------------------------- 43 44 pub fn ofList(arena: *Arena, items: []const u32) !u32 { 45 var result = try arena.alloc(.leaf); 46 var i: usize = items.len; 47 while (i > 0) { 48 i -= 1; 49 result = try arena.alloc(.{ .fork = .{ .left = items[i], .right = result } }); 50 } 51 return result; 52 } 53 54 pub fn toList(arena: *Arena, idx: u32) !?std.ArrayList(u32) { 55 var result = std.ArrayList(u32).empty; 56 errdefer result.deinit(arena.allocator); 57 58 var current = idx; 59 while (true) { 60 const node = try reduce.reduce(current, arena, 10_000); 61 const n = arena.get(node); 62 switch (n.*) { 63 .leaf => return result, 64 .stem => return null, 65 .fork => |f| { 66 try result.append(arena.allocator, f.left); 67 current = f.right; 68 }, 69 .app => return null, 70 } 71 } 72 } 73 74 // --------------------------------------------------------------------------- 75 // String / Bytes encoding/decoding 76 // Strings are lists of byte values (each character encoded as a number tree). 77 // --------------------------------------------------------------------------- 78 79 pub fn ofString(arena: *Arena, s: []const u8) !u32 { 80 var bytes = try arena.allocator.alloc(u32, s.len); 81 defer arena.allocator.free(bytes); 82 for (s, 0..) |c, i| { 83 bytes[i] = try ofNumber(arena, c); 84 } 85 return try ofList(arena, bytes); 86 } 87 88 pub fn toString(arena: *Arena, idx: u32) !?[]u8 { 89 var list = try toList(arena, idx) orelse return null; 90 defer list.deinit(arena.allocator); 91 var result = try arena.allocator.alloc(u8, list.items.len); 92 errdefer arena.allocator.free(result); 93 for (list.items, 0..) |elem_idx, i| { 94 const num = try toNumber(arena, elem_idx) orelse { 95 arena.allocator.free(result); 96 return null; 97 }; 98 if (num > 255) { 99 arena.allocator.free(result); 100 return null; 101 } 102 result[i] = @intCast(num); 103 } 104 return result; 105 } 106 107 pub fn ofBytes(arena: *Arena, bytes: []const u8) !u32 { 108 return try ofString(arena, bytes); 109 } 110 111 pub fn toBytes(arena: *Arena, idx: u32) !?[]u8 { 112 return try toString(arena, idx); 113 } 114 115 // --------------------------------------------------------------------------- 116 // Result unwrapping (ok/err protocol) 117 // ok value rest = pair true (pair value rest) 118 // err code rest = pair false (pair code rest) 119 // --------------------------------------------------------------------------- 120 121 pub const UnwrapResult = struct { 122 ok: bool, 123 value: u32, 124 rest: u32, 125 }; 126 127 pub fn unwrapResult(arena: *Arena, idx: u32) !?UnwrapResult { 128 const node = try reduce.reduce(idx, arena, 10_000); 129 const n = arena.get(node); 130 switch (n.*) { 131 .fork => |f| { 132 const tag = try reduce.reduce(f.left, arena, 10_000); 133 const rest_pair = try reduce.reduce(f.right, arena, 10_000); 134 const rp = arena.get(rest_pair); 135 switch (rp.*) { 136 .fork => |rf| { 137 const is_ok = tree.sameTree(arena, tag, try arena.alloc(.{ .stem = .{ .child = try arena.alloc(.leaf) } })); 138 return UnwrapResult{ 139 .ok = is_ok, 140 .value = rf.left, 141 .rest = rf.right, 142 }; 143 }, 144 else => return null, 145 } 146 }, 147 else => return null, 148 } 149 } 150 151 // --------------------------------------------------------------------------- 152 // Host ABI value unwrapping 153 // A host ABI value is: pair tag payload 154 // --------------------------------------------------------------------------- 155 156 pub const HostValue = struct { 157 tag: u64, 158 payload: u32, 159 }; 160 161 pub fn unwrapHostValue(arena: *Arena, idx: u32) !?HostValue { 162 const node = try reduce.reduce(idx, arena, 10_000); 163 const n = arena.get(node); 164 switch (n.*) { 165 .fork => |f| { 166 const tag_num = try toNumber(arena, f.left) orelse return null; 167 return HostValue{ .tag = tag_num, .payload = f.right }; 168 }, 169 else => return null, 170 } 171 } 172 173 /// Returns true if the tree is a valid boolean (Leaf=false, Stem Leaf=true). 174 pub fn isBool(arena: *Arena, idx: u32) !bool { 175 const node = try reduce.reduce(idx, arena, 10_000); 176 const n = arena.get(node); 177 return switch (n.*) { 178 .leaf => true, 179 .stem => |s| arena.get(s.child).* == .leaf, 180 else => false, 181 }; 182 } 183 184 /// Extract the boolean value: false for Leaf, true for Stem Leaf. 185 /// Returns null if the tree is not a valid boolean. 186 pub fn toBool(arena: *Arena, idx: u32) !?bool { 187 const node = try reduce.reduce(idx, arena, 10_000); 188 const n = arena.get(node); 189 return switch (n.*) { 190 .leaf => false, 191 .stem => |s| if (arena.get(s.child).* == .leaf) true else null, 192 else => null, 193 }; 194 } 195 196 // --------------------------------------------------------------------------- 197 // Host ABI tag constants 198 // --------------------------------------------------------------------------- 199 200 pub const HOST_TREE_TAG: u64 = 0; 201 pub const HOST_STRING_TAG: u64 = 1; 202 pub const HOST_NUMBER_TAG: u64 = 2; 203 pub const HOST_BOOL_TAG: u64 = 3; 204 pub const HOST_LIST_TAG: u64 = 4; 205 pub const HOST_BYTES_TAG: u64 = 5;