about summary refs log tree commit diff stats
path: root/archive/1.vm/088file.mu
diff options
context:
space:
mode:
Diffstat (limited to 'archive/1.vm/088file.mu')
-rw-r--r--archive/1.vm/088file.mu213
1 files changed, 213 insertions, 0 deletions
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
+]