From 2a4088119cf41175457414dfa59bd4064b8f0562 Mon Sep 17 00:00:00 2001 From: Kartik Agaram Date: Wed, 1 Jan 2020 17:04:37 -0800 Subject: 5852 --- archive/1.vm/088file.mu | 213 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 archive/1.vm/088file.mu (limited to 'archive/1.vm/088file.mu') diff --git a/archive/1.vm/088file.mu b/archive/1.vm/088file.mu new file mode 100644 index 00000000..da3e35d3 --- /dev/null +++ b/archive/1.vm/088file.mu @@ -0,0 +1,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 +] -- cgit 1.4.1-2-gfad0