(* BSD 2-Clause License Copyright (c) 2018, Anton Krotov All rights reserved. *) MODULE FILES; IMPORT UTILS, C := COLLECTIONS, CONSOLE; TYPE FILE* = POINTER TO RECORD (C.ITEM) ptr: INTEGER; buffer: ARRAY 64*1024 OF BYTE; count: INTEGER END; VAR files: C.COLLECTION; PROCEDURE copy (src: ARRAY OF BYTE; src_idx: INTEGER; VAR dst: ARRAY OF BYTE; dst_idx: INTEGER; bytes: INTEGER); BEGIN WHILE bytes > 0 DO dst[dst_idx] := src[src_idx]; INC(dst_idx); INC(src_idx); DEC(bytes) END END copy; PROCEDURE flush (file: FILE): INTEGER; VAR res: INTEGER; BEGIN IF file # NIL THEN res := UTILS.FileWrite(file.ptr, file.buffer, file.count); IF res < 0 THEN res := 0 END ELSE res := 0 END RETURN res END flush; PROCEDURE NewFile (): FILE; VAR file: FILE; citem: C.ITEM; BEGIN citem := C.pop(files); IF citem = NIL THEN NEW(file) ELSE file := citem(FILE) END RETURN file END NewFile; PROCEDURE create* (name: ARRAY OF CHAR): FILE; VAR file: FILE; ptr: INTEGER; BEGIN ptr := UTILS.FileCreate(name); IF ptr > 0 THEN file := NewFile(); file.ptr := ptr; file.count := 0 ELSE file := NIL END RETURN file END create; PROCEDURE open* (name: ARRAY OF CHAR): FILE; VAR file: FILE; ptr: INTEGER; BEGIN ptr := UTILS.FileOpen(name); IF ptr > 0 THEN file := NewFile(); file.ptr := ptr; file.count := -1 ELSE file := NIL END RETURN file END open; PROCEDURE close* (VAR file: FILE); VAR n: INTEGER; BEGIN IF file # NIL THEN IF file.count > 0 THEN n := flush(file) END; file.count := -1; UTILS.FileClose(file.ptr); file.ptr := 0; C.push(files, file); file := NIL END END close; PROCEDURE read* (file: FILE; VAR chunk: ARRAY OF BYTE; bytes: INTEGER): INTEGER; VAR res: INTEGER; BEGIN IF file # NIL THEN res := UTILS.FileRead(file.ptr, chunk, MAX(MIN(bytes, LEN(chunk)), 0)); IF res < 0 THEN res := 0 END ELSE res := 0 END RETURN res END read; PROCEDURE write* (file: FILE; chunk: ARRAY OF BYTE; bytes: INTEGER): INTEGER; VAR free, n, k, res, idx: INTEGER; BEGIN idx := 0; res := 0; IF (file # NIL) & (file.count >= 0) THEN free := LEN(file.buffer) - file.count; WHILE bytes > 0 DO n := MIN(free, bytes); copy(chunk, idx, file.buffer, file.count, n); INC(res, n); DEC(free, n); DEC(bytes, n); INC(idx, n); INC(file.count, n); IF free = 0 THEN k := flush(file); IF k # LEN(file.buffer) THEN bytes := 0; DEC(res, n) ELSE file.count := 0; free := LEN(file.buffer) END END END END RETURN res END write; PROCEDURE WriteByte* (file: FILE; byte: BYTE): BOOLEAN; VAR res: BOOLEAN; BEGIN res := TRUE; IF (file # NIL) & (file.count >= 0) THEN IF file.count = LEN(file.buffer) THEN IF flush(file) # LEN(file.buffer) THEN res := FALSE ELSE file.buffer[0] := byte; file.count := 1 END ELSE file.buffer[file.count] := byte; INC(file.count) END ELSE res := FALSE END RETURN res END WriteByte; BEGIN files := C.create() END FILES.