Landin library reference source

core/io/memory.ldn

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