src/ByteManifestIo.bend

mylsm-lsm-store@0.6.0.1 · 155 lines · 16 definitions

1import Base
2import mylsm-lsm-store@0.6.0.1/src/ByteManifest.bend as Manifest
3import mylsm-lsm-store@0.6.0.1/src/Fs.bend as Fs
4import bend-kit-bytes@0.3.2.0/bytes.bend as Bytes
5
6# Separates invalid manifest bytes from filesystem failures while opening it.
7type Error is Data:
8  Invalid{error: Manifest.Error}
9  Host{code: U32, message: String}
10
11def path.reverse(path: String, reversed: String) -> String:
12  match path:
13    case SNil{}: reversed
14    case SCon{character, rest}: path.reverse(rest, SCon{character, reversed})
15
16def path.clone(path: String) -> String:
17  path.reverse(path.reverse(path, SNil{}), SNil{})
18
19def read.result(
20  result: Result<&1, &1, Manifest.Error, Manifest.ManifestValue>
21) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
22  match result:
23    case Fail{error}:
24      IO.pure(Result<&1, &1, Error, Manifest.ManifestValue>, Fail{Invalid{error}})
25    case Done{value}:
26      IO.pure(Result<&1, &1, Error, Manifest.ManifestValue>, Done{value})
27
28def read.host(error: U32 & String) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
29  match error:
30    case (code, message):
31      IO.pure(Result<&1, &1, Error, Manifest.ManifestValue>, Fail{Host{code, message}})
32
33def read.decoded(
34  pair: File & Result<&1, &1, U32 & String, Bytes.Bytes>
35) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
36  match pair:
37    case (file, Fail{error}):
38      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
39        _closed : Unit <- File.close(file)
40        read.host(error)
41    case (file, Done{bytes}):
42      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
43        _closed : Unit <- File.close(file)
44        read.result(Manifest.decode(bytes))
45
46def read.sized(file: File, size: Nat) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
47  match size:
48    case 56n:
49      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
50        pair : File & Result<&1, &1, U32 & String, Bytes.Bytes> <- Fs.read_at(file, 0, 56)
51        read.decoded(pair)
52    case 63n:
53      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
54        pair : File & Result<&1, &1, U32 & String, Bytes.Bytes> <- Fs.read_at(file, 0, 63)
55        read.decoded(pair)
56    case 60n:
57      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
58        pair : File & Result<&1, &1, U32 & String, Bytes.Bytes> <- Fs.read_at(file, 0, 60)
59        read.decoded(pair)
60    case _:
61      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
62        _closed : Unit <- File.close(file)
63        read.result(Fail{Manifest.Malformed{}})
64
65def read.size.result(
66  file: File,
67  result: Result<&1, &1, U32 & String, Nat>
68) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
69  match result:
70    case Done{size}: read.sized(file, size)
71    case Fail{error}:
72      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
73        _closed : Unit <- File.close(file)
74        read.host(error)
75
76def read.path.opened(
77  path: String,
78  opened: Result<&1, &1, U32 & String, File>
79) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
80  match opened:
81    case Fail{error}:
82      read.host(error)
83    case Done{file}:
84      do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
85        size : Result<&1, &1, U32 & String, Nat> <- Fs.file_size(path)
86        read.size.result(file, size)
87
88# Reads and validates one bounded generation-four or generation-five manifest.
89def read(
90  +path: String
91) -> IO(Result<&1, &1, Error, Manifest.ManifestValue>):
92  do IO<Result<&1, &1, Error, Manifest.ManifestValue>>:
93    opened : Result<&1, &1, U32 & String, File> <- File.open(path, "r")
94    read.path.opened(path, opened)
95
96def write.directory.synced(
97  directory: String,
98  result: Result<&1, &1, U32 & String, Unit>
99) -> IO(Result<&1, &1, U32 & String, Unit>):
100  match result:
101    case Fail{error}: IO.pure(Result<&1, &1, U32 & String, Unit>, Fail{error})
102    case Done{Unit{}}: Fs.fsync(directory)
103
104def write.renamed(
105  +directory: String,
106  result: Result<&1, &1, U32 & String, Unit>
107) -> IO(Result<&1, &1, U32 & String, Unit>):
108  match result:
109    case Fail{error}: IO.pure(Result<&1, &1, U32 & String, Unit>, Fail{error})
110    case Done{Unit{}}:
111      do IO<Result<&1, &1, U32 & String, Unit>>:
112        renamed : Result<&1, &1, U32 & String, Unit> <- Fs.rename(directory ++ "/MANIFEST.tmp", directory ++ "/MANIFEST")
113        write.directory.synced(directory, renamed)
114
115def write.closed(
116  +directory: String,
117  result: Result<&1, &1, U32 & String, Unit>
118) -> IO(Result<&1, &1, U32 & String, Unit>):
119  match result:
120    case Fail{error}: IO.pure(Result<&1, &1, U32 & String, Unit>, Fail{error})
121    case Done{Unit{}}:
122      do IO<Result<&1, &1, U32 & String, Unit>>:
123        synced : Result<&1, &1, U32 & String, Unit> <- Fs.fsync(directory ++ "/MANIFEST.tmp")
124        write.renamed(directory, synced)
125
126def write.file.synced(
127  directory: String,
128  result: File & Result<&1, &1, U32 & String, Unit>
129) -> IO(Result<&1, &1, U32 & String, Unit>):
130  match result:
131    case (file, result):
132      do IO<Result<&1, &1, U32 & String, Unit>>:
133        _closed : Unit <- File.close(file)
134        write.closed(directory, result)
135
136def write.bytes(
137  directory: String,
138  bytes: Bytes.Bytes,
139  opened: Result<&1, &1, U32 & String, File>
140) -> IO(Result<&1, &1, U32 & String, Unit>):
141  match opened:
142    case Fail{error}: IO.pure(Result<&1, &1, U32 & String, Unit>, Fail{error})
143    case Done{file}:
144      do IO<Result<&1, &1, U32 & String, Unit>>:
145        result : File & Result<&1, &1, U32 & String, Unit> <- Fs.write_bytes(file, bytes)
146        write.file.synced(directory, result)
147
148# Publishes a manifest through a synced temporary and atomic rename.
149def write_atomic(
150  +directory: String,
151  value: Manifest.ManifestValue
152) -> IO(Result<&1, &1, U32 & String, Unit>):
153  do IO<Result<&1, &1, U32 & String, Unit>>:
154    opened : Result<&1, &1, U32 & String, File> <- File.open(directory ++ "/MANIFEST.tmp", "w")
155    write.bytes(directory, Manifest.encode(value), opened)