1 # Wrappers around file system primitives that take a 'resources' object and
2 # are thus easier to test.
4 # - start-reading - asynchronously open a file, returning a channel source for
5 # receiving the results
6 # - start-writing - asynchronously open a file, returning a channel sink for
8 # - slurp - synchronously read from a file
9 # - dump - synchronously write to a file
21 def start-reading resources:&:resources, filename:text -> contents:&:source:char, error?:bool [
26 break-unless resources
28 contents, error? <- start-reading-from-fake-resource resources, filename
32 file:num <- $open-file-for-reading filename
33 return-unless file, null/no-contents, true/error
34 contents:&:source:char, sink:&:sink:char <- new-channel 30
35 start-running receive-from-file file, sink
38 def slurp resources:&:resources, filename:text -> contents:text, error?:bool [
41 source:&:source:char, error?:bool <- start-reading resources, filename
42 return-if error?, null/no-contents
43 buf:&:buffer:char <- new-buffer 30/capacity
45 c:char, done?:bool, source <- read source
50 contents <- buffer-to-array buf
53 def start-reading-from-fake-resource resources:&:resources, resource:text -> contents:&:source:char, error?:bool [
58 data:&:@:resource <- get *resources, data:offset
59 len:num <- length *data
61 done?:bool <- greater-or-equal i, len
63 tmp:resource <- index *data, i
65 curr-resource:text <- get tmp, name:offset
66 found?:bool <- equal resource, curr-resource
68 contents:&:source:char, sink:&:sink:char <- new-channel 30
69 curr-contents:text <- get tmp, contents:offset
70 start-running receive-from-text curr-contents, sink
73 return null/no-such-resource, true/error-found
76 def receive-from-file file:num, sink:&:sink:char -> sink:&:sink:char [
80 c:char, eof?:bool <- $read-from-file file
86 file <- $close-file file
89 def receive-from-text contents:text, sink:&:sink:char -> sink:&:sink:char [
93 len:num <- length *contents
95 done?:bool <- greater-or-equal i, len
97 c:char <- index *contents, i
105 def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num, error?:bool [
109 source:&:source:char, sink:&:sink:char <- new-channel 30
111 break-unless resources
113 routine-id <- start-running transmit-to-fake-resource resources, filename, source
117 file:num <- $open-file-for-writing filename
118 return-unless file, null/sink, 0/routine-id, true/error
121 msg:text <- append [no such file: ] filename
124 routine-id <- start-running transmit-to-file file, source
127 def dump resources:&:resources, filename:text, contents:text -> resources:&:resources, error?:bool [
130 # todo: really create an empty file
131 return-unless contents, resources, false/no-error
132 sink-file:&:sink:char, write-routine:num, error?:bool <- start-writing resources, filename
135 len:num <- length *contents
137 done?:bool <- greater-or-equal i, len
139 c:char <- index *contents, i
140 sink-file <- write sink-file, c
145 # make sure to wait for the file to be actually written to disk
146 # (Mu practices structured concurrency: http://250bpm.com/blog:71)
147 wait-for-routine write-routine
150 def transmit-to-file file:num, source:&:source:char -> source:&:source:char [
154 c:char, done?:bool, source <- read source
156 $write-to-file file, c
159 file <- $close-file file
162 def transmit-to-fake-resource resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
165 lock:location <- get-location *resources, lock:offset
166 wait-for-reset-then-set lock
167 # compute new file contents
168 buf:&:buffer:char <- new-buffer 30
170 c:char, done?:bool, source <- read source
175 contents:text <- buffer-to-array buf
176 new-resource:resource <- merge filename, contents
178 curr-filename:text <- copy null
179 data:&:@:resource <- get *resources, data:offset
180 # replace file contents if it already exists
182 len:num <- length *data
184 done?:bool <- greater-or-equal i, len
186 tmp:resource <- index *data, i
187 curr-filename <- get tmp, name:offset
188 found?:bool <- equal filename, curr-filename
191 put-index *data, i, new-resource
192 jump +unlock-and-exit
197 # if file didn't already exist, make room for it
198 new-len:num <- add len, 1
199 new-data:&:@:resource <- new resource:type, new-len
200 put *resources, data:offset, new-data
201 # copy over old files
204 done?:bool <- greater-or-equal i, len
206 tmp:resource <- index *data, i
207 put-index *new-data, i, tmp
210 put-index *new-data, len, new-resource