about summary refs log tree commit diff stats
path: root/088file.mu
blob: da3e35d3e438f232c23d95b77ed459fdcb5bb38f (plain) (blame)
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# Wrappers around file system primitives that take a 'resources' object and
# are thus easier to test.
#
# - start-reading - asynchronously open a file, returning a channel source for
#   receiving the results
# - start-writing - asynchronously open a file, returning a channel sink for
#   the data to write
# - slurp - synchronously read from a file
# - dump - synchronously write to a file

container resources [
  lock:bool
  data:&:@:resource
]

container resource [
  name:text
  contents:text
]

def start-reading resources:&:resources, filename:text -> contents:&:source:char, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  {
    break-unless resources
    # fake file system
    contents, error? <- start-reading-from-fake-resource resources, filename
    return
  }
  # real file system
  file:num <- $open-file-for-reading filename
  return-unless file, null/no-contents, true/error
  contents:&:source:char, sink:&:sink:char <- new-channel 30
  start-running receive-from-file file, sink
]

def slurp resources:&:resources, filename:text -> contents:text, error?:bool [
  local-scope
  load-inputs
  source:&:source:char, error?:bool <- start-reading resources, filename
  return-if error?, null/no-contents
  buf:&:buffer:char <- new-buffer 30/capacity
  {
    c:char, done?:bool, source <- read source
    break-if done?
    buf <- append buf, c
    loop
  }
  contents <- buffer-to-array buf
]

def start-reading-from-fake-resource resources:&:resources, resource:text -> contents:&:source:char, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  i:num <- copy 0
  data:&:@:resource <- get *resources, data:offset
  len:num <- length *data
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    i <- add i, 1
    curr-resource:text <- get tmp, name:offset
    found?:bool <- equal resource, curr-resource
    loop-unless found?
    contents:&:source:char, sink:&:sink:char <- new-channel 30
    curr-contents:text <- get tmp, contents:offset
    start-running receive-from-text curr-contents, sink
    return
  }
  return null/no-such-resource, true/error-found
]

def receive-from-file file:num, sink:&:sink:char -> sink:&:sink:char [
  local-scope
  load-inputs
  {
    c:char, eof?:bool <- $read-from-file file
    break-if eof?
    sink <- write sink, c
    loop
  }
  sink <- close sink
  file <- $close-file file
]

def receive-from-text contents:text, sink:&:sink:char -> sink:&:sink:char [
  local-scope
  load-inputs
  i:num <- copy 0
  len:num <- length *contents
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    c:char <- index *contents, i
    sink <- write sink, c
    i <- add i, 1
    loop
  }
  sink <- close sink
]

def start-writing resources:&:resources, filename:text -> sink:&:sink:char, routine-id:num, error?:bool [
  local-scope
  load-inputs
  error? <- copy false
  source:&:source:char, sink:&:sink:char <- new-channel 30
  {
    break-unless resources
    # fake file system
    routine-id <- start-running transmit-to-fake-resource resources, filename, source
    return
  }
  # real file system
  file:num <- $open-file-for-writing filename
  return-unless file, null/sink, 0/routine-id, true/error
  {
    break-if file
    msg:text <- append [no such file: ] filename
    assert file, msg
  }
  routine-id <- start-running transmit-to-file file, source
]

def dump resources:&:resources, filename:text, contents:text -> resources:&:resources, error?:bool [
  local-scope
  load-inputs
  # todo: really create an empty file
  return-unless contents, resources, false/no-error
  sink-file:&:sink:char, write-routine:num, error?:bool <- start-writing resources, filename
  return-if error?
  i:num <- copy 0
  len:num <- length *contents
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    c:char <- index *contents, i
    sink-file <- write sink-file, c
    i <- add i, 1
    loop
  }
  close sink-file
  # make sure to wait for the file to be actually written to disk
  # (Mu practices structured concurrency: http://250bpm.com/blog:71)
  wait-for-routine write-routine
]

def transmit-to-file file:num, source:&:source:char -> source:&:source:char [
  local-scope
  load-inputs
  {
    c:char, done?:bool, source <- read source
    break-if done?
    $write-to-file file, c
    loop
  }
  file <- $close-file file
]

def transmit-to-fake-resource resources:&:resources, filename:text, source:&:source:char -> resources:&:resources, source:&:source:char [
  local-scope
  load-inputs
  lock:location <- get-location *resources, lock:offset
  wait-for-reset-then-set lock
  # compute new file contents
  buf:&:buffer:char <- new-buffer 30
  {
    c:char, done?:bool, source <- read source
    break-if done?
    buf <- append buf, c
    loop
  }
  contents:text <- buffer-to-array buf
  new-resource:resource <- merge filename, contents
  # write to resources
  curr-filename:text <- copy null
  data:&:@:resource <- get *resources, data:offset
  # replace file contents if it already exists
  i:num <- copy 0
  len:num <- length *data
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    curr-filename <- get tmp, name:offset
    found?:bool <- equal filename, curr-filename
    {
      break-unless found?
      put-index *data, i, new-resource
      jump +unlock-and-exit
    }
    i <- add i, 1
    loop
  }
  # if file didn't already exist, make room for it
  new-len:num <- add len, 1
  new-data:&:@:resource <- new resource:type, new-len
  put *resources, data:offset, new-data
  # copy over old files
  i:num <- copy 0
  {
    done?:bool <- greater-or-equal i, len
    break-if done?
    tmp:resource <- index *data, i
    put-index *new-data, i, tmp
  }
  # write new file
  put-index *new-data, len, new-resource
  +unlock-and-exit
  reset lock
]