1--- Caller-supplied in-memory file slot holding its path, readable bytes and
2--- writable capacity. Keep all referenced ranges alive and initialize the
3--- fields before constructing the provider.
4---
5--- Finite caller-backed provider of every io concept. Handles are indexes,
6--- not ownership tokens.
7--- Clients preserve length <= capacity and supply all retained backing.
8public memory_file: type = struct
9 name: []u8
10 contents: []mut u8
11 length: usize
12 cursor: usize
13 readable: bool
14 writable: bool
15 opened: bool
16 writing: bool
17end memory_file
18
19--- In-memory world using caller-owned file slots, output buffers and argument
20--- records. No host handles or implicit allocator are used.
21public memory: type = struct
22 files: []mut memory_file
23 arguments: []argument
24 output: []mut u8
25 errors: []mut u8
26 output_used: usize
27 errors_used: usize
28 output_open: bool
29 errors_open: bool
30 read_limit: usize
31 write_limit: usize
32 fail_read_at: usize
33 fail_write_at: usize
34 fail_close_at: usize
35 read_calls: usize
36 write_calls: usize
37 close_calls: usize
38 open_calls: usize
39end memory
40
41--- Create an in-memory world over the supplied records and buffers. Retains
42--- their references; keep them alive and avoid conflicting access for the
43--- world's lifetime.
44public memory_over: (files: []mut memory_file,
45 arguments: []argument,
46 output: []mut u8, errors: []mut u8)
47 -> (state: memory from files, arguments, output, errors) =
48 state = (files: files, arguments: arguments, output: output, errors: errors,
49 output_used: 0, errors_used: 0,
50 output_open: true, errors_open: true,
51 read_limit: 0 -% 1, write_limit: 0 -% 1,
52 fail_read_at: 0, fail_write_at: 0, fail_close_at: 0,
53 read_calls: 0, write_calls: 0, close_calls: 0, open_calls: 0)
54end memory_over
55
56memory_name_matches: (path: ptr u8, name: []u8) -> (yes: bool) =
57 yes = false
58 mut at: usize = 0
59 while at < lenof name do
60 current: ptr u8 = ptr(usize(path) + at)
61 return when current.val == 0 or current.val <> name[at]
62 inc at
63 end while
64 final: ptr u8 = ptr(usize(path) + at)
65 yes = final.val == 0
66end memory_name_matches
67
68memory_open: (self: ptr mut memory, path: ptr u8, writing: bool)
69 -> (opened: file) ! not_found | no_access | io_failed =
70 inc self.val.open_calls
71 mut at: usize = 0
72 while at < memory_file_count(self.val.files) do
73 current: ptr mut memory_file = addr self.val.files[at]
74 if memory_name_matches(path, current.val.name) then
75 fail no_access when writing and not current.val.writable
76 fail no_access when not writing and not current.val.readable
77 fail io_failed when current.val.opened
78 or current.val.length > memory_bytes_count(current.val.contents)
79 or at > 2147483644
80 current.val.cursor = 0
81 current.val.opened = true
82 current.val.writing = writing
83 if writing then current.val.length = 0 end if
84 opened = file_value(i32(at) + 3)
85 return
86 end if
87 inc at
88 end while
89 fail not_found
90end memory_open
91
92memory_open_read: (self: ptr mut memory, path: ptr u8)
93 -> (opened: file) ! not_found | no_access | io_failed =
94 opened = try memory_open(self, path, false)
95end memory_open_read
96
97memory_open_write: (self: ptr mut memory, path: ptr u8)
98 -> (opened: file) ! not_found | no_access | io_failed =
99 opened = try memory_open(self, path, true)
100end memory_open_write
101
102memory_same_file: (self: ptr mut memory, left: ptr u8, right: ptr u8)
103 -> (same: bool) ! io_failed =
104 same = false
105 mut left_index: usize = memory_file_count(self.val.files)
106 mut right_index: usize = memory_file_count(self.val.files)
107 for at in usize(0)..<memory_file_count(self.val.files) do
108 if left_index == memory_file_count(self.val.files)
109 and memory_name_matches(left, self.val.files[at].name) then
110 left_index = at
111 end if
112 if right_index == memory_file_count(self.val.files)
113 and memory_name_matches(right, self.val.files[at].name) then
114 right_index = at
115 end if
116 end for
117 fail io_failed when left_index == memory_file_count(self.val.files)
118 same = left_index == right_index
119end memory_same_file
120
121memory_close: (self: ptr mut memory, sink opened: file) -> none ! io_failed =
122 inc self.val.close_calls
123 if i32(opened) == 1 then
124 fail io_failed when not self.val.output_open
125 self.val.output_open = false
126 elsif i32(opened) == 2 then
127 fail io_failed when not self.val.errors_open
128 self.val.errors_open = false
129 else
130 fail io_failed when i32(opened) < 3
131 index: usize = usize(i32(opened) - 3)
132 fail io_failed when index >= memory_file_count(self.val.files)
133 current: ptr mut memory_file = addr self.val.files[index]
134 fail io_failed when not current.val.opened
135 current.val.opened = false
136 end if
137 fail io_failed when self.val.close_calls == self.val.fail_close_at
138end memory_close
139
140memory_read: (self: ptr mut memory, opened: file, into: []mut u8)
141 -> (count: usize) ! io_failed =
142 if lenof into == 0 then
143 count = 0
144 return
145 end if
146 inc self.val.read_calls
147 fail io_failed when self.val.read_calls == self.val.fail_read_at
148 or i32(opened) < 3
149 index: usize = usize(i32(opened) - 3)
150 fail io_failed when index >= memory_file_count(self.val.files)
151 current: ptr mut memory_file = addr self.val.files[index]
152 fail io_failed when not current.val.opened or current.val.writing
153 or current.val.length > memory_bytes_count(current.val.contents)
154 or current.val.cursor > current.val.length
155 count = current.val.length - current.val.cursor
156 fail io_failed when count > 0 and self.val.read_limit == 0
157 if count > lenof into then count = lenof into end if
158 if count > self.val.read_limit then count = self.val.read_limit end if
159 mut at: usize = 0
160 while at < count do
161 into[at] = current.val.contents[current.val.cursor + at]
162 inc at
163 end while
164 current.val.cursor += count
165end memory_read
166
167memory_write_some: (self: ptr mut memory, opened: file, bytes: []u8)
168 -> (count: usize) ! io_failed =
169 if lenof bytes == 0 then
170 count = 0
171 return
172 end if
173 inc self.val.write_calls
174 fail io_failed when self.val.write_calls == self.val.fail_write_at
175 or self.val.write_limit == 0
176 count = lenof bytes
177 if count > self.val.write_limit then count = self.val.write_limit end if
178 if i32(opened) == 1 then
179 fail io_failed when not self.val.output_open
180 or self.val.output_used > memory_bytes_count(self.val.output)
181 available: usize = memory_bytes_count(self.val.output) - self.val.output_used
182 fail io_failed when available == 0
183 if count > available then count = available end if
184 mut n: usize = 0
185 while n < count do
186 self.val.output[self.val.output_used + n] = bytes[n]
187 inc n
188 end while
189 self.val.output_used += count
190 elsif i32(opened) == 2 then
191 fail io_failed when not self.val.errors_open
192 or self.val.errors_used > memory_bytes_count(self.val.errors)
193 available: usize = memory_bytes_count(self.val.errors) - self.val.errors_used
194 fail io_failed when available == 0
195 if count > available then count = available end if
196 mut n: usize = 0
197 while n < count do
198 self.val.errors[self.val.errors_used + n] = bytes[n]
199 inc n
200 end while
201 self.val.errors_used += count
202 else
203 fail io_failed when i32(opened) < 3
204 index: usize = usize(i32(opened) - 3)
205 fail io_failed when index >= memory_file_count(self.val.files)
206 current: ptr mut memory_file = addr self.val.files[index]
207 fail io_failed when not current.val.opened or not current.val.writing
208 or current.val.length > memory_bytes_count(current.val.contents)
209 available: usize = memory_bytes_count(current.val.contents) - current.val.length
210 fail io_failed when available == 0
211 if count > available then count = available end if
212 mut n: usize = 0
213 while n < count do
214 current.val.contents[current.val.length + n] = bytes[n]
215 inc n
216 end while
217 current.val.length += count
218 current.val.cursor = current.val.length
219 end if
220end memory_write_some
221
222memory_write: (self: ptr mut memory, opened: file, bytes: []u8)
223 -> none ! io_failed =
224 mut at: usize = 0
225 while at < lenof bytes do
226 count: usize = try memory_write_some(self, opened, bytes[at..<lenof bytes])
227 at += count
228 end while
229end memory_write
230
231memory_out: (self: ptr memory) -> (stream: file) =
232 _ = self.val.output_used
233 stream = file_value(1)
234end memory_out
235memory_err: (self: ptr memory) -> (stream: file) =
236 _ = self.val.errors_used
237 stream = file_value(2)
238end memory_err
239memory_argument_count: (self: ptr memory) -> (count: usize) =
240 count = memory_args_count(self.val.arguments)
241end memory_argument_count
242memory_argument: (self: ptr memory, index: usize)
243 -> (value: argument from self) ! no_argument =
244 fail no_argument when index >= memory_args_count(self.val.arguments)
245 value = self.val.arguments[index]
246end memory_argument
247
248memory is reader (read: memory_read)
249memory is writer (write: memory_write, write_some: memory_write_some)
250memory is files
251 (open_read: memory_open_read, open_write: memory_open_write,
252 same_file: memory_same_file, close: memory_close)
253memory is process
254 (out: memory_out, err: memory_err,
255 argument_count: memory_argument_count, argument: memory_argument)
256memory is world ()
257
258--- Lend the initialized standard-output prefix accumulated by the memory
259--- provider.
260public written: (inout state: memory) -> (bytes: []u8 from state) =
261 bytes = state.output[0..<state.output_used]
262end written
263--- Lend the initialized standard-error prefix accumulated by the memory
264--- provider.
265public written_errors: (inout state: memory) -> (bytes: []u8 from state) =
266 bytes = state.errors[0..<state.errors_used]
267end written_errors
268
269memory_file_count: (values: []memory_file) -> (count: usize) =
270 count = lenof values
271end memory_file_count
272memory_args_count: (values: []argument) -> (count: usize) =
273 count = lenof values
274end memory_args_count
275memory_bytes_count: (values: []u8) -> (count: usize) =
276 count = lenof values
277end memory_bytes_count