Files
Canonical source:
protos/lib/io/Files.protosat locked revisione99b712ecf658a527b1fe82c8beba2c1286d8dc9.
// 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)}