From d144069dbe5f49f7af178ad4a0b51cf69c61d463 Mon Sep 17 00:00:00 2001 From: ookami125 Date: Fri, 28 Mar 2025 16:40:20 -0400 Subject: [PATCH] Some changes with more on the way --- spacetimedb.sh | 11 + src/main.zig | 9 +- src/spacetime.zig | 351 ++++++++++++++++++++------- src/spacetime/serializer.zig | 56 ++++- src/spacetime/types.zig | 447 +++++++++++++++++++++++------------ 5 files changed, 622 insertions(+), 252 deletions(-) diff --git a/spacetimedb.sh b/spacetimedb.sh index 38b71dc..fbf0e8d 100755 --- a/spacetimedb.sh +++ b/spacetimedb.sh @@ -2,4 +2,15 @@ DB_HASH=$(spacetime list 2>/dev/null | tail -1) func=$1; shift; + +if [[ "$func" == "publish" ]]; then + zig build -freference-trace=100 || exit 1 + spacetime logout + spacetime login --server-issued-login local + spacetime publish -y --server local --bin-path=zig-out/bin/stdb-zig-helloworld.wasm + DB_HASH=$(spacetime list 2>/dev/null | tail -1) + spacetime logs $DB_HASH + exit $? +fi + spacetime "$func" $DB_HASH "$@" \ No newline at end of file diff --git a/src/main.zig b/src/main.zig index 4564704..255d758 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,10 +1,11 @@ const std = @import("std"); const spacetime = @import("spacetime.zig"); +comptime { _ = spacetime; } -pub export fn spacetime_includes() void { - _ = &spacetime.__describe_module__; - _ = &spacetime.__call_reducer__; -} +pub const std_options = std.Options{ + .log_level = .debug, + .logFn = spacetime.logFn, +}; pub const DbVector2 = struct { x: f32, diff --git a/src/spacetime.zig b/src/spacetime.zig index b27e030..3794b4c 100644 --- a/src/spacetime.zig +++ b/src/spacetime.zig @@ -47,73 +47,121 @@ pub extern "spacetime_10.0" fn console_log( message_len: usize, ) void; -pub fn print(fmt: []const u8) void { - console_log(2, null, 0, null, 0, 0, fmt.ptr, fmt.len); -} - -pub fn debug_print(comptime fmt: []const u8, args: anytype) void { - var buf: [512]u8 = undefined; - var fbs = std.io.fixedBufferStream(&buf); - std.fmt.format(fbs.writer().any(), fmt, args) catch { - return print("Expand the buf in debug_print!"); +pub fn logFn(comptime level: std.log.Level, comptime _: @TypeOf(.enum_literal), comptime fmt: []const u8, args: anytype) void { + const allocator = std.heap.wasm_allocator; + const msg = std.fmt.allocPrint(allocator, fmt, args) catch "debug_print allocation failure!"; + defer allocator.free(msg); + const outLevel = switch(level) { + .err => 0, + .warn => 1, + .info => 2, + .debug => 3, }; - return print(fbs.getWritten()); + console_log(outLevel, null, 0, null, 0, 0, msg.ptr, msg.len); + } pub const BytesSink = extern struct { inner: u32 }; pub const BytesSource = extern struct { inner: u32 }; +pub const TableId = extern struct { _inner: u32, }; +pub const RowIter = extern struct { _inner: u32, pub const INVALID = RowIter{ ._inner = 0}; }; +pub const IndexId = extern struct{ _inner: u32 }; +pub const ColId = extern struct { _inner: u16 }; + +pub const Identity = struct { + __identity__: u256, +}; + +pub const Timestamp = struct { + __timestamp_micros_since_unix_epoch__: i64 +}; + +pub const TimeDuration = struct { + __time_duration_micros__: i64 +}; + +pub const ScheduleAt = union(enum){ + Interval: TimeDuration, + Time: Timestamp, +}; + +pub const ConnectionId = struct { + __connection_id__: u128, +}; + +pub const SpacetimeValue = enum(u1) { + OK = 0, + EXHAUSTED = 1, +}; + +pub const SpacetimeError = error { + HOST_CALL_FAILURE, + NOT_IN_TRANSACTION, + BSATN_DECODE_ERROR, + NO_SUCH_TABLE, + NO_SUCH_INDEX, + NO_SUCH_ITER, + NO_SUCH_BYTES, + NO_SPACE, + BUFFER_TOO_SMALL, + UNIQUE_ALREADY_EXISTS, + SCHEDULE_AT_DELAY_TOO_LONG, + INDEX_NOT_UNIQUE, + NO_SUCH_ROW, +}; pub extern "spacetime_10.0" fn bytes_sink_write(sink: BytesSink, buffer_ptr: [*c]const u8, buffer_len_ptr: *usize) u16; pub extern "spacetime_10.0" fn bytes_source_read(source: BytesSource, buffer_ptr: [*c]u8, buffer_len_ptr: *usize) i16; -pub const TableId = extern struct { _inner: u32, }; pub extern "spacetime_10.0" fn table_id_from_name(name: [*c]const u8, name_len: usize, out: *TableId) u16; +pub extern "spacetime_10.0" fn index_id_from_name(name_ptr: [*c]const u8, name_len: usize, out: *IndexId) u16; + pub extern "spacetime_10.0" fn datastore_insert_bsatn(table_id: TableId, row_ptr: [*c]const u8, row_len_ptr: *usize) u16; - -pub const RowIter = extern struct { _inner: u32, pub const INVALID = RowIter{ ._inner = 0}; }; 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 extern "spacetime_10.0" fn datastore_index_scan_range_bsatn( index_id: IndexId, prefix_ptr: [*c]const u8, prefix_len: usize, prefix_elems: ColId, rstart_ptr: [*c]const u8, rstart_len: usize, rend_ptr: [*c]const u8, rend_len: usize, out: *RowIter) u16; +pub extern "spacetime_10.0" fn row_iter_bsatn_close(iter: RowIter) u16; -pub const ScheduleAt = union(enum){ - Interval: struct{ __time_duration_micros__: i64 }, - Time: struct{ __timestamp_micros_since_unix_epoch__: i64 }, -}; +pub extern "spacetime_10.0" fn datastore_delete_by_index_scan_range_bsatn(index_id: IndexId, prefix_ptr: [*c]const u8, prefix_len: usize, prefix_elems: ColId, rstart_ptr: [*c]const u8, rstart_len: usize, rend_ptr: [*c]const u8, rend_len: usize, out: [*c]u32) u16; -pub const EXHAUSTED = -1; -pub const OK = 0; -pub const NO_SUCH_ITER = 6; -pub const NO_SUCH_BYTES = 8; -pub const NO_SPACE = 9; -pub const BUFFER_TOO_SMALL = 11; +pub fn retMap(errVal: i17) !SpacetimeValue { + return switch(errVal) { + -1 => SpacetimeValue.EXHAUSTED, + 0 => SpacetimeValue.OK, + 1 => SpacetimeError.HOST_CALL_FAILURE, + 2 => SpacetimeError.NOT_IN_TRANSACTION, + 3 => SpacetimeError.BSATN_DECODE_ERROR, + 4 => SpacetimeError.NO_SUCH_TABLE, + 5 => SpacetimeError.NO_SUCH_INDEX, + 6 => SpacetimeError.NO_SUCH_ITER, + 8 => SpacetimeError.NO_SUCH_BYTES, + 9 => SpacetimeError.NO_SPACE, + 11 => SpacetimeError.BUFFER_TOO_SMALL, + 12 => SpacetimeError.UNIQUE_ALREADY_EXISTS, + 13 => SpacetimeError.SCHEDULE_AT_DELAY_TOO_LONG, + 14 => SpacetimeError.INDEX_NOT_UNIQUE, + 15 => SpacetimeError.NO_SUCH_ROW, + else => unreachable, + }; +} + +pub const ReducerError = SpacetimeError || std.mem.Allocator.Error || std.fmt.BufPrintError; pub fn read_bytes_source(source: BytesSource, buf: []u8) ![]u8 { - const INVALID: i16 = NO_SUCH_BYTES; - var buf_len = buf.len; - const ret = bytes_source_read(source, @ptrCast(buf), &buf_len); - switch(ret) { - -1, 0 => {}, - INVALID => return error.InvalidSource, - else => unreachable, - } - + _ = try retMap(bytes_source_read(source, @ptrCast(buf), &buf_len)); return buf[0..buf_len]; } -pub fn write_to_sink(sink: BytesSink, _buf: []const u8) void { +pub fn write_to_sink(sink: BytesSink, _buf: []const u8) !void { var buf: []const u8 = _buf; while(true) { const len: *usize = &buf.len; - switch(bytes_sink_write(sink, buf.ptr, len)) { - 0 => { - buf = buf[len.*..]; - if(buf.len == 0) { - break; - } - }, - NO_SUCH_BYTES => @panic("invalid sink passed"), - NO_SPACE => @panic("no space left at sink"), - else => unreachable, + _ = try retMap(bytes_sink_write(sink, buf.ptr, len)); + buf = buf[len.*..]; + if(buf.len == 0) { + break; } } } @@ -133,11 +181,25 @@ pub const StructDecl = struct { fn spacetimeType2ZigType(t: AlgebraicType) type { return switch (t) { .String => []const u8, - .U32 => u32, - .U64 => u64, + .Bool => bool, + .I8 => i8, + .U8 => u8, + .I16 => i16, + .U16 => u16, .I32 => i32, + .U32 => u32, .I64 => i64, - else => unreachable, + .U64 => u64, + .I128 => i128, + .U128 => u128, + .I256 => i256, + .U256 => u256, + .F32 => f32, + .F64 => f64, + else => { + @compileLog(t); + @compileError("spacetimeType2ZigType: unsupported type!"); + }, }; } @@ -146,23 +208,54 @@ const StructFieldImpl = struct { type: AlgebraicType, }; -pub fn readArg(allocator: std.mem.Allocator, args: BytesSource, comptime t: AlgebraicType) !spacetimeType2ZigType(t) { +pub fn readArg(allocator: std.mem.Allocator, args: BytesSource, comptime t: type) !t { switch(t) { - .String => { + []const u8 => { var maxbuf: [4]u8 = undefined; const len_buf = try read_bytes_source(args, &maxbuf); const len: usize = std.mem.bytesToValue(u32, len_buf); const string_buf = try allocator.alloc(u8, len); return try read_bytes_source(args, string_buf); }, - .U32, .U64, .I32, .I64 => { - const read_type = spacetimeType2ZigType(t); + i8, u8, i16, u16, i32, u32, + i64, u64, i128, u128, i256, u256, + f32, f64 => { + const read_type = t; var maxbuf: [@sizeOf(read_type)]u8 = undefined; const len_buf = try read_bytes_source(args, &maxbuf); - const len: read_type = std.mem.bytesToValue(read_type, len_buf); - return len; + return std.mem.bytesToValue(t, len_buf); + }, + else => { + switch(@typeInfo(t)) { + .@"struct" => { + const fields = std.meta.fields(t); + var temp: t = undefined; + inline for(fields) |field| { + @field(temp, field.name) = try readArg(allocator, args, field.type); + } + return temp; + }, + .@"union" => { + const tagType = std.meta.Tag(t); + const intType = u8; + const tag: tagType = @enumFromInt(try readArg(allocator, args, intType)); + var temp: t = undefined;//@unionInit(t, @tagName(tag), undefined); + switch(tag) { + inline else => |tag_field| { + const field = std.meta.fields(t)[@intFromEnum(tag_field)]; + @field(temp, field.name) = (try readArg(allocator, args, field.type)); + + } + } + //@field(temp, field.name) = try readArg(allocator, args, @TypeOf(field)); + return temp; + }, + else => { + @compileLog(t); + @compileError("unsupported type in readArg!"); + } + } }, - else => @compileError("unsupported type in readArg!"), } } @@ -175,7 +268,7 @@ pub fn zigTypeToSpacetimeType(comptime param: ?type) AlgebraicType { u32 => .{ .U32 = {}, }, u64 => .{ .U64 = {}, }, f32 => .{ .F32 = {}, }, - //Identity => .{ .U256 = {}, }, + u256 => .{ .U256 = {}, }, else => blk: { if(@typeInfo(param.?) == .@"struct") { var elements: []const ProductTypeElement = &.{}; @@ -194,6 +287,23 @@ pub fn zigTypeToSpacetimeType(comptime param: ?type) AlgebraicType { .elements = elements } }; + } else if(@typeInfo(param.?) == .@"union") { + var variants: []const SumTypeVariant = &.{}; + const fields = std.meta.fields(param.?); + for(fields) |field| { + variants = variants ++ &[_]SumTypeVariant{ + SumTypeVariant{ + .name = field.name, + .algebraic_type = zigTypeToSpacetimeType(field.type), + }, + }; + } + + break :blk .{ + .Sum = SumType{ + .variants = variants + } + }; } @compileLog(param.?); @compileError("Unmatched type passed to zigTypeToSpacetimeType!"); @@ -277,6 +387,27 @@ pub fn addStructImpl(structImpls: *[]const StructImpl, layout: anytype) u32 { return structImpls.len - 1; } +pub fn getStructImplOrType(structImpls: []const StructImpl, layout: type) AlgebraicType { + const name = blk: { + var temp: []const u8 = @typeName(layout); + if(std.mem.lastIndexOf(u8, temp, ".")) |idx| + temp = temp[idx+1..]; + break :blk temp; + }; + + inline for(structImpls, 0..) |structImpl, i| { + if(std.mem.eql(u8, structImpl.name, name)) { + return .{ + .Ref = AlgebraicTypeRef{ + .inner = i, + }, + }; + } + } + + return zigTypeToSpacetimeType(layout); +} + pub fn compile(comptime moduleTables : []const Table, comptime moduleReducers : []const Reducer) !RawModuleDefV9 { var def : RawModuleDefV9 = undefined; _ = &def; @@ -290,22 +421,67 @@ pub fn compile(comptime moduleTables : []const Table, comptime moduleReducers : var structDecls: []const StructImpl = &[_]StructImpl{}; inline for(moduleTables) |table| { - //const table: @as(*const field.type, @alignCast(@ptrCast(field.default_value))).* = .{}; - const name: []const u8 = table.name.?; + const table_name: []const u8 = table.name.?; const table_type: TableType = table.type; const table_access: TableAccess = table.access; const product_type_ref: AlgebraicTypeRef = AlgebraicTypeRef{ .inner = addStructImpl(&structDecls, table.schema), }; + const primary_key: []const u16 = blk: { + if(table.primary_key) |key| { + break :blk &[_]u16{ std.meta.fieldIndex(table.schema, key).?, }; + } + break :blk &[_]u16{}; + }; + + var indexes: []const RawIndexDefV9 = &[_]RawIndexDefV9{}; + if(table.primary_key) |key| { + indexes = indexes ++ &[_]RawIndexDefV9{ + RawIndexDefV9{ + .name = null, + .accessor_name = key, + .algorithm = .{ + .BTree = &.{ 0 } + } + } + }; + } + + var constraints: []const RawConstraintDefV9 = &[_]RawConstraintDefV9{}; + if(table.primary_key) |_| { + constraints = constraints ++ &[_]RawConstraintDefV9{ + RawConstraintDefV9{ + .name = null, + .data = .{ .unique = .{ .Columns = &.{ primary_key[0] } } }, + } + }; + } + + const schedule: ?RawScheduleDefV9 = schedule_blk: { + if(table.schedule_reducer == null) break :schedule_blk null; + const column = column_blk: for(std.meta.fields(table.schema), 0..) |field, i| { + if(field.type == ScheduleAt) break :column_blk i; + }; + const resolvedReducer = blk: for(moduleReducers) |reducer| { + if(reducer.func == table.schedule_reducer.?.func) + break :blk reducer; + }; + break :schedule_blk RawScheduleDefV9{ + .name = table_name ++ "_sched", + .reducer_name = resolvedReducer.name.?, + .scheduled_at_column = column, + }; + }; + tableDefs = tableDefs ++ &[_]RawTableDefV9{ .{ - .name = name, + .name = table_name, .product_type_ref = product_type_ref, - .primary_key = &[_]u16{}, - .indexes = &[_]RawIndexDefV9{}, - .constraints = &[_]RawConstraintDefV9{}, + .primary_key = primary_key, + .indexes = indexes, + .constraints = constraints, .sequences = &[_]RawSequenceDefV9{}, - .schedule = null, + .schedule = schedule, .table_type = table_type, .table_access = table_access, } @@ -356,7 +532,7 @@ pub fn compile(comptime moduleTables : []const Table, comptime moduleReducers : params = params ++ &[_]ProductTypeElement{ .{ .name = param_name, - .algebraic_type = zigTypeToSpacetimeType(param.type), + .algebraic_type = getStructImplOrType(structDecls, param.type.?), } }; } @@ -382,7 +558,7 @@ pub fn compile(comptime moduleTables : []const Table, comptime moduleReducers : }; } -pub fn callReducer(comptime mdef: []const Reducer, id: usize, args: anytype) void { +pub fn callReducer(comptime mdef: []const Reducer, id: usize, args: anytype) ReducerError!void { inline for(mdef, 0..) |field, i| { if(id == i) { const func = field.func_type; @@ -392,8 +568,7 @@ pub fn callReducer(comptime mdef: []const Reducer, id: usize, args: anytype) voi } const name: []const u8 = field.name.?; - var buf: [128]u8 = undefined; - print(std.fmt.bufPrint(&buf, "invalid number of args passed to {s}, expected {} got {}", .{name, @typeInfo(func).@"fn".params.len, std.meta.fields(@TypeOf(args)).len}) catch "!!!Error while printing last error!!!"); + std.log.err("invalid number of args passed to {s}, expected {} got {}", .{name, @typeInfo(func).@"fn".params.len, std.meta.fields(@TypeOf(args)).len}); @panic("invalid number of args passed to func"); } } @@ -401,7 +576,7 @@ pub fn callReducer(comptime mdef: []const Reducer, id: usize, args: anytype) voi pub fn PrintModule(data: anytype) void { var buf: [64]u8 = undefined; - print(std.fmt.bufPrint(&buf, "\"{s}\": {{", .{@typeName(@TypeOf(data))}) catch ""); + std.log.debug(std.fmt.bufPrint(&buf, "\"{s}\": {{", .{@typeName(@TypeOf(data))}) catch ""); switch(@TypeOf(data)) { RawModuleDefV9 => { PrintModule(data.typespace); @@ -457,16 +632,16 @@ pub fn PrintModule(data: anytype) void { } }, []const u8 => { - print(std.fmt.bufPrint(&buf, "\"{s}\"", .{data}) catch ""); + std.log.debug(std.fmt.bufPrint(&buf, "\"{s}\"", .{data}) catch ""); }, u32 => { - print(std.fmt.bufPrint(&buf, "{}", .{data}) catch ""); + std.log.debug(std.fmt.bufPrint(&buf, "{}", .{data}) catch ""); }, else => { - print("\"...\""); + std.log.debug("\"...\""); }, } - print("},"); + std.log.debug("},"); } pub const Param = struct { @@ -487,6 +662,11 @@ pub const Table = struct { schema: type, type: TableType = .User, access: TableAccess = .Private, + primary_key: ?[]const u8 = null, + schedule_reducer: ?*const Reducer = null, + indexes: ?[]const []const u8 = null, + unique: ?[]const []const u8 = null, + autoinc: ?[]const []const u8 = null, }; pub const reducers: []const Reducer = blk: { @@ -521,6 +701,11 @@ pub const tables: []const Table = blk: { .access = field.access, .schema = field.schema, .name = field.name orelse decl.name, + .primary_key = field.primary_key, + .schedule_reducer = field.schedule_reducer, + .indexes = field.indexes, + .autoinc = field.autoinc, + .unique = field.unique, } }; } @@ -530,7 +715,7 @@ pub const tables: []const Table = blk: { pub export fn __describe_module__(description: BytesSink) void { const allocator = std.heap.wasm_allocator; - print("Hello from Zig!"); + std.log.debug("Hello from Zig!", .{}); var moduleDefBytes = std.ArrayList(u8).init(allocator); defer moduleDefBytes.deinit(); @@ -543,12 +728,14 @@ pub export fn __describe_module__(description: BytesSink) void { @compileError(fmterr); }; + //PrintModule(compiledModule); + serialize_module(&moduleDefBytes, compiledModule) catch { - print("Allocator Error: Cannot continue!"); + std.log.err("Allocator Error: Cannot continue!", .{}); @panic("Allocator Error: Cannot continue!"); }; - write_to_sink(description, moduleDefBytes.items); + write_to_sink(description, moduleDefBytes.items) catch @panic("Failed to write Module Descripton to SpacetimeDB!"); } pub export fn __call_reducer__( @@ -568,9 +755,9 @@ pub export fn __call_reducer__( const allocator = std.heap.wasm_allocator; var ctx: ReducerContext = .{ - .indentity = std.mem.bytesAsValue(u256, std.mem.sliceAsBytes(&[_]u64{ sender_0, sender_1, sender_2, sender_3})).*, - .timestamp = timestamp, - .connection_id = std.mem.bytesAsValue(u128, std.mem.sliceAsBytes(&[_]u64{ conn_id_0, conn_id_1})).*, + .sender = std.mem.bytesAsValue(Identity, std.mem.sliceAsBytes(&[_]u64{ sender_0, sender_1, sender_2, sender_3})).*, + .timestamp = Timestamp{ .__timestamp_micros_since_unix_epoch__ = @intCast(timestamp), }, + .connection_id = std.mem.bytesAsValue(ConnectionId, std.mem.sliceAsBytes(&[_]u64{ conn_id_0, conn_id_1})).*, .db = .{ .allocator = allocator, }, @@ -623,15 +810,19 @@ pub export fn __call_reducer__( if(args.inner != 0) { inline for(params, 0..) |param, name| { comptime if(name == 0) continue; - @field(constructedArg, utils.itoa(name)) = readArg(allocator, args, zigTypeToSpacetimeType(param.type.?)) catch |err2| { - var buf: [512]u8 = undefined; - print(std.fmt.bufPrint(&buf, "Error: {}", .{err2}) catch "Expand Error Buffer!"); + @field(constructedArg, utils.itoa(name)) = readArg(allocator, args, param.type.?) catch |err2| { + std.log.err("Error: {}", .{err2}); @panic("blah"); }; } } - callReducer(reducers, i, constructedArg); + callReducer(reducers, i, constructedArg) catch |errRet| { + std.log.debug("{s}", .{@errorName(errRet)}); + if (@errorReturnTrace()) |trace| { + std.debug.dumpStackTrace(trace.*); + } + }; } } diff --git a/src/spacetime/serializer.zig b/src/spacetime/serializer.zig index 39aa3df..ff09a31 100644 --- a/src/spacetime/serializer.zig +++ b/src/spacetime/serializer.zig @@ -114,16 +114,42 @@ fn serialize_raw_row_level_security_def_v9(array: *std.ArrayList(u8), val: RawRo unreachable; } +fn serialize_raw_index_algorithm(array: *std.ArrayList(u8), val: RawIndexAlgorithm) !void { + try array.appendSlice(&[_]u8{@intFromEnum(val)}); + switch(val) { + .BTree, .Hash => |deref| { + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(deref.len)))); + try array.appendSlice(std.mem.sliceAsBytes(deref.ptr[0..deref.len])); + }, + .Direct => unreachable, + } +} + fn serialize_raw_index_def_v9(array: *std.ArrayList(u8), val: RawIndexDefV9) !void { - _ = array; - _ = val; - unreachable; + try array.appendSlice(&[_]u8{ @intFromBool(val.name == null) }); + if(val.name) |name| { + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(name.len)))); + try array.appendSlice(name); + } + try array.appendSlice(&[_]u8{ @intFromBool(val.accessor_name == null) }); + if(val.accessor_name) |accessor_name| { + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(accessor_name.len)))); + try array.appendSlice(accessor_name); + } + try serialize_raw_index_algorithm(array, val.algorithm); } fn serialize_raw_constraint_def_v9(array: *std.ArrayList(u8), val: RawConstraintDefV9) !void { - _ = array; - _ = val; - unreachable; + try array.appendSlice(&[_]u8{ @intFromBool(val.name == null) }); + if(val.name) |name| { + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(name.len)))); + try array.appendSlice(name); + } + // I have no idea what union this applies to, could be data or unique + // Both only have 1 option so right now it doesn't matter though. + try array.appendSlice(&.{ 0 }); + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(val.data.unique.Columns.len)))); + try array.appendSlice(std.mem.sliceAsBytes(val.data.unique.Columns.ptr[0..val.data.unique.Columns.len])); } fn serialize_raw_sequence_def_v9(array: *std.ArrayList(u8), val: RawSequenceDefV9) !void { @@ -133,9 +159,15 @@ fn serialize_raw_sequence_def_v9(array: *std.ArrayList(u8), val: RawSequenceDefV } fn serialize_raw_schedule_def_v9(array: *std.ArrayList(u8), val: RawScheduleDefV9) !void { - _ = array; - _ = val; - unreachable; + try array.appendSlice(&[_]u8{ @intFromBool(val.name == null) }); + if(val.name) |name| { + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(name.len)))); + try array.appendSlice(name); + } + try array.appendSlice(&std.mem.toBytes(@as(u32, @intCast(val.reducer_name.len)))); + try array.appendSlice(val.reducer_name); + + try array.appendSlice(&std.mem.toBytes(@as(u16, @intCast(val.scheduled_at_column)))); } fn serialize_table_type(array: *std.ArrayList(u8), val: TableType) !void { @@ -177,20 +209,18 @@ fn serialize_product_type(array: *std.ArrayList(u8), val: ProductType) std.mem.A } fn serialize_algebraic_type(array: *std.ArrayList(u8), val: AlgebraicType) !void { + try array.appendSlice(&[_]u8{@intFromEnum(val)}); switch(val) { AlgebraicType.Product => |product| { - 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)); }, AlgebraicType.Sum => |sum| { - try array.appendSlice(&[_]u8{@intFromEnum(val)}); try serialize_sum_type(array, sum); }, - else => try array.appendSlice(&[_]u8{@intFromEnum(val)}), + else => {}, } } diff --git a/src/spacetime/types.zig b/src/spacetime/types.zig index 44cbc79..f2b288a 100644 --- a/src/spacetime/types.zig +++ b/src/spacetime/types.zig @@ -4,6 +4,9 @@ const spacetime = @import("../spacetime.zig"); const console_log = spacetime.console_log; const TableId = spacetime.TableId; +const SpacetimeValue = spacetime.SpacetimeValue; +const SpacetimeError = spacetime.SpacetimeError; + pub const Str = []const u8; pub const SumTypeVariant = struct { @@ -52,7 +55,7 @@ pub const AlgebraicTypeRef = struct { inner: u32, }; -pub const RawIndexAlgorithm = union { +pub const RawIndexAlgorithm = union(enum) { BTree: []const u16, Hash: []const u16, Direct: u16, @@ -64,11 +67,11 @@ pub const RawIndexDefV9 = struct { algorithm: RawIndexAlgorithm, }; -pub const RawUniqueConstraintDataV9 = union { - Columns: u16, +pub const RawUniqueConstraintDataV9 = union(enum) { + Columns: []const u16, }; -pub const RawConstraintDataV9 = union { +pub const RawConstraintDataV9 = union(enum) { unique: RawUniqueConstraintDataV9, }; @@ -78,18 +81,18 @@ pub const RawConstraintDefV9 = struct { }; pub const RawSequenceDefV9 = struct { - Name: ?Str, - Column: u16, - Start: ?i128, - MinValue: ?i128, - MaxValue: ?i128, - Increment: i128 + name: ?Str, + column: u16, + start: ?i128, + min_value: ?i128, + max_value: ?i128, + increment: i128 }; pub const RawScheduleDefV9 = struct { - Name: ?Str, - ReducerName: Str, - ScheduledAtColumn: u16 + name: ?Str, + reducer_name: Str, + scheduled_at_column: u16 }; pub const TableType = enum { @@ -131,115 +134,105 @@ pub const Lifecycle = enum { }; fn getUnionSize(data: anytype) usize { - //const fields = std.meta.fields(@TypeOf(data)); - var size: usize = 0; - - switch(data) { - inline else => |field| { - switch(@TypeOf(field)) { - []const u8 => { - const val = @field(data, field.name); - size = @max(size, 4 + val.len); - }, - i64, u32, u64, f32 => { - size = @max(size, @sizeOf(field.type)); - }, - else => { - switch(@typeInfo(@TypeOf(field))) { - .@"struct" => { - size = @max(size, getStructSize(field)); - }, - .@"union" => { - size = @max(size, 1 + getUnionSize(field)); - }, - else => @compileError("Unsupported type in getUnionSize"), - } - } - } - } - } - - return size; + return 1 + switch(data) { + inline else => |field| getDataSize(field), + }; } fn getStructSize(data: anytype) usize { - 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; - }, - i64, u32, u64, f32 => { - size += @sizeOf(field.type); - }, - else => blk: { - if(@typeInfo(field.type) == .@"struct") { - size += getStructSize(@field(data, field.name)); - break :blk; - } else if(@typeInfo(field.type) == .@"union") { - size += 1 + getUnionSize(@field(data, field.name)); - break :blk; - } - @compileError("Unsupported type in getStructSize"); - }, - } + inline for(std.meta.fields(@TypeOf(data))) |field| { + size += getDataSize(@field(data, field.name)); } - return size; } -fn getUnionData(data: anytype, mem: []u8) []u8 { - var offset_mem = mem; - switch(data) { - inline else => |field| { - std.mem.bytesAsValue(u8, offset_mem[0..@sizeOf(u8)]).* = @intFromEnum(data); - offset_mem = offset_mem[@sizeOf(u8)..]; - offset_mem = getStructData(field, offset_mem); +fn getDataSize(data: anytype) usize { + return switch(@TypeOf(data)) { + []const u8 => 4 + data.len, + i8, u8, i16, u16, i32, u32, + i64, u64, i128, u128, i256, u256, + f32, f64 => |data_type| @sizeOf(data_type), + else => |data_type| switch(@typeInfo(data_type)) { + .@"struct" => getStructSize(data), + .@"union" => getUnionSize(data), + .@"enum" => @sizeOf(data_type), + .@"enum_literal" => @compileError("enum literals can't be supported without the type info"), + .@"optional" => 1 + getDataSize(data), + else => { + @compileLog(data_type); + @compileError("Unsupported type in getStructSize"); + }, }, - } - return offset_mem; + }; } -fn getStructData(data: anytype, mem: []u8) []u8 { +fn getUnionData(data: anytype, mem: *[]u8) void { + const tag: u8 = @intFromEnum(data); + appendValue(tag, mem); + switch(data) { + inline else => |field| { + appendValue(field, mem); + }, + } +} + +fn getStructData(data: anytype, mem: *[]u8) void { 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 ..]; - }, - i32, i64, u32, u64, f32 => { - const val = @field(data, field.name); - std.mem.bytesAsValue(field.type, offset_mem[0..@sizeOf(field.type)]).* = val; - offset_mem = offset_mem[@sizeOf(field.type)..]; - }, - else => blk: { - if(@typeInfo(field.type) == .@"struct") { - offset_mem = getStructData(@field(data, field.name), offset_mem); - break :blk; - } else if(@typeInfo(field.type) == .@"union") { - offset_mem = getUnionData(@field(data, field.name), offset_mem); - break :blk; + appendValue(@field(data, field.name), mem); + } +} + +fn appendValue(data: anytype, mem: *[]u8) void { + const data_type = @TypeOf(data); + switch(data_type) { + []const u8 => { + std.mem.bytesAsValue(u32, mem.*[0..4]).* = data.len; + std.mem.copyForwards(u8, mem.*[4..], data); + mem.* = mem.*[4 + data.len ..]; + }, + i8, u8, i16, u16, i32, u32, + i64, u64, i128, u128, i256, u256, + f32, f64 => { + std.mem.bytesAsValue(data_type, mem.*[0..@sizeOf(data_type)]).* = data; + mem.* = mem.*[@sizeOf(data_type)..]; + }, + else => blk: { + if(@typeInfo(data_type) == .@"struct") { + getStructData(data, mem); + break :blk; + } else if(@typeInfo(data_type) == .@"union") { + getUnionData(data, mem); + break :blk; + } else if(@typeInfo(data_type) == .@"optional") { + mem.*[0] = @intFromBool(data == null); + mem.* = mem.*[1..]; + if(data != null) { + appendValue(data.?, mem); } - @compileLog(field.type); - @compileError("Unsupported type in StructSerializer"); - }, + break :blk; + } else if(@typeInfo(data_type) == .@"enum") { + std.mem.bytesAsValue(data_type, mem.*[0..@sizeOf(data_type)]).* = data; + mem.* = mem.*[@sizeOf(data_type)..]; + break :blk; + } + @compileLog(data_type); + @compileError("failed to append type!"); } } - return offset_mem; } pub fn StructSerializer(struct_type: type) fn(std.mem.Allocator, struct_type) std.mem.Allocator.Error![]u8 { return struct { pub fn serialize(allocator: std.mem.Allocator, data: struct_type) ![]u8 { - const size: usize = getStructSize(data); + const size: usize = switch(@typeInfo(@TypeOf(data))) { + .@"struct" => getStructSize(data), + else => @compileError("A table schema has to be a struct!"), + }; const mem = try allocator.alloc(u8, size); - _ = getStructData(data, mem); + var offset_mem = mem; + _ = getStructData(data, &offset_mem); return mem; } }.serialize; @@ -265,7 +258,9 @@ pub fn UnionDeserializer(union_type: type) fn(allocator: std.mem.Allocator, *[]c @field(ret.*, field.name) = str; offset_mem = offset_mem[4+len ..]; }, - u32, u64, i32, i64, f32 => { + i8, u8, i16, u16, i32, u32, + i64, u64, i128, u128, i256, u256, + f32, f64 => { @field(ret.*, field.name) = std.mem.bytesAsValue(field.type, offset_mem[0..@sizeOf(field.type)]).*; offset_mem = offset_mem[@sizeOf(field.type)..]; }, @@ -304,7 +299,10 @@ pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[ @field(ret.*, field.name) = str; offset_mem = offset_mem[4+len ..]; }, - u32, u64, i32, i64, f32 => { + i8, u8, i16, u16, i32, u32, + i64, u64, i128, u128, i256, u256, + f32, f64 => { + std.log.debug("field_type: {} (offset_mem.len: {})", .{field.type, offset_mem.len}); @field(ret.*, field.name) = std.mem.bytesAsValue(field.type, offset_mem[0..@sizeOf(field.type)]).*; offset_mem = offset_mem[@sizeOf(field.type)..]; }, @@ -322,11 +320,190 @@ pub fn StructDeserializer(struct_type: type) fn(allocator: std.mem.Allocator, *[ } } data.* = offset_mem; + std.log.debug("StructDeserializer Ended!", .{}); return ret; } }.deserialize; } +pub const BoundVariant = enum(u8) +{ + Inclusive = 0, + Exclusive = 1, + Unbounded = 2, +}; + +noinline fn lineInfo() usize { + return @returnAddress(); +} + +pub fn Iter(struct_type: type) type { + return struct { + allocator: std.mem.Allocator, + handle: spacetime.RowIter, + buffer: [0x20_000]u8 = undefined, + contents: ?[]u8 = null, + last_ret: SpacetimeValue = .OK, + + pub fn next(self: *@This()) spacetime.ReducerError!?*struct_type { + std.log.debug("line: {} (handle: {})", .{358, self.handle}); + var buffer_len: usize = undefined; + //while(true) + //{ + var ret: spacetime.SpacetimeValue = self.last_ret; + if(self.contents == null or self.contents.?.len == 0) { + std.log.debug("line: {} (contents: {any})", .{364, self.contents}); + if(self.handle._inner == spacetime.RowIter.INVALID._inner) { + std.log.debug("line: {}", .{366}); + + self.contents = null; + return null; + } + std.log.debug("line: {}", .{371}); + + buffer_len = self.buffer.len; + std.log.debug("line: {}", .{374}); + ret = try spacetime.retMap(spacetime.row_iter_bsatn_advance(self.handle, @constCast(@ptrCast(&self.buffer)), &buffer_len)); + std.log.debug("ret: {}", .{ret}); + std.log.debug("self.buffer[0..buffer_len]: {any} {any} {any}", .{(&self.buffer).ptr, self.buffer.len, buffer_len}); + self.contents = self.buffer[0..buffer_len]; + std.log.debug("line: {}", .{379}); + + if(ret == .EXHAUSTED) { + std.log.debug("line: {}", .{382}); + self.handle = spacetime.RowIter.INVALID; + } + std.log.debug("line: {}", .{385}); + } + + std.log.debug("{}", .{struct_type}); + return StructDeserializer(struct_type)(self.allocator, &(self.contents.?)); + //} + } + + pub fn one_or_null(self: *@This()) ?*struct_type { + defer self.close(); + return self.next() catch null; + } + + pub fn close(self: *@This()) void { + _ = spacetime.row_iter_bsatn_close(self.handle); + self.handle = spacetime.RowIter.INVALID; + self.contents = null; + } + }; +} + +pub fn Column2ORM(comptime table_name: []const u8, comptime column_name: [:0]const u8) type { + const table = blk: { + for(spacetime.tables) |table| { + if(std.mem.eql(u8, table_name, table.name.?)) { + break :blk table; + } + } + @compileError("Table " ++ table_name ++ " does not exist!"); + }; + const struct_type = table.schema; + const column_type = utils.getMemberDefaultType(struct_type, column_name); + + const wrapped_type = @Type(.{ + .@"struct" = std.builtin.Type.Struct{ + .backing_integer = null, + .decls = &.{}, + .fields = &.{ + std.builtin.Type.StructField{ + .alignment = @alignOf(column_type), + .default_value = null, + .is_comptime = false, + .name = column_name, + .type = column_type, + } + }, + .is_tuple = false, + .layout = .auto, + } + }); + + return struct { + allocator: std.mem.Allocator, + + pub fn filter(self: @This(), val: wrapped_type) !Iter(struct_type) { + const temp_name: []const u8 = table_name ++ "_" ++ column_name ++ "_idx_btree"; + var id = spacetime.IndexId{ ._inner = std.math.maxInt(u32)}; + const err = try spacetime.retMap(spacetime.index_id_from_name(temp_name.ptr, temp_name.len, &id)); + std.log.debug("index_id_from_name({}): {x}", .{err, id._inner}); + + const nVal: struct{ bounds: BoundVariant, val: wrapped_type } = .{ + .bounds = .Inclusive, + .val = val, + }; + + const size: usize = getStructSize(nVal); + const mem = try self.allocator.alloc(u8, size); + var offset_mem = mem; + defer self.allocator.free(mem); + getStructData(nVal, &offset_mem); + + const data = mem[0..size]; + const rstart: []u8 = data[0..]; + const rend: []u8 = data[0..]; + + var rowIter: spacetime.RowIter = undefined; + + _ = try spacetime.retMap(spacetime.datastore_index_scan_range_bsatn( + id, + data.ptr, 0, + spacetime.ColId{ ._inner = 0}, + rstart.ptr, rstart.len, + rend.ptr, rend.len, + &rowIter + )); + + return Iter(struct_type){ + .allocator = self.allocator, + .handle = rowIter, + }; + } + + pub fn find(self: @This(), val: wrapped_type) !?*struct_type { + var iter = try self.filter(val); + return iter.one_or_null(); + } + + pub fn delete(self: @This(), val: wrapped_type) !void { + const temp_name: []const u8 = table_name ++ "_" ++ column_name ++ "_idx_btree"; + var id = spacetime.IndexId{ ._inner = std.math.maxInt(u32)}; + _ = spacetime.index_id_from_name(temp_name.ptr, temp_name.len, &id); + + const nVal: struct{ bounds: BoundVariant, val: wrapped_type } = .{ + .bounds = .Inclusive, + .val = val, + }; + + const size: usize = getStructSize(nVal); + const mem = try self.allocator.alloc(u8, size); + var offset_mem = mem; + defer self.allocator.free(mem); + getStructData(nVal, &offset_mem); + + const data = mem[0..size]; + const rstart: []u8 = data[0..]; + const rend: []u8 = data[0..]; + + var deleted_fields: u32 = undefined; + + _ = spacetime.datastore_delete_by_index_scan_range_bsatn( + id, + data.ptr, 0, + spacetime.ColId{ ._inner = 0}, + rstart.ptr, rstart.len, + rend.ptr, rend.len, + &deleted_fields + ); + } + }; +} + pub fn Table2ORM(comptime table_name: []const u8) type { const table = blk: { for(spacetime.tables) |table| { @@ -340,71 +517,31 @@ pub fn Table2ORM(comptime table_name: []const u8) type { return struct { allocator: std.mem.Allocator, - pub const Iter = struct { - allocator: std.mem.Allocator, - handle: spacetime.RowIter, - buffer: [0x20_000]u8 = undefined, - contents: []u8 = undefined, - last_ret: i16 = spacetime.OK, - - pub fn next(self: *@This()) !?*struct_type { - var buffer_len: usize = undefined; - while(true) - { - var ret = self.last_ret; - if(self.contents.len == 0) { - if(self.handle._inner == spacetime.RowIter.INVALID._inner) { - return null; - } - buffer_len = self.buffer.len; - ret = spacetime.row_iter_bsatn_advance(self.handle, @constCast(@ptrCast(&self.buffer)), &buffer_len); - self.contents = self.buffer[0..buffer_len]; - - if(ret == spacetime.EXHAUSTED) { - self.handle = spacetime.RowIter.INVALID; - } - } - - switch(ret) { - spacetime.EXHAUSTED, spacetime.OK => { - return StructDeserializer(struct_type)(self.allocator, &self.contents); - }, - spacetime.BUFFER_TOO_SMALL => { - return error.BUFFER_TOO_SMALL; - }, - spacetime.NO_SUCH_ITER => { - return error.NO_SUCH_ITER; - }, - else => { - var buffer: [512]u8 = undefined; - const msg = try std.fmt.bufPrint(&buffer, "Iter Err: {}!", .{ ret }); - console_log(2, null, 0, null, 0, 0, msg.ptr, msg.len); - @panic("Fix Me!"); - } - } - } - } - }; - - pub fn insert(self: @This(), data: struct_type) void { + pub fn insert(self: @This(), data: struct_type) !void { var id: TableId = undefined; _ = spacetime.table_id_from_name(table_name.ptr, table_name.len, &id); - const raw_data = StructSerializer(struct_type)(self.allocator, data) catch return; + const raw_data = try StructSerializer(struct_type)(self.allocator, data); defer self.allocator.free(raw_data); var raw_data_len: usize = raw_data.len; _ = spacetime.datastore_insert_bsatn(id, raw_data.ptr, &raw_data_len); } - pub fn iter(self: @This()) Iter { + pub fn iter(self: @This()) Iter(struct_type) { var id: TableId = undefined; _ = spacetime.table_id_from_name(table_name.ptr, table_name.len, &id); var rowIter: spacetime.RowIter = undefined; _ = spacetime.datastore_table_scan_bsatn(id, &rowIter); - return Iter{ + return Iter(struct_type){ .allocator = self.allocator, .handle = rowIter, }; } + + pub fn col(self: @This(), comptime column_name: [:0]const u8) Column2ORM(table_name, column_name) { + return .{ + .allocator = self.allocator, + }; + } }; } @@ -419,9 +556,9 @@ pub const Local = struct { }; pub const ReducerContext = struct { - indentity: u256, - timestamp: u64, - connection_id: u128, + sender: spacetime.Identity, + timestamp: spacetime.Timestamp, + connection_id: spacetime.ConnectionId, db: Local, };