-
Notifications
You must be signed in to change notification settings - Fork 5
/
struct_wrapper.mojo
146 lines (121 loc) · 4.5 KB
/
struct_wrapper.mojo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from sys.ffi import external_call
from testing import assert_raises
alias c_char = UInt8
alias c_int = Int32
alias c_long = UInt64
alias c_void = UInt8
alias c_size_t = Int
alias SEEK_SET = 0
alias SEEK_END = 2
@register_passable("trivial")
struct FILE:
...
struct FileHandle:
var handle: Pointer[FILE]
fn __init__(inout self, path: String, mode: String) raises:
var path_ptr = self._as_char_ptr(path)
var mode_ptr = self._as_char_ptr(mode)
# https://man7.org/linux/man-pages/man3/fopen.3.html
var handle = external_call["fopen", Pointer[FILE]](
path_ptr, mode_ptr
)
mode_ptr.free()
path_ptr.free()
if handle == Pointer[FILE]():
raise Error("Error opening file")
self.handle = handle
@staticmethod
fn _as_char_ptr(s: String) -> Pointer[c_char]:
var nelem = len(s)
var ptr = Pointer[c_char]().alloc(nelem + 1) # +1 for null termination
for i in range(len(s)):
ptr.store(i, ord(s[i]))
ptr.store(nelem, 0) # null-terminate the string
return ptr
fn fclose(self) raises:
"""Safe and idiomatic wrapper https://man7.org/linux/man-pages/man3/fclose.3.html."""
debug_assert(self.handle != Pointer[FILE](), "File must be opened first")
var ret = external_call["fclose", c_int, Pointer[FILE]](self.handle)
if ret:
raise Error("Error in closing the file")
return
fn fseek(self, offset: UInt64 = 0, whence: Int32 = SEEK_END) raises:
"""Safe and idiomatic wrapper https://man7.org/linux/man-pages/man3/fseek.3.html."""
debug_assert(self.handle != Pointer[FILE](), "File must be opened first")
var ret = external_call["fseek", c_int, Pointer[FILE], c_long, c_int](
self.handle, offset, whence
)
if ret:
self.fclose()
raise Error("Error seeking in file")
return
fn ftell(self) raises -> UInt64:
"""Safe and idiomatic wrapper https://man7.org/linux/man-pages/man3/ftell.3p.html."""
debug_assert(self.handle != Pointer[FILE](), "File must be opened")
var ret = external_call["ftell", c_long, Pointer[FILE]](self.handle)
if ret == -1:
self.fclose()
raise Error("ftell failed")
return ret
@staticmethod
fn _fread(
ptr: Pointer[c_void],
size: c_size_t,
nitems: c_size_t,
stream: Pointer[FILE],
) -> c_int:
return external_call[
"fread",
c_size_t,
Pointer[c_void],
c_size_t,
c_size_t,
Pointer[FILE],
](ptr, size, nitems, stream)
fn fread(self, buf_read_size: Int = 1024) raises -> String:
"""Safe and idiomatic wrapper https://man7.org/linux/man-pages/man3/fread.3p.html."""
debug_assert(self.handle != Pointer[FILE](), "File must be opened first")
# Choosing a large buffer for the sake of example.
# Exercise: Implement `read_file_to_end` in case
# the size is greater than the buffer size
var buf = Pointer[c_char]().alloc(buf_read_size + 1) # +1 for null termination
var count = self._fread(buf.bitcast[c_void](), 1, buf_read_size, self.handle)
if count <= 0:
if count < 0:
self.fclose()
raise Error("Cannot read data")
else:
print("End of file reached")
buf.store(count, 0) # null-terminate
# String owns the ptr so not need to call `free`
# String len is Int and count is Int32
return String(buf.bitcast[Int8](), int(count) + 1) # +1 include null-termintor
fn fopen(path: String, mode: String = "r") raises -> FileHandle:
return FileHandle(path, mode)
def main():
try:
file = fopen("test.txt")
file.fseek(0)
size = file.ftell()
print("file size in bytes:", size) # 36 bytes
file.fseek(whence=SEEK_SET)
print(file.fread())
file.fclose()
except:
print("Error has occured")
# test double close
with assert_raises():
file = fopen("test.txt")
file.fclose()
file.fclose()
with assert_raises():
# test notexist
_ = fopen("notexist.txt")
with assert_raises():
# test fseek and ftell fail cases (and multi close)
file = fopen("test.txt")
file.fseek(-100)
_ = file.ftell()
file.fseek(whence=SEEK_SET)
_ = file.fread()
file.fclose()