Skip to content

Instantly share code, notes, and snippets.

@likern
Created February 10, 2022 09:18
Show Gist options
  • Save likern/b42bfe2930ddff0712f60698e4c9c6d1 to your computer and use it in GitHub Desktop.
Save likern/b42bfe2930ddff0712f60698e4c9c6d1 to your computer and use it in GitHub Desktop.
const std = @import("std");
const Allocator = std.mem.Allocator;
const expect = std.testing.expect;
//const utils = @import("utils");
const IOFileDescriptor = error{ NoValidFileDescriptor, NoValidFilePath };
const IOOffsetOverflow = error{Overflow};
const IOError = IOFileDescriptor || IOOffsetOverflow;
pub const FileOpenError = std.os.OpenError || std.mem.Allocator.Error;
pub const FileOpenOrCreate = FileOpenError;
pub const FileCloseError = IOFileDescriptor;
pub const FileReadError = IOError || error{EOF} || std.os.ReadError;
pub const FileWriteError = IOError || std.os.PWriteError;
pub const FileStatError = IOFileDescriptor || std.os.FStatError;
pub const FileUnlinkError = IOFileDescriptor || std.os.UnlinkError;
pub const FileStat = std.os.Stat;
pub const String = []const u8;
pub const OpenOptions = struct { path: String };
pub const File = struct {
const Self = @This();
fd: ?std.os.fd_t,
__path: ?[]const u8,
__allocator: Allocator,
pub fn open(path: []const u8, allocator: Allocator) FileOpenError!File {
// Allocator memory and copy over path into owned field.
const copied_path = try allocator.alloc(u8, path.len);
std.mem.copy(u8, copied_path, path);
// open or create file with read / write permission
// use direct IO (no buffering allowed)
const flags = std.os.O.DIRECT | std.os.O.RDWR;
const fd = try std.os.open(path, flags, 0777);
return File{ .fd = fd, .__path = copied_path, .__allocator = allocator };
}
pub fn openOrCreate(path: []const u8, allocator: Allocator) FileOpenOrCreate!File {
// Allocator memory and copy over path into owned field.
const copied_path = try allocator.alloc(u8, path.len);
std.mem.copy(u8, copied_path, path);
// open or create file with read / write permission
// use direct IO (no buffering allowed)
const flags = std.os.O.DIRECT | std.os.O.RDWR | std.os.O.CREAT;
// const mode = std.os.S.IRUSR | std.os.S.IWUSR;
const mode = std.os.S.IRUSR | std.os.S.IWUSR | std.os.S.IRGRP | std.os.S.IWGRP | std.os.S.IROTH | std.os.S.IWOTH;
const fd = try std.os.open(path, flags, mode);
std.debug.print("openOrCreate: fd [{x}] [{d}]\n", .{ fd, fd });
return File{ .fd = fd, .__path = copied_path, .__allocator = allocator };
}
pub fn close(self: *Self) FileCloseError!void {
if (self.fd) |fd| {
std.os.close(fd);
self.fd = null;
if (self.__path) |path| {
// Free memory allocated for .__path and set it to null
self.__allocator.free(path);
self.__path = null;
return;
} else {
return FileCloseError.NoValidFilePath;
}
} else {
return FileCloseError.NoValidFileDescriptor;
}
}
pub fn unlink(self: *Self) FileUnlinkError!void {
if (self.fd) |_| {
try std.os.unlink(self.__path.?);
self.fd = null;
if (self.__path) |path| {
// Free memory allocated for .__path and set it to null
self.__allocator.free(path);
self.__path = null;
return;
} else {
return FileCloseError.NoValidFilePath;
}
} else {
return FileUnlinkError.NoValidFileDescriptor;
}
}
/// Read bytes at offset of file start into buffer buf.
/// If error happens, buf might be partially overwritten.
/// So don't rely on untouched buf in case of error.
pub fn read(self: *Self, buf: []u8, offset: u64) FileReadError!void {
var slice = buf[0..];
var readOffset = offset;
if (self.fd) |fd| {
while (true) {
const length = try std.os.pread(fd, slice, readOffset);
if (length == slice.len) {
// We successfully have read all required bytes.
return;
} else if (length == 0) {
// No more to read, reached EOF (End-Of-File).
// We partially filled provided buffer buf
// and consider this as an error.
return FileReadError.EOF;
} else if (length < slice.len) {
// We successfully have read length bytes,
// but it's less than we requested (buf has more space).
// Continue reading from file.
// Before adjust slice, shrinking it.
slice = slice[length..];
readOffset = try std.math.add(readOffset, length);
}
}
} else {
return FileReadError.NoValidFileDescriptor;
}
}
/// Write buf into file starting at offset of file start.
pub fn write(self: *Self, buf: []const u8, offset: u64) FileWriteError!void {
var slice = buf[0..];
var writeOffset: u64 = offset;
if (self.fd) |fd| {
while (true) {
//const fd_t = @TypeOf(fd);
//@compileLog("fd type is {}\n", .{fd_t});
std.debug.print("write bytes [{any}] of len [{d}] to file [{d}] at offset [{d}]\n", .{ slice, slice.len, fd, writeOffset });
const length = try std.os.pwrite(fd, slice, writeOffset);
if (length == slice.len) {
// No more to write, successfully have written all buffer data
return;
} else if (length < slice.len) {
// We successfully have written length bytes,
// but it's less than we requested (buf has more data).
// Continue writing to file.
// Before adjust slice, shrinking it.
slice = slice[length..];
writeOffset = try std.math.add(u64, writeOffset, length);
}
}
} else {
return FileWriteError.NoValidFileDescriptor;
}
}
pub fn stat(self: *Self) FileStatError!FileStat {
if (self.fd) |fd| {
return std.os.fstat(fd);
} else {
return FileStatError.NoValidFileDescriptor;
}
}
};
// test "create and delete file" {
// const allocator = std.testing.allocator;
// var file = try File.openOrCreate("some_cool_file.zdb", allocator);
// try expect(file.fd != null);
// try expect(file.__path != null);
// try file.unlink();
// }
test "write and read from file" {
const allocator = std.testing.allocator;
var file = try File.openOrCreate("write_and_read.zdb", allocator);
errdefer file.close() catch unreachable;
const buffer = [_]u8{ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 };
const offset = 0;
try file.write(&buffer, offset);
// std.fmt.bufPrint();
// const file_name = try utils.string.generateFileName(16, ".zdb");
//expect(file_name.len == 16);
// const file = File.open()
}
// test "can create new file" {
// try utils.Utils.generateFileName(16, null);
// }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment