Author: Virgil Dupras <firstname.lastname@example.org>
Date: Fri, 13 Jan 2023 13:18:42 -0500
sys/file: add "file" and "dstfile" structbinds
See doc/sys/file. For now, it's not of much use, but I think it will grow to
a powerful tool.
5 files changed, 75 insertions(+), 20 deletions(-)
diff --git a/fs/doc/sys/file.txt b/fs/doc/sys/file.txt
@@ -193,13 +193,6 @@ Does it mean that Dusk couldn't read them? No, only that file/dir enumeration
would have to go through FS-specific tools. I think that this inconvenience is
worth it if it means an overall simpler API.
-## Path literals
-There are 2 words for Path/File shortcuts: p" and f".
-p" foo" is a shortcut for S" foo" curpath :find# and f" foo" is a shortcut for
-p" foo" Path :open.
MemFile is a structure that extends File and provides read/write/seek
@@ -230,3 +223,57 @@ one units and we don't want to load these pieces of code twice. To this end,
whenever a file is loaded, we record its ID in a linked list to remember we
loaded it. If you use the "?f<<" word instead of "f<<", the specified file will
be loaded only if it wasn't already loaded.
+## Handle management and global structbinds
+When we open a file, the filesystem returns a handle. The management of the
+memory required to service these handles is specific to the filesystem
+implementation, but we typically require significant memory space for each
+handle for the contents buffer. Therefore, it's important to close handles when
+we don't use them anymore so that this memory space can be used for another
+Handles opened directly by the system operator can be combersome to manage and
+forgetting to close them is easy. To help with this, sys/file defines two global
+structbinds, "file" and "dstfile" with accompanying helper words listed below.
+The idea with those structbinds is that they're easily accessible to the sysop
+and also implicitly close themselves when being overwritten. This way, the sysop
+doesn't have to remember to close those handles as long as she use those
+Also, those structbinds serve as a kind of "stdio, but for seekable contents".
+That is, some words will use them to simplify their API (you don't have to pass
+them a handle, just have the structbinds properly set).
+Words that use "file" and "dstfile" are expected to never set them themselves.
+They can seek, read, write to it, but the binding to the handles themselves is
+the domain of the sysop.
+file Structbind to File, defaults to 0
+dstfile Structbind to File, defaults to 0
+: file$ ( -- )
+ Reset "file" and "dstfile", closing them if necessary.
+: >file ( hdl -- )
+ Close current "file" and rebind it to "hdl".
+: >dstfile ( hdl -- )
+ Close current "dstfile" and rebind it to "hdl".
+: file>dstfile ( -- )
+ Close current "dstfile", set it to "file" and reset "file".
+## Path literals
+There are 2 words for Path/File shortcuts: p" and f".
+p" foo" is a shortcut for:
+ S" foo" curpath :find#
+f" foo" is a shortcut for:
+ p" foo" Path :open >file
diff --git a/fs/sys/file.fs b/fs/sys/file.fs
@@ -120,8 +120,6 @@ struct[ Path
Path _curpath structbind Path curpath
-: p" [compile] S" curpath :find# ; immediate
-: f" [compile] p" Path :open ; immediate
: f<< word curpath :find# Path :fload ;
: ?f<< word curpath :find# dup Path :floaded? if drop else Path :fload then ;
@@ -132,6 +130,17 @@ Path _curpath structbind Path curpath
to@! stdio readio >r execute
r> to stdio readio r> File :close ;
+0 structbind File file
+0 structbind File dstfile
+: >file ( hdl -- ) file :self if file :close then ['] file rebind ;
+: >dstfile ( hdl -- ) dstfile :self if dstfile :close then ['] dstfile rebind ;
+: file>dstfile ( -- ) file :self >dstfile 0 ['] file rebind ;
+: file$ 0 >dstfile 0 >file ;
+: p" [compile] S" curpath :find# ; immediate
+: f" [compile] p" Path :open >file ; immediate
extends File struct[ MemFile
SZ &+ :buf(
: :)buf dup :buf( swap size + ;
@@ -148,4 +157,3 @@ extends File struct[ MemFile
0 ( putback ) , ['] _readbuf , ['] _writebuf , ['] drop , ['] drop ,
0 ( pos ) , dup ( size ) , ['] _seek , ( sz ) allot ;
diff --git a/fs/tests/emul/cos/cvm.fs b/fs/tests/emul/cos/cvm.fs
@@ -8,7 +8,7 @@ testbegin
0 value myres
vm structbind COSVM vm
-vm mem f" /tests/emul/cos/dummy.bin" File :readall
+vm mem f" /tests/emul/cos/dummy.bin" file :readall
: iowr00 to myres ;
' iowr00 vm iowr !
vm running #
diff --git a/fs/tests/fs/blob.fs b/fs/tests/fs/blob.fs
@@ -7,13 +7,13 @@ testbegin
512 TOTSEC RAMDrive :new value mydrv
mydrv BlobFS :mount value myfs
myfs filesystems CELLSZ + ! \ register it as "B:"
-p" B:/foobar" Path :open value myfile \ any subpath works
-S" hello" myfile File :puts
-512 myfile File :seek
-S" world!" myfile File :puts
-0 myfile File :seek
+f" B:/foobar" \ any subpath works
+S" hello" file :puts
+512 file :seek
+S" world!" file :puts
+0 file :seek
create expected ," hello"
-here 5 myfile File :read
+here 5 file :read
here expected 5 = #
mydrv RAMDrive :buf( expected 5 = #
create expected ," world!"
diff --git a/fs/tests/sys/file.fs b/fs/tests/sys/file.fs
@@ -42,9 +42,9 @@ S" foo.fs" myroot :newfile #
S" /foo.fs" myroot :find# ( path ) \ found!
dup Path :info ( path info )
FSInfo name S" FOO.FS" #s=
-( path ) Path :open value myfile
-myfile FATFile :cluster0 0 #eq \ no cluster allocated yet
-S" 42" c@+ myfile File :write myfile File :close
+( path ) Path :open >file
+file :self FATFile :cluster0 0 #eq \ no cluster allocated yet
+S" 42" c@+ file :write file :flush
S" /foo.fs" myroot :find# Path :fload 42 #eq
\ how about a directory?
S" bar" myroot :newdir dup # value mydir