fix other mandelbrot variants
[mu.git] / archive / 1.vm / 088file.mu
blobda3e35d3e438f232c23d95b77ed459fdcb5bb38f
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
7 #   the data to write
8 # - slurp - synchronously read from a file
9 # - dump - synchronously write to a file
11 container resources [
12   lock:bool
13   data:&:@:resource
16 container resource [
17   name:text
18   contents:text
21 def start-reading resources:&:resources, filename:text -> contents:&:source:char, error?:bool [
22   local-scope
23   load-inputs
24   error? <- copy false
25   {
26     break-unless resources
27     # fake file system
28     contents, error? <- start-reading-from-fake-resource resources, filename
29     return
30   }
31   # real file system
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 [
39   local-scope
40   load-inputs
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
44   {
45     c:char, done?:bool, source <- read source
46     break-if done?
47     buf <- append buf, c
48     loop
49   }
50   contents <- buffer-to-array buf
53 def start-reading-from-fake-resource resources:&:resources, resource:text -> contents:&:source:char, error?:bool [
54   local-scope
55   load-inputs
56   error? <- copy false
57   i:num <- copy 0
58   data:&:@:resource <- get *resources, data:offset
59   len:num <- length *data
60   {
61     done?:bool <- greater-or-equal i, len
62     break-if done?
63     tmp:resource <- index *data, i
64     i <- add i, 1
65     curr-resource:text <- get tmp, name:offset
66     found?:bool <- equal resource, curr-resource
67     loop-unless found?
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
71     return
72   }
73   return null/no-such-resource, true/error-found
76 def receive-from-file file:num, sink:&:sink:char -> sink:&:sink:char [
77   local-scope
78   load-inputs
79   {
80     c:char, eof?:bool <- $read-from-file file
81     break-if eof?
82     sink <- write sink, c
83     loop
84   }
85   sink <- close sink
86   file <- $close-file file
89 def receive-from-text contents:text, sink:&:sink:char -> sink:&:sink:char [
90   local-scope
91   load-inputs
92   i:num <- copy 0
93   len:num <- length *contents
94   {
95     done?:bool <- greater-or-equal i, len
96     break-if done?
97     c:char <- index *contents, i
98     sink <- write sink, c
99     i <- add i, 1
100     loop
101   }
102   sink <- close sink
105 def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num, error?:bool [
106   local-scope
107   load-inputs
108   error? <- copy false
109   source:&:source:char, sink:&:sink:char <- new-channel 30
110   {
111     break-unless resources
112     # fake file system
113     routine-id <- start-running transmit-to-fake-resource resources, filename, source
114     return
115   }
116   # real file system
117   file:num <- $open-file-for-writing filename
118   return-unless file, null/sink, 0/routine-id, true/error
119   {
120     break-if file
121     msg:text <- append [no such file: ] filename
122     assert file, msg
123   }
124   routine-id <- start-running transmit-to-file file, source
127 def dump resources:&:resources, filename:text, contents:text -> resources:&:resources, error?:bool [
128   local-scope
129   load-inputs
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
133   return-if error?
134   i:num <- copy 0
135   len:num <- length *contents
136   {
137     done?:bool <- greater-or-equal i, len
138     break-if done?
139     c:char <- index *contents, i
140     sink-file <- write sink-file, c
141     i <- add i, 1
142     loop
143   }
144   close sink-file
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 [
151   local-scope
152   load-inputs
153   {
154     c:char, done?:bool, source <- read source
155     break-if done?
156     $write-to-file file, c
157     loop
158   }
159   file <- $close-file file
162 def transmit-to-fake-resource resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
163   local-scope
164   load-inputs
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
169   {
170     c:char, done?:bool, source <- read source
171     break-if done?
172     buf <- append buf, c
173     loop
174   }
175   contents:text <- buffer-to-array buf
176   new-resource:resource <- merge filename, contents
177   # write to resources
178   curr-filename:text <- copy null
179   data:&:@:resource <- get *resources, data:offset
180   # replace file contents if it already exists
181   i:num <- copy 0
182   len:num <- length *data
183   {
184     done?:bool <- greater-or-equal i, len
185     break-if done?
186     tmp:resource <- index *data, i
187     curr-filename <- get tmp, name:offset
188     found?:bool <- equal filename, curr-filename
189     {
190       break-unless found?
191       put-index *data, i, new-resource
192       jump +unlock-and-exit
193     }
194     i <- add i, 1
195     loop
196   }
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
202   i:num <- copy 0
203   {
204     done?:bool <- greater-or-equal i, len
205     break-if done?
206     tmp:resource <- index *data, i
207     put-index *new-data, i, tmp
208   }
209   # write new file
210   put-index *new-data, len, new-resource
211   +unlock-and-exit
212   reset lock