Initial work merging Oar into Ona itself
This commit is contained in:
		
							parent
							
								
									32b18e4ebf
								
							
						
					
					
						commit
						7599ce61f2
					
				| @ -204,6 +204,38 @@ test "Check memory begins with" { | |||||||
|     try testing.expect(!begins(u8, &.{69, 89, 42, 0}, bytes_sequence)); |     try testing.expect(!begins(u8, &.{69, 89, 42, 0}, bytes_sequence)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// | ||||||
|  | /// Returns a sliced reference of the raw bytes in `pointer`. | ||||||
|  | /// | ||||||
|  | /// **Note** that passing a slice will convert it to a byte slice. | ||||||
|  | /// | ||||||
|  | pub fn bytes(pointer: anytype) switch (@typeInfo(@TypeOf(pointer))) { | ||||||
|  |     .Pointer => |info| if (info.is_const) []const u8 else []u8, | ||||||
|  |     else => @compileError("`pointer` must be a pointer type"), | ||||||
|  | } { | ||||||
|  |     const Pointer = @TypeOf(pointer); | ||||||
|  |     const pointer_info = @typeInfo(Pointer).Pointer; | ||||||
|  | 
 | ||||||
|  |     switch (pointer_info.size) { | ||||||
|  |         .Many => @compileError("`pointer` cannot be an unbound pointer type"), | ||||||
|  |         .C => @compileError("`pointer` cannot be a C-style pointer"), | ||||||
|  | 
 | ||||||
|  |         .One => return @ptrCast(if (pointer_info.is_const) [*]const u8 | ||||||
|  |                 else [*]u8, pointer)[0 .. @sizeOf(Pointer)], | ||||||
|  | 
 | ||||||
|  |         .Slice => return @ptrCast(if (pointer_info.is_const) [*]const u8 else | ||||||
|  |             [*]u8, pointer.ptr)[0 .. (@sizeOf(Pointer) * pointer.len)], | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | test "Bytes of types" { | ||||||
|  |     const testing = std.testing; | ||||||
|  | 
 | ||||||
|  |     var foo: u32 = 10; | ||||||
|  | 
 | ||||||
|  |     testing.expectEqual(bytes(&foo), 0x0a); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// | /// | ||||||
| /// Returns `true` if `this` is the same length and contains the same data as `that`, otherwise | /// Returns `true` if `this` is the same length and contains the same data as `that`, otherwise | ||||||
| /// `false`. | /// `false`. | ||||||
|  | |||||||
| @ -5,7 +5,13 @@ const std = @import("std"); | |||||||
| /// | /// | ||||||
| /// | /// | ||||||
| pub const Archive = struct { | pub const Archive = struct { | ||||||
|  |     pub fn deinit(archive: *Archive) { | ||||||
| 
 | 
 | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn init(file_system: *const sys.FileSystem, file_path: sys.Path) { | ||||||
|  | 
 | ||||||
|  |     } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /// | /// | ||||||
|  | |||||||
							
								
								
									
										138
									
								
								src/ona/oar.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/ona/oar.zig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | |||||||
|  | const core = @import("./core.zig"); | ||||||
|  | const sys = @import("./sys.zig"); | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// | ||||||
|  | /// | ||||||
|  | pub const Archive = struct { | ||||||
|  |     file_system: *const sys.FileSystem, | ||||||
|  |     archive_path: sys.Path, | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     const Header = extern struct { | ||||||
|  |         signature: [signature_magic.len]u8, | ||||||
|  |         revision: u8, | ||||||
|  |         entry_offset: u64, | ||||||
|  |         padding: [500]u8 = std.mem.zeroes([500]u8), | ||||||
|  | 
 | ||||||
|  |         /// | ||||||
|  |         /// Magic identifier used to validate [Entry] data. | ||||||
|  |         /// | ||||||
|  |         const signature_magic = [3]u8{'o', 'a', 'r'}; | ||||||
|  | 
 | ||||||
|  |         comptime { | ||||||
|  |             const size = @sizeOf(@This()); | ||||||
|  | 
 | ||||||
|  |             if (size != 512) | ||||||
|  |                 @compileError("Header is " ++ | ||||||
|  |                     std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes"); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// An entry block of an Oar archive file. | ||||||
|  |     /// | ||||||
|  |     /// Typically, following the block in memory is the file data it holds the meta-information for. | ||||||
|  |     /// | ||||||
|  |     const Entry = extern struct { | ||||||
|  |         signature: [signature_magic.len]u8 = signature_magic, | ||||||
|  |         revision: u8, | ||||||
|  |         path: Path, | ||||||
|  |         file_size: u64, | ||||||
|  |         absolute_offset: u64, | ||||||
|  |         padding: [232]u8 = std.mem.zeroes([232]u8), | ||||||
|  | 
 | ||||||
|  |         comptime { | ||||||
|  |             const entry_size = @sizeOf(Entry); | ||||||
|  | 
 | ||||||
|  |             if (entry_size != 512) | ||||||
|  |                 @compileError("Entry is " ++ | ||||||
|  |                     std.fmt.comptimePrint("{d}", .{entry_size}) ++ " bytes"); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     pub const OpenError = error { | ||||||
|  |         FileNotFound, | ||||||
|  |         EntryNotFound, | ||||||
|  |         UnsupportedArchive, | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     /// | ||||||
|  |     pub fn open(archive: Archive, entry_path: Path) OpenError!EntryAccess { | ||||||
|  |         const file_access = try archive.file_system.open(entry_path, .readonly); | ||||||
|  | 
 | ||||||
|  |         errdefer file_access.close(); | ||||||
|  | 
 | ||||||
|  |         var header = std.mem.zeroes(Header); | ||||||
|  |         const header_size = @sizeOf(Header); | ||||||
|  |         const io = core.io; | ||||||
|  | 
 | ||||||
|  |         // Validate header. | ||||||
|  |         if ((try file_access.read(io.bytes(&header)) != header_size) or header | ||||||
|  |             (!core.io.equals(u8, &header.signature, &Header.signature_magic)) or | ||||||
|  |             (header.revision != revision) or | ||||||
|  |             (header.entry_offset <= header_size)) return error.UnsupportedArchive; | ||||||
|  | 
 | ||||||
|  |         // Go to file table. | ||||||
|  |         try file_access.seek(header.entry_offset); | ||||||
|  | 
 | ||||||
|  |         // Read file table. | ||||||
|  |         var entry_buffer = std.mem.zeroes([8]Entry); | ||||||
|  |         var bytes_read = try file_access.read(io.bytes(&entry_buffer)); | ||||||
|  | 
 | ||||||
|  |         while (@mod(bytes_read, @sizeOf(Entry)) == 0) { | ||||||
|  |             for (entry_buffer[0 .. (bytes_read / @sizeOf(Entry))]) |entry| { | ||||||
|  |                 if (entry.path.equals(entry_path)) { | ||||||
|  |                     file_access.seek(entry.file_offset); | ||||||
|  | 
 | ||||||
|  |                     return Entry{ | ||||||
|  |                         .file_access = file_access, | ||||||
|  |                     }; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bytes_read = try file_access.read(io.bytes(&entry_buffer)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         var head = std.mem.zeroes(usize); | ||||||
|  |         var tail = (header.file_count - 1); | ||||||
|  | 
 | ||||||
|  |         while (head <= tail) { | ||||||
|  |             const midpoint = (head + (tail - head) / 2); | ||||||
|  | 
 | ||||||
|  |             const comparison = entry_path.compare(arr[m])); | ||||||
|  | 
 | ||||||
|  |             if (comparison == 0) return midpoint; | ||||||
|  | 
 | ||||||
|  |             if (comparison > 0) { | ||||||
|  |                 // If x greater, ignore left half | ||||||
|  |                 head = (midpoint + 1); | ||||||
|  |             } else { | ||||||
|  |                 // If x is smaller, ignore right half | ||||||
|  |                 tail = (midpoint - 1); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return error.EntryNotFound; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | pub const EntryAccess = struct { | ||||||
|  |     file_access: FileAccess, | ||||||
|  | 
 | ||||||
|  |     pub fn close(entry: Entry) void { | ||||||
|  |         entry.file_access.close(); | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /// | ||||||
|  | /// | ||||||
|  | /// | ||||||
|  | const revision = 0; | ||||||
| @ -100,37 +100,6 @@ pub const FileSystem = union(enum) { | |||||||
|     native: []const u8, |     native: []const u8, | ||||||
|     archive: Archive, |     archive: Archive, | ||||||
| 
 | 
 | ||||||
|     /// |  | ||||||
|     /// Archive file system information. |  | ||||||
|     /// |  | ||||||
|     const Archive = struct { |  | ||||||
|         file_access: core.io.FileAccess, |  | ||||||
|         index_cache: IndexCache, |  | ||||||
|         entry_table: [max_open_entries]Entry = std.mem.zeroes([max_open_entries]Entry), |  | ||||||
| 
 |  | ||||||
|         /// |  | ||||||
|         /// Hard limit on the maximum number of entries open at once. |  | ||||||
|         /// |  | ||||||
|         const max_open_entries = 16; |  | ||||||
| 
 |  | ||||||
|         /// |  | ||||||
|         /// Stateful extension of an [oar.Entry]. |  | ||||||
|         /// |  | ||||||
|         const Entry = struct { |  | ||||||
|             owner: ?*core.io.FileAccess, |  | ||||||
|             cursor: u64, |  | ||||||
|             header: oar.Entry, |  | ||||||
|         }; |  | ||||||
| 
 |  | ||||||
|         /// |  | ||||||
|         /// Table cache for associating [oar.Path] values with offsets to entries in a given file. |  | ||||||
|         /// |  | ||||||
|         const IndexCache = core.table.Hashed(oar.Path, u64, .{ |  | ||||||
|             .equals = oar.Path.equals, |  | ||||||
|             .hash = oar.Path.hash, |  | ||||||
|         }); |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     /// |     /// | ||||||
|     /// With files typically being backed by a block device, they can produce a variety of errors - |     /// With files typically being backed by a block device, they can produce a variety of errors - | ||||||
|     /// from physical to virtual errors - these are all encapsulated by the API as general |     /// from physical to virtual errors - these are all encapsulated by the API as general | ||||||
| @ -172,7 +141,9 @@ pub const FileSystem = union(enum) { | |||||||
|     /// Returns a [FileAccess] reference that provides access to the file referenced by `path`or a |     /// Returns a [FileAccess] reference that provides access to the file referenced by `path`or a | ||||||
|     /// [OpenError] if it failed. |     /// [OpenError] if it failed. | ||||||
|     /// |     /// | ||||||
|     pub fn open(file_system: *FileSystem, path: Path, mode: OpenMode) OpenError!core.io.FileAccess { |     pub fn open(file_system: *const FileSystem, path: Path, | ||||||
|  |         mode: OpenMode) OpenError!core.io.FileAccess { | ||||||
|  | 
 | ||||||
|         switch (file_system.*) { |         switch (file_system.*) { | ||||||
|             .archive => |*archive| { |             .archive => |*archive| { | ||||||
|                 if (mode != .readonly) return error.ModeUnsupported; |                 if (mode != .readonly) return error.ModeUnsupported; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user