Compare commits
	
		
			10 Commits
		
	
	
		
			71ab200325
			...
			3a0da39b3e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 3a0da39b3e | ||
| 37bd0f10ac | |||
| c91630d6da | |||
| 074a62807b | |||
| c4775e27d8 | |||
| a36b3bc321 | |||
| e01b19cd68 | |||
| 774536fd58 | |||
| af1976b69c | |||
| 49557f32bc | 
| @ -5,4 +5,5 @@ steps: | ||||
| - name: build & test | ||||
|   image: euantorano/zig:0.9.1 | ||||
|   commands: | ||||
|   - zig test src/main.zig | ||||
|   - zig build test | ||||
|   - $(find zig-cache -name test) main.zig | ||||
|  | ||||
							
								
								
									
										2
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.vscode/tasks.json
									
									
									
									
										vendored
									
									
								
							| @ -44,7 +44,7 @@ | ||||
| 		{ | ||||
| 			"label": "Test", | ||||
| 			"type": "shell", | ||||
| 			"command": "zig test src/main.zig", | ||||
| 			"command": "$(find zig-cache -name test) src/main.zig", | ||||
| 			"group": { | ||||
| 				"kind": "test", | ||||
| 				"isDefault": true | ||||
|  | ||||
							
								
								
									
										31
									
								
								src/io.zig
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/io.zig
									
									
									
									
									
								
							| @ -10,7 +10,7 @@ pub const Writer = struct { | ||||
|     writeContext: fn (*anyopaque, []const u8) usize, | ||||
| 
 | ||||
|     /// | ||||
|     /// | ||||
|     /// Radices supported by [writeInt]. | ||||
|     /// | ||||
|     pub const Radix = enum { | ||||
|         binary, | ||||
| @ -79,8 +79,8 @@ pub const Writer = struct { | ||||
|         const Int = @TypeOf(value); | ||||
|         const type_info = @typeInfo(Int); | ||||
| 
 | ||||
|         if (type_info != .Int) @compileError("value must be of type int"); | ||||
| 
 | ||||
|         switch (type_info) { | ||||
|             .Int => { | ||||
|                 if (value == 0) return writer.writeByte('0'); | ||||
| 
 | ||||
|                 // TODO: Unhardcode this as it will break with large ints. | ||||
| @ -107,11 +107,17 @@ pub const Writer = struct { | ||||
|                     std.mem.swap(u8, &buffer[i], &buffer[buffer_count - i - 1]); | ||||
| 
 | ||||
|                 return (writer.write(buffer[0 .. buffer_count]) == buffer_count); | ||||
|             }, | ||||
| 
 | ||||
|             // Cast comptime int into known-size integer and try again. | ||||
|             .ComptimeInt => return writer. | ||||
|                 writeInt(radix, @intCast(std.math.IntFittingRange(value, value), value)), | ||||
| 
 | ||||
|             else => @compileError("value must be of type int"), | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| var null_context = @as(usize, 0); | ||||
| 
 | ||||
| /// | ||||
| /// Writer that silently throws consumed data away and never fails. | ||||
| /// | ||||
| @ -119,13 +125,10 @@ var null_context = @as(usize, 0); | ||||
| /// sent somewhere for whatever reason. | ||||
| /// | ||||
| pub const null_writer = Writer{ | ||||
|     .context = (&null_context), | ||||
| 
 | ||||
|     .operation = struct { | ||||
|         fn write(context: *anyopaque, buffer: []const u8) usize { | ||||
|             // Validate context canary value. | ||||
|             std.debug.assert(@ptrCast(*usize, @alignCast(@alignOf(usize), context)).* == 0); | ||||
|     .context = undefined, | ||||
| 
 | ||||
|     .writeContext = struct { | ||||
|         fn write(_: *anyopaque, buffer: []const u8) usize { | ||||
|             return buffer.len; | ||||
|         } | ||||
|     }.write, | ||||
| @ -137,9 +140,9 @@ test { | ||||
|     { | ||||
|         const sequence = "foo"; | ||||
| 
 | ||||
|         testing.expectEqual(null_writer.write(sequence), sequence.len); | ||||
|         try testing.expectEqual(null_writer.write(sequence), sequence.len); | ||||
|     } | ||||
| 
 | ||||
|     testing.expect(null_writer.writeByte(0)); | ||||
|     testing.expect(null_writer.writeInt(.decimal, 420)); | ||||
|     try testing.expect(null_writer.writeByte(0)); | ||||
|     try testing.expect(null_writer.writeInt(.decimal, 420)); | ||||
| } | ||||
|  | ||||
| @ -15,7 +15,10 @@ pub fn main() anyerror!void { | ||||
| } | ||||
| 
 | ||||
| test { | ||||
| 
 | ||||
|     _ = io; | ||||
|     _ = stack; | ||||
|     _ = std; | ||||
|     _ = sys; | ||||
| } | ||||
| 
 | ||||
| fn run(event_loop: *sys.EventLoop, graphics: *sys.GraphicsContext) anyerror!void { | ||||
| @ -38,7 +41,7 @@ fn run(event_loop: *sys.EventLoop, graphics: *sys.GraphicsContext) anyerror!void | ||||
|         if ((try event_loop.readFile(file_access, buffer)) != file_size) | ||||
|             return error.ScriptLoadFailure; | ||||
| 
 | ||||
|         event_loop.log(buffer); | ||||
|         event_loop.log(.debug, buffer); | ||||
|     } | ||||
| 
 | ||||
|     while (graphics.poll()) |_| { | ||||
|  | ||||
| @ -91,7 +91,7 @@ pub const FixedPushError = error { | ||||
|     Overflow, | ||||
| }; | ||||
| 
 | ||||
| test "fixed stack" { | ||||
| test { | ||||
|     const testing = std.testing; | ||||
|     var buffer = std.mem.zeroes([4]u8); | ||||
|     var stack = Fixed(u8){.buffer = &buffer}; | ||||
|  | ||||
							
								
								
									
										96
									
								
								src/sys.zig
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								src/sys.zig
									
									
									
									
									
								
							| @ -8,7 +8,7 @@ const stack = @import("./stack.zig"); | ||||
| const std = @import("std"); | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// A thread-safe platform abstraction over multiplexing system I/O processing and event handling. | ||||
| /// | ||||
| pub const EventLoop = opaque { | ||||
|     /// | ||||
| @ -23,7 +23,7 @@ pub const EventLoop = opaque { | ||||
|             exit, | ||||
| 
 | ||||
|             close: struct { | ||||
|                 file_system_path: *FileAccess, | ||||
|                 file_access: *FileAccess, | ||||
|             }, | ||||
| 
 | ||||
|             log: struct { | ||||
| @ -33,7 +33,7 @@ pub const EventLoop = opaque { | ||||
| 
 | ||||
|             open: struct { | ||||
|                 mode: OpenMode, | ||||
|                 path: *const FileSystem.Path, | ||||
|                 file_system_path: *const FileSystem.Path, | ||||
|                 result: OpenError!*FileAccess = error.NotFound, | ||||
|             }, | ||||
| 
 | ||||
| @ -138,13 +138,15 @@ pub const EventLoop = opaque { | ||||
|     /// | ||||
|     /// Closes access to the file referenced by `file_access` via `event_loop`. | ||||
|     /// | ||||
|     /// *Note* that nothing happens to `file_access` if it is already closed. | ||||
|     /// | ||||
|     pub fn close(event_loop: *EventLoop, file_access: *FileAccess) void { | ||||
|         var message = FileSystemMessage{ | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
|             .request = .{.close = .{.file_access = file_access}}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -174,7 +176,7 @@ pub const EventLoop = opaque { | ||||
|     /// *Note* that `message` is not guaranteed to be partly, wholely, or at all written. | ||||
|     /// | ||||
|     pub fn log(event_loop: *EventLoop, kind: LogKind, message: []const u8) void { | ||||
|         var message = FileSystemMessage{ | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .request = .{.log = .{ | ||||
| @ -183,7 +185,7 @@ pub const EventLoop = opaque { | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
| @ -199,24 +201,26 @@ pub const EventLoop = opaque { | ||||
|     pub fn open(event_loop: *EventLoop, mode: OpenMode, | ||||
|         file_system_path: FileSystem.Path) OpenError!*FileAccess { | ||||
| 
 | ||||
|         var message = FileSystemMessage{ | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .request = .{ | ||||
|                 .open = .{ | ||||
|             .request = .{.open = .{ | ||||
|                 .mode = mode, | ||||
|                 .file_system_path = &file_system_path, | ||||
|                 }, | ||||
|             }, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
| 
 | ||||
|         return message.request.open.result; | ||||
|         return file_system_message.request.open.result; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// [FileSystemMessage] processing function used by a dedicated worker thread, where `data` is | ||||
|     /// a type-erased reference to a [EventLoop]. | ||||
|     /// | ||||
|     /// The processor returns `0` if it exited normally or any other value if an erroneous exit | ||||
|     /// occured. | ||||
|     /// | ||||
|     fn processFileSystemMessages(data: ?*anyopaque) callconv(.C) c_int { | ||||
|         const implementation = Implementation.cast(@ptrCast(*EventLoop, data orelse unreachable)); | ||||
| @ -266,20 +270,23 @@ pub const EventLoop = opaque { | ||||
|                     }, | ||||
| 
 | ||||
|                     .close => |*close_request| { | ||||
|                         // Don't care if this doesn't work. | ||||
|                         // TODO: Use this result somehow. | ||||
|                         _ = ext.SDL_RWclose(@ptrCast(*ext.SDL_RWops, @alignCast( | ||||
|                                 @alignOf(ext.SDL_RWops), close_request.file_access))); | ||||
|                     }, | ||||
| 
 | ||||
|                     .read_file => |read_request| { | ||||
|                         // TODO: Implement. | ||||
|                         _ = read_request; | ||||
|                     }, | ||||
| 
 | ||||
|                     .seek_file => |seek_request| { | ||||
|                         // TODO: Implement. | ||||
|                         _ = seek_request; | ||||
|                     }, | ||||
| 
 | ||||
|                     .tell_file => |tell_request| { | ||||
|                         // TODO: Implement. | ||||
|                         _ = tell_request; | ||||
|                     }, | ||||
|                 } | ||||
| @ -304,29 +311,45 @@ pub const EventLoop = opaque { | ||||
|     pub fn readFile(event_loop: *EventLoop, file_access: *FileAccess, | ||||
|         buffer: []const u8) FileError!usize { | ||||
| 
 | ||||
|         var message = FileSystemMessage{ | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .request = .{ | ||||
|                 .read_file = .{ | ||||
|             .request = .{.read_file = .{ | ||||
|                 .file_access = file_access, | ||||
|                 .buffer = buffer, | ||||
|                 }, | ||||
|             }, | ||||
|             }}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
| 
 | ||||
|         return message.request.read_file.result; | ||||
|         return file_system_message.request.read_file.result; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to tell the current file cursor position for the file referenced by `file_access`. | ||||
|     /// | ||||
|     /// Returns the number of bytes into the file that the cursor is relative to its beginning or a | ||||
|     /// [FileError] if the file failed to be queried. | ||||
|     /// | ||||
|     pub fn queryFile(event_loop: *EventLoop, file_access: *FileAccess) FileError!usize { | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
|             .request = .{.tell_file = .{.file_access = file_access}}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
| 
 | ||||
|         return file_system_message.request.tell_file.result; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// Attempts to seek the file cursor through the file referenced by `file_access` from `origin` | ||||
|     /// to `offset` via `event_loop`, returning a [FileError] if the file failed to be sought. | ||||
|     /// | ||||
|     pub fn seekFile(event_loop: *EventLoop, file_access: *FileAccess, | ||||
|         origin: SeekOrigin, offset: usize) FileError!void { | ||||
| 
 | ||||
|         var message = FileSystemMessage{ | ||||
|         var file_system_message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
| 
 | ||||
|             .request = .{ | ||||
| @ -338,23 +361,9 @@ pub const EventLoop = opaque { | ||||
|             }, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
|         suspend event_loop.enqueueFileSystemMessage(&file_system_message); | ||||
| 
 | ||||
|         return message.request.seek_file.result; | ||||
|     } | ||||
| 
 | ||||
|     /// | ||||
|     /// | ||||
|     /// | ||||
|     pub fn tellFile(event_loop: *EventLoop, file_access: *FileAccess) FileError!usize { | ||||
|         var message = FileSystemMessage{ | ||||
|             .frame = @frame(), | ||||
|             .request = .{.tell_file = .{.file_access = file_access}}, | ||||
|         }; | ||||
| 
 | ||||
|         suspend event_loop.enqueueFileSystemMessage(&message); | ||||
| 
 | ||||
|         return message.request.tell_file.result; | ||||
|         return file_system_message.request.seek_file.result; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| @ -368,11 +377,11 @@ pub const FileAccess = opaque { | ||||
|     /// | ||||
|     pub fn size(file_access: *FileAccess, event_loop: *EventLoop) FileError!usize { | ||||
|         // Save cursor to return to it later. | ||||
|         const origin_cursor = try event_loop.tellFile(file_access); | ||||
|         const origin_cursor = try event_loop.queryFile(file_access); | ||||
| 
 | ||||
|         try event_loop.seekFile(file_access, .tail, 0); | ||||
| 
 | ||||
|         const ending_cursor = try event_loop.tellFile(file_access); | ||||
|         const ending_cursor = try event_loop.queryFile(file_access); | ||||
| 
 | ||||
|         // Return to original cursor. | ||||
|         try event_loop.seekFile(file_access, .head, origin_cursor); | ||||
| @ -391,14 +400,15 @@ pub const FileError = error { | ||||
| }; | ||||
| 
 | ||||
| /// | ||||
| /// | ||||
| /// Platform-agnostic mechanism for working with an abstraction of the underlying file-system(s) | ||||
| /// available to the application in a sandboxed environment. | ||||
| /// | ||||
| pub const FileSystem = enum { | ||||
|     data, | ||||
|     user, | ||||
| 
 | ||||
|     /// | ||||
|     /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem] | ||||
|     /// Platform-agnostic mechanism for referencing files and directories on a [FileSystem]. | ||||
|     /// | ||||
|     pub const Path = struct { | ||||
|         file_system: FileSystem, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user