Skip to content

Files

Canonical source: protos/lib/io/Files.protos at locked revision e99b712ecf658a527b1fe82c8beba2c1286d8dc9.

// THE LICENSED WORK IS PROVIDED UNDER THE TERMS OF THE ADAPTIVE PUBLIC LICENSE
// ("LICENSE") AS FIRST COMPLETED BY: Guillermo Adrián Molina. ANY USE, PUBLIC
// DISPLAY, PUBLIC PERFORMANCE, REPRODUCTION OR DISTRIBUTION OF, OR PREPARATION OF
// DERIVATIVE WORKS BASED ON, THE LICENSED WORK CONSTITUTES RECIPIENT'S ACCEPTANCE
// OF THIS LICENSE AND ITS TERMS, WHETHER OR NOT SUCH RECIPIENT READS THE TERMS OF
// THE LICENSE. "LICENSED WORK" AND "RECIPIENT" ARE DEFINED IN THE LICENSE. A COPY
// OF THE LICENSE IS LOCATED IN THE TEXT FILE ENTITLED "LICENSE.TXT" ACCOMPANYING
// THE CONTENTS OF THIS FILE. IF A COPY OF THE LICENSE DOES NOT ACCOMPANY THIS
// FILE, A COPY OF THE LICENSE MAY ALSO BE OBTAINED AT THE FOLLOWING WEB SITE:
// https://github.com/guillermomolina/protos
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
// the specific language governing rights and limitations under the License.
//! Whole-file convenience operations over an explicit Filesystem capability.
/// Answers a Future of a fresh Bytes value with the complete contents of the file
/// at `path` in `filesystem`. The file is opened and always closed by the
/// operation.
readAllBytes: (filesystem, path) => {
(() => {
openFuture: null
file: null
(() => {
openFuture = filesystem.open(path)
file = openFuture.value()
// Bytes is deliberately not a required public prelude binding.
// Encoding.UTF8.encode("") is the existing ordinary source-level
// construction path for one fresh empty open Bytes value.
bytes: Encoding.UTF8.encode("")
// Keep implementation-selected read-ahead finite. The batch shape
// is internal policy and does not define a public chunk boundary.
batchTemplate: Array(null)
batchTemplate = Array(...batchTemplate, ...batchTemplate)
batchTemplate = Array(...batchTemplate, ...batchTemplate)
batchTemplate = Array(...batchTemplate, ...batchTemplate)
batchTemplate = Array(...batchTemplate, ...batchTemplate)
done: false
readBatch: null
readBatch = () => {
pending: Array(...batchTemplate)
issueIndex: 0
batchTemplate.each((ignored) => {
pending[issueIndex] = file.read(65536)
issueIndex = issueIndex + 1
})
chunks: Future.all(...pending).value()
chunks.each((chunk) => {
done.ifFalse(() => {
(chunk === null).ifTrue(() => {
done = true
})
(chunk === null).ifFalse(() => {
chunk.each((octet) => {
bytes.add(octet)
})
})
})
})
done.ifFalse(() => {
readBatch()
})
}
readBatch()
bytes
}).ensure(() => {
// If cancellation/failure wins at the acquisition wait boundary,
// the successful File result may already have committed without
// having been assigned to `file`. Observe that retained Future
// under ensure cleanup and close any late successful acquisition.
(file === null).ifTrue(() => {
(openFuture === null).ifFalse(() => {
openFuture.cancel()
lateFile: Error.handle(
() => openFuture.value(),
(ignored) => null
)
(lateFile === null).ifFalse(() => {
lateFile.close().value()
})
})
})
(file === null).ifFalse(() => {
file.close().value()
})
})
}).future()
}
/// Answers a Future that replaces the contents of the file at `path` in
/// `filesystem` with a snapshot of `bytes`, creating or truncating it, and
/// answers `filesystem`. The snapshot is taken synchronously; the file is always
/// closed, and a failed write is not retried.
writeAllBytes: (filesystem, path, bytes) => {
// Capture the complete caller Bytes value synchronously, before the helper
// creates its Future or starts any Filesystem effect. Latin1 is a bijection
// over all 256 octets, so this one-shot round trip is an ordinary Core way
// to obtain one fresh private Bytes value without borrowing caller state or
// invoking user-shadowable Bytes iteration messages.
snapshot: Encoding.Latin1.encode(Encoding.Latin1.decode(bytes))
(() => {
openFuture: null
file: null
(() => {
options: {
read: false
write: true
create: true
truncate: true
}
openFuture = filesystem.open(path, options)
file = openFuture.value()
offset: 0
snapshotSize: snapshot.size()
(() => offset < snapshotSize).whileTrue(() => {
remaining: snapshotSize - offset
chunkSize: 65536
(remaining < chunkSize).ifTrue(() => {
chunkSize = remaining
})
chunk: Encoding.UTF8.encode("")
chunkIndex: 0
(() => chunkIndex < chunkSize).whileTrue(() => {
chunk.add(snapshot[offset + chunkIndex])
chunkIndex = chunkIndex + 1
})
// Each write is awaited before constructing/issuing the next
// chunk. A failed ordinary write is never retried because the
// Core failure may already have contributed a committed prefix.
file.write(chunk).value()
offset = offset + chunkSize
})
filesystem
}).ensure(() => {
// Reuse the strong acquisition-custody pattern closed by LIB004-A:
// if cancellation/failure wins while open is pending, retain and
// observe that exact Future so a post-commit File cannot be orphaned.
(file === null).ifTrue(() => {
(openFuture === null).ifFalse(() => {
openFuture.cancel()
lateFile: Error.handle(
() => openFuture.value(),
(ignored) => null
)
(lateFile === null).ifFalse(() => {
lateFile.close().value()
})
})
})
(file === null).ifFalse(() => {
file.close().value()
})
})
}).future()
}
/// Answers a Future of the complete contents of the file at `path` decoded with
/// `encoding` after the whole file has been read.
readAllText: (filesystem, path, encoding) => {
(() => {
// readAllBytes owns acquisition/read/close. Decode only after that Future
// resolves, so no text conversion can weaken its resource-lifetime rules.
bytes: readAllBytes(filesystem, path).value()
encoding.decode(bytes)
}).future()
}
/// Encodes `text` with `encoding` synchronously and then writes it as by
/// `writeAllBytes`; an encoding failure signals before any Filesystem effect.
writeAllText: (filesystem, path, text, encoding) => {
// One-shot conversion is synchronous and happens before writeAllBytes can
// perform any Filesystem effect. EncodingError therefore cannot truncate or
// partially rewrite the destination. No ambient/default Encoding exists.
bytes: encoding.encode(text)
writeAllBytes(filesystem, path, bytes)
}

← io family · Standard-library source