From bb444a9755877f7997fe59236dbb2474b9283bfe Mon Sep 17 00:00:00 2001 From: ookami125 Date: Sun, 23 Mar 2025 05:24:55 -0400 Subject: [PATCH] Added support for nested structs --- .gitignore | 3 +- src/main.zig | 45 ++++---- src/spacetime.zig | 213 +++++++++++++++++++++++++++-------- src/spacetime/serializer.zig | 6 +- src/spacetime/types.zig | 146 ++++++++++++++++-------- 5 files changed, 296 insertions(+), 117 deletions(-) diff --git a/.gitignore b/.gitignore index e1039d1..9aa732f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ .zig-cache/ -csharp-reference/ -rust-reference/ +references/ zig-out/ \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index aa58f59..07b8fea 100644 --- a/src/main.zig +++ b/src/main.zig @@ -7,23 +7,30 @@ pub export fn spacetime_includes() void { } pub const moduleTablesDef = .{ - .Person = Person, + .person = Person, }; pub const moduleReducersDef = .{ .Init = spacetime.Reducer(Init){ .lifecycle = .Init }, .OnConnect = spacetime.Reducer(OnConnect){ .lifecycle = .OnConnect }, .OnDisconnect = spacetime.Reducer(OnDisconnect){ .lifecycle = .OnDisconnect }, - .add = spacetime.Reducer(add){ .param_names = &[_][:0]const u8{ "name", "age", "blah" }}, + .add = spacetime.Reducer(add){ .param_names = &[_][:0]const u8{ "name" }}, .say_hello = spacetime.Reducer(say_hello){}, }; +pub const DbVector2 = spacetime.Struct(.{ + .name = "DbVector2", + .fields = &[_]spacetime.StructFieldDecl{ + .{ .name = "x", .type = f32, }, + .{ .name = "y", .type = f32 }, + }, +}); + pub const Person = spacetime.Struct(.{ .name = "person", .fields = &[_]spacetime.StructFieldDecl{ - .{ .name = "name", .type = .String, }, - .{ .name = "age", .type = .U32, }, - .{ .name = "blah", .type = .U64, }, + .{ .name = "name", .type = []const u8, }, + .{ .name = "pos", .type = DbVector2, }, }, }); @@ -45,22 +52,22 @@ pub fn OnDisconnect(ctx: *spacetime.ReducerContext) void { spacetime.print("[OnDisconnect]"); } -pub fn add(ctx: *spacetime.ReducerContext, name: []const u8, age: u32, blah: u64) void { - const personTable = ctx.*.db.get(moduleTablesDef.Person); - personTable.insert(Person{ .name = name, .age = age, .blah = blah }); +pub fn add(ctx: *spacetime.ReducerContext, name: []const u8) void { + const personTable = ctx.*.db.get(moduleTablesDef.person); + personTable.insert(Person{ .name = name, .pos = DbVector2{ .x = 10.4, .y = 20.6 } }); - var buf: [128]u8 = undefined; - spacetime.print(std.fmt.bufPrint(&buf, "[add] {{{s}, {}}}!", .{ name, age }) catch "[add] Error: name to long"); + var buf: [128]u8 = undefined; + spacetime.print(std.fmt.bufPrint(&buf, "[add] {{{s}}}!", .{ name }) catch "[add] Error: name to long"); } pub fn say_hello(ctx: *spacetime.ReducerContext) void { - var personIter = ctx.*.db.get(moduleTablesDef.Person).iter(); - while(personIter.next() catch { - @panic("person Iter errored!"); - }) |person| { - var buffer: [512]u8 = undefined; - const msg = std.fmt.bufPrint(&buffer, "Hello, {s} (age: {})!", .{ person.name, person.age }) catch ""; - spacetime.print(msg); - } - spacetime.print("Hello, World!"); + var personIter = ctx.*.db.get(moduleTablesDef.person).iter(); + while(personIter.next() catch { + @panic("person Iter errored!"); + }) |person| { + var buffer: [512]u8 = undefined; + const msg = std.fmt.bufPrint(&buffer, "Hello, {s} (pos: {{{d}, {d}}})!", .{ person.name, person.pos.x, person.pos.y }) catch ""; + spacetime.print(msg); + } + spacetime.print("Hello, World!"); } \ No newline at end of file diff --git a/src/spacetime.zig b/src/spacetime.zig index 62cf307..aec3b4f 100644 --- a/src/spacetime.zig +++ b/src/spacetime.zig @@ -64,6 +64,13 @@ pub const RowIter = extern struct { _inner: u32, pub const INVALID = RowIter{ ._ pub extern "spacetime_10.0" fn row_iter_bsatn_advance(iter: RowIter, buffer_ptr: [*c]u8, buffer_len_ptr: *usize) i16; pub extern "spacetime_10.0" fn datastore_table_scan_bsatn(table_id: TableId, out: [*c]RowIter) u16; +// pub const Identity = struct { +// __identity__: u256, +// }; + +pub const MagicStruct = "spacetime_10.0__struct_"; +pub const MagicTable = "spacetime_10.0__table_"; + pub const EXHAUSTED = -1; pub const OK = 0; pub const NO_SUCH_ITER = 6; @@ -120,7 +127,7 @@ pub fn Reducer(comptime func: anytype) type { pub const StructFieldDecl = struct { name: [:0]const u8, - type: AlgebraicType, + type: type, isPrimaryKey: bool = false, autoInc: bool = false, }; @@ -140,7 +147,7 @@ fn spacetimeType2ZigType(t: AlgebraicType) type { } pub fn Struct(comptime decl: StructDecl) type { - const @"spacetime_10.0__table_" = struct { + const @"spacetime_10.0__struct_" = struct { name: ?[]const u8 = decl.name, table_type: TableType = .User, table_access: TableAccess = .Private, @@ -148,9 +155,9 @@ pub fn Struct(comptime decl: StructDecl) type { var zigStructMembers: []const std.builtin.Type.StructField = &[_]std.builtin.Type.StructField{ std.builtin.Type.StructField{ - .name = "spacetime_10.0__table_", - .type = @"spacetime_10.0__table_", - .default_value = @as(?*const anyopaque, &@"spacetime_10.0__table_"{}), + .name = MagicStruct, + .type = @"spacetime_10.0__struct_", + .default_value = @as(?*const anyopaque, &@"spacetime_10.0__struct_"{}), .is_comptime = false, .alignment = 0, }, @@ -160,7 +167,7 @@ pub fn Struct(comptime decl: StructDecl) type { zigStructMembers = zigStructMembers ++ &[_]std.builtin.Type.StructField{ std.builtin.Type.StructField{ .name = field.name, - .type = spacetimeType2ZigType(field.type), + .type = field.type, .default_value = null, .is_comptime = false, .alignment = 0, @@ -178,6 +185,17 @@ pub fn Struct(comptime decl: StructDecl) type { }); } +// pub fn Table(comptime decl: type) type { +// const @"spacetime_10.0__table_" = struct { +// name: []const u8, +// table_type: TableType = .User, +// table_access: TableAccess = .Private, +// type: decl = std.mem.zeroes(decl), +// }; + +// return @"spacetime_10.0__table_"; +//} + pub fn readArg(allocator: std.mem.Allocator, args: BytesSource, comptime t: AlgebraicType) !spacetimeType2ZigType(t) { switch(t) { .String => { @@ -209,6 +227,8 @@ pub fn zigTypeToSpacetimeType(comptime param: ?type) AlgebraicType { []const u8 => .{ .String = {} }, u32 => .{ .U32 = {}, }, u64 => .{ .U64 = {}, }, + f32 => .{ .F32 = {}, }, + //Identity => .{ .U256 = {}, }, else => { @compileLog(param.?); @compileError("Unmatched type passed to zigTypeToSpacetimeType!"); @@ -216,6 +236,64 @@ pub fn zigTypeToSpacetimeType(comptime param: ?type) AlgebraicType { }; } +pub fn buildTypeList(name: []const u8, default_values: type, raw_types: *[]const AlgebraicType, types: *[]const RawTypeDefV9) usize +{ + var product_elements: []const ProductTypeElement = &[_]ProductTypeElement{}; + + inline for(@typeInfo(default_values).@"struct".fields, 0..) |table_field, i| { + if(i == 0) continue; + + + if(@typeInfo(table_field.type) == .@"struct" and std.meta.fieldIndex(table_field.type, MagicStruct) != null) { + + const table = std.meta.fields(table_field.type)[std.meta.fieldIndex(table_field.type, MagicStruct).?]; + const subname = @as(*table.type, @constCast(@alignCast(@ptrCast(table.default_value)))).*.name.?; + //_ = subname; + //@compileLog(table_field.type); + const id = buildTypeList(subname, table_field.type, raw_types, types); + product_elements = product_elements ++ &[_]ProductTypeElement{ + .{ + .name = table_field.name, + .algebraic_type = .{ + .Ref = .{ + .inner = id, + } + } + } + }; + + } else { + product_elements = product_elements ++ &[_]ProductTypeElement{ + .{ + .name = table_field.name, + .algebraic_type = zigTypeToSpacetimeType(table_field.type) + } + }; + } + + } + + raw_types.* = raw_types.* ++ &[_]AlgebraicType{ + .{ + .Product = .{ + .elements = product_elements, + } + }, + }; + types.* = types.* ++ &[_]RawTypeDefV9{ + .{ + .name = .{ + .scope = &[_][]u8{}, + .name = name + }, + .ty = .{ .inner = raw_types.len-1, }, + .custom_ordering = true, + } + }; + + return types.len-1; +} + pub fn compile(comptime moduleTables : anytype, comptime moduleReducers : anytype) !RawModuleDefV9 { var def : RawModuleDefV9 = undefined; _ = &def; @@ -227,50 +305,20 @@ pub fn compile(comptime moduleTables : anytype, comptime moduleReducers : anytyp var types: []const RawTypeDefV9 = &[_]RawTypeDefV9{}; inline for(std.meta.fields(@TypeOf(moduleTables))) |field| { + //const struct_decl = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*; + //@compileLog(@TypeOf(struct_decl.type)); const default_values = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*; const structInfo = blk: { for(@typeInfo(default_values).@"struct".fields) |structInfoField| { - if(std.mem.eql(u8, structInfoField.name, "spacetime_10.0__table_")) { + if(std.mem.eql(u8, structInfoField.name, MagicStruct)) { break :blk structInfoField.type{}; } } }; - var product_type_ref: AlgebraicTypeRef = undefined; - { - var product_elements: []const ProductTypeElement = &[_]ProductTypeElement{}; - - inline for(@typeInfo(default_values).@"struct".fields, 0..) |table_field, i| { - if(i == 0) continue; - product_elements = product_elements ++ &[_]ProductTypeElement{ - .{ - .name = table_field.name, - .algebraic_type = zigTypeToSpacetimeType(table_field.type) - } - }; - } - - raw_types = raw_types ++ &[_]AlgebraicType{ - .{ - .Product = .{ - .elements = product_elements, - } - }, - }; - - types = types ++ &[_]RawTypeDefV9{ - .{ - .name = .{ - .scope = &[_][]u8{}, - .name = field.name - }, - .ty = .{ .inner = raw_types.len-1, }, - .custom_ordering = true, - } - }; - - product_type_ref.inner = types.len-1; - } + const product_type_ref: AlgebraicTypeRef = AlgebraicTypeRef{ + .inner = buildTypeList(field.name, default_values, &raw_types, &types), + }; const name: []const u8 = structInfo.name.?; const table_type: TableType = structInfo.table_type; @@ -352,6 +400,76 @@ pub fn callReducer(comptime mdef: anytype, id: usize, args: anytype) void { } } +pub fn PrintModule(data: anytype) void { + var buf: [64]u8 = undefined; + print(std.fmt.bufPrint(&buf, "\"{s}\": {{", .{@typeName(@TypeOf(data))}) catch ""); + switch(@TypeOf(data)) { + RawModuleDefV9 => { + PrintModule(data.typespace); + PrintModule(data.tables); + PrintModule(data.reducers); + PrintModule(data.types); + }, + Typespace => { + for(data.types) |_type| { + PrintModule(_type); + } + }, + AlgebraicType => { + switch(data) { + .Ref => PrintModule(data.Ref), + .Product => PrintModule(data.Product), + else => {}, + } + }, + AlgebraicTypeRef => { + PrintModule(data.inner); + }, + ProductType => { + for(data.elements) |elem| { + PrintModule(elem); + } + }, + ProductTypeElement => { + PrintModule(data.name); + PrintModule(data.algebraic_type); + }, + []const RawTableDefV9 => { + for(data) |elem| { + PrintModule(elem); + } + }, + []const RawTypeDefV9 => { + for(data) |elem| { + PrintModule(elem); + } + }, + RawTypeDefV9 => { + PrintModule(data.name); + PrintModule(data.ty); + }, + RawScopedTypeNameV9 => { + PrintModule(data.scope); + PrintModule(data.name); + }, + [][]const u8 => { + for(data) |elem| { + PrintModule(elem); + } + }, + []const u8 => { + print(std.fmt.bufPrint(&buf, "\"{s}\"", .{data}) catch ""); + }, + u32 => { + print(std.fmt.bufPrint(&buf, "{}", .{data}) catch ""); + }, + else => { + print("\"...\""); + }, + } + print("},"); +} + const moduleTablesDef = @import("root").moduleTablesDef; const moduleReducersDef = @import("root").moduleReducersDef; @@ -362,17 +480,24 @@ pub export fn __describe_module__(description: BytesSink) void { var moduleDefBytes = std.ArrayList(u8).init(allocator); defer moduleDefBytes.deinit(); - serialize_module(&moduleDefBytes, comptime compile(moduleTablesDef, moduleReducersDef) catch |err| { + const compiledModule = comptime compile(moduleTablesDef, moduleReducersDef) catch |err| { var buf: [1024]u8 = undefined; const fmterr = std.fmt.bufPrint(&buf, "Error: {}", .{err}) catch { @compileError("ERROR2: No Space Left! Expand error buffer size!"); }; @compileError(fmterr); - }) catch { + }; + + //PrintModule(compiledModule); + + serialize_module(&moduleDefBytes, compiledModule) catch { print("Allocator Error: Cannot continue!"); @panic("Allocator Error: Cannot continue!"); }; + //var buffer: [8196]u8 = undefined; + //print(std.fmt.bufPrint(&buffer, "{any}", .{moduleDefBytes.items}) catch "Expand buffer"); + write_to_sink(description, moduleDefBytes.items); } diff --git a/src/spacetime/serializer.zig b/src/spacetime/serializer.zig index 5f4697d..b1674c7 100644 --- a/src/spacetime/serializer.zig +++ b/src/spacetime/serializer.zig @@ -148,10 +148,8 @@ fn serialize_table_access(array: *std.ArrayList(u8), val: TableAccess) !void { fn serialize_product_type_element(array: *std.ArrayList(u8), val: ProductTypeElement) !void { try array.appendSlice(&[_]u8{ 0 }); - //if(val.name) |name| { try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(val.name.len)))); try array.appendSlice(val.name); - //} try serialize_algebraic_type(array, val.algebraic_type); } @@ -168,6 +166,10 @@ fn serialize_algebraic_type(array: *std.ArrayList(u8), val: AlgebraicType) !void try array.appendSlice(&[_]u8{@intFromEnum(val)}); try serialize_product_type(array, product); }, + AlgebraicType.Ref => |ref| { + try array.appendSlice(&[_]u8{@intFromEnum(val)}); + try array.appendSlice(&std.mem.toBytes(ref.inner)); + }, else => try array.appendSlice(&[_]u8{@intFromEnum(val)}), } } diff --git a/src/spacetime/types.zig b/src/spacetime/types.zig index 56fed25..a89d6e2 100644 --- a/src/spacetime/types.zig +++ b/src/spacetime/types.zig @@ -128,57 +128,92 @@ pub const Lifecycle = enum { OnDisconnect, }; +fn getStructSize(data: anytype) usize { + const struct_type = @TypeOf(data); + const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, spacetime.MagicStruct).?].type; + + const fields = std.meta.fields(@TypeOf(data)); + var size: usize = 0; + inline for(fields) |field| { + switch(field.type) { + []const u8 => { + const val = @field(data, field.name); + size += 4 + val.len; + }, + u32 => { + size += 4; + }, + u64 => { + size += 8; + }, + f32 => { + size += 4; + }, + @"spacetime_10.0__table_" => {}, + else => blk: { + if(@typeInfo(field.type) == .@"struct" and std.meta.fieldIndex(field.type, spacetime.MagicStruct) != null) { + size += getStructSize(@field(data, field.name)); + break :blk; + } + //const subname = @as(*field.type, @constCast(@alignCast(@ptrCast(field.default_value)))).*.name.?; + @compileLog(field.type); + @compileError("Unsupported type in StructSerializer"); + }, + } + } + + return size; +} + +fn getStructData(data: anytype, mem: []u8) []u8 { + const struct_type = @TypeOf(data); + const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, spacetime.MagicStruct).?].type; + + const fields = std.meta.fields(@TypeOf(data)); + var offset_mem = mem; + inline for(fields) |field| { + switch(field.type) { + []const u8 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val.len; + std.mem.copyForwards(u8, offset_mem[4..], val); + offset_mem = offset_mem[4 + val.len ..]; + }, + u32 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val; + offset_mem = offset_mem[4..]; + }, + u64 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(u64, offset_mem[0..4]).* = val; + offset_mem = offset_mem[8..]; + }, + f32 => { + const val = @field(data, field.name); + std.mem.bytesAsValue(f32, offset_mem[0..4]).* = val; + offset_mem = offset_mem[4..]; + }, + @"spacetime_10.0__table_" => {}, + else => blk: { + if(@typeInfo(field.type) == .@"struct" and std.meta.fieldIndex(field.type, spacetime.MagicStruct) != null) { + offset_mem = getStructData(@field(data, field.name), offset_mem); + break :blk; + } + @compileLog(field.type); + @compileError("Unsupported type in StructSerializer"); + }, + } + } + return offset_mem; +} + pub fn StructSerializer(struct_type: type) fn(std.mem.Allocator, struct_type) std.mem.Allocator.Error![]u8 { - - const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, "spacetime_10.0__table_").?].type; - return struct { pub fn serialize(allocator: std.mem.Allocator, data: struct_type) ![]u8 { - const fields = std.meta.fields(@TypeOf(data)); - var size: usize = 0; - inline for(fields) |field| { - switch(field.type) { - []const u8 => { - const val = @field(data, field.name); - size += 4 + val.len; - }, - u32 => { - size += 4; - }, - u64 => { - size += 8; - }, - @"spacetime_10.0__table_" => {}, - else => { - @compileLog(field.type); - @compileError("Unsupported type in StructSerializer"); - }, - } - } + const size: usize = getStructSize(data); const mem = try allocator.alloc(u8, size); - var offset_mem = mem; - inline for(fields) |field| { - switch(field.type) { - []const u8 => { - const val = @field(data, field.name); - std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val.len; - std.mem.copyForwards(u8, offset_mem[4..], val); - offset_mem = offset_mem[4 + val.len ..]; - }, - u32 => { - const val = @field(data, field.name); - std.mem.bytesAsValue(u32, offset_mem[0..4]).* = val; - offset_mem = offset_mem[4..]; - }, - u64 => { - const val = @field(data, field.name); - std.mem.bytesAsValue(u64, offset_mem[0..4]).* = val; - offset_mem = offset_mem[8..]; - }, - @"spacetime_10.0__table_" => {}, - else => @compileError("Unsupported type in StructSerializer"), - } - } + _ = getStructData(data, mem); return mem; } }.serialize; @@ -186,7 +221,7 @@ pub fn StructSerializer(struct_type: type) fn(std.mem.Allocator, struct_type) st pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[]const u8) std.mem.Allocator.Error!*struct_type { - const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, "spacetime_10.0__table_").?].type; + const @"spacetime_10.0__table_" = std.meta.fields(struct_type)[std.meta.fieldIndex(struct_type, spacetime.MagicStruct).?].type; return struct { pub fn deserialize(allocator: std.mem.Allocator, data: *[]const u8) std.mem.Allocator.Error!*struct_type { @@ -209,8 +244,19 @@ pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[ @field(ret.*, field.name) = std.mem.bytesAsValue(u64, offset_mem[0..4]).*; offset_mem = offset_mem[8..]; }, + f32 => { + @field(ret.*, field.name) = std.mem.bytesAsValue(f32, offset_mem[0..4]).*; + offset_mem = offset_mem[4..]; + }, @"spacetime_10.0__table_" => {}, - else => @compileError("Unsupported type in StructDeserializer"), + else => blk: { + if(@typeInfo(field.type) == .@"struct" and std.meta.fieldIndex(field.type, spacetime.MagicStruct) != null) { + @field(ret.*, field.name) = (try StructDeserializer(field.type)(allocator, &offset_mem)).*; + break :blk; + } + @compileLog(field.type); + @compileError("Unsupported type in StructDeserializer"); + }, } } data.* = offset_mem; @@ -222,7 +268,7 @@ pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[ pub fn Table2Struct(comptime table_type: type) type { const fields = std.meta.fields(table_type); - const field = fields[std.meta.fieldIndex(table_type, "spacetime_10.0__table_").?]; + const field = fields[std.meta.fieldIndex(table_type, spacetime.MagicStruct).?]; const struct_type = @as(*const field.type, @alignCast(@ptrCast(field.default_value.?))).*; const table_name: []const u8 = struct_type.name.?;