Skip to content

Commit

Permalink
feat(fs): file writes update the file cache (#6186)
Browse files Browse the repository at this point in the history
Co-authored-by: kevinkang-Globe <[email protected]>
  • Loading branch information
liamHowatt and kevinkang-Globe committed May 13, 2024
1 parent e3f70a2 commit 4e85b0f
Show file tree
Hide file tree
Showing 6 changed files with 376 additions and 147 deletions.
3 changes: 2 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
'sphinx_sitemap',
'lv_example',
'sphinx_rtd_dark_mode',
'link_roles'
'link_roles',
'sphinxcontrib.mermaid'
]

default_dark_mode = False
Expand Down
2 changes: 1 addition & 1 deletion docs/libs/fs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ The work directory can be set with ``LV_FS_..._PATH``. E.g.
``"/home/joe/projects/"`` The actual file/directory paths will be
appended to it.

Cached reading is also supported if ``LV_FS_..._CACHE_SIZE`` is set to
:ref:`Cached reading <overview_file_system_cache>` is also supported if ``LV_FS_..._CACHE_SIZE`` is set to
not ``0`` value. :cpp:func:`lv_fs_read` caches this size of data to lower the
number of actual reads from the storage.

Expand Down
89 changes: 89 additions & 0 deletions docs/overview/fs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,95 @@ To use files in image widgets the following callbacks are required:
- seek
- tell

.. _overview_file_system_cache:

Optional file buffering/caching
*******************************

Files will buffer their reads if the corresponding ``LV_FS_*_CACHE_SIZE``
config option is set to a value greater than zero. Each open file will
buffer up to that many bytes to reduce the number of FS driver calls.

Generally speaking, file buffering can be optimized for different kinds
of access patterns. The one implemented here is optimal for reading large
files in chunks, which is what the image decoder does.
It has the potential to call the driver's ``read`` fewer
times than ``lv_fs_read`` is called. In the best case where the cache size is
\>= the size of the file, ``read`` will only be called once. This strategy is good
for linear reading of large files but less helpful for short random reads across a file bigger than the buffer
since data will be buffered that will be discarded after the next seek and read.
The cache should be sufficiently large or disabled in that case. Another case where the cache should be disabled
is if the file contents are expected to change by an external factor like with special OS files.

The implementation is documented below. Note that the FS functions make calls
to other driver FS functions when the cache is enabled. i.e., ``lv_fs_read`` may call the driver's ``seek``
so the driver needs to implement more callbacks when the cache is enabled.

``lv_fs_read`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------

.. mermaid::
:zoom:

%%{init: {'theme':'neutral'}}%%
flowchart LR
A["call lv_fs_read and
the cache is enabled"] --> B{{"is there cached data
at the file position?"}}
B -->|yes| C{{"does the cache have
all required bytes available?"}}
C -->|yes| D["copy all required bytes from
the cache to the destination
buffer"]
C -->|no| F["copy the available
required bytes
until the end of the cache
into the destination buffer"]
--> G["seek the real file to the end
of what the cache had available"]
--> H{{"is the number of remaining bytes
larger than the size of the whole cache?"}}
H -->|yes| I["read the remaining bytes
from the real file to the
destination buffer"]
H -->|no| J["eagerly read the real file
to fill the whole cache
or as many bytes as the
read call can"]
--> O["copy the required bytes
to the destination buffer"]
B -->|no| K["seek the real file to
the file position"]
--> L{{"is the number of required
bytes greater than the
size of the entire cache?"}}
L -->|yes| M["read the real file to
the destination buffer"]
L -->|no| N["eagerly read the real file
to fill the whole cache
or as many bytes as the
read call can"]
--> P["copy the required bytes
to the destination buffer"]

``lv_fs_write`` :sub:`(behavior when the cache is enabled)`
--------------------------------------------------

The part of the cache that coincides with the written content
will be updated to reflect the written content.

``lv_fs_seek`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------

The driver's ``seek`` will not actually be called unless the ``whence``
is ``LV_FS_SEEK_END``, in which case ``seek`` and ``tell`` will be called
to determine where the end of the file is.

``lv_fs_tell`` :sub:`(behavior when the cache is enabled)`
-------------------------------------------------

The driver's ``tell`` will not actually be called.

.. _overview_file_system_api:

API
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ sphinxcontrib-htmlhelp
sphinxcontrib-jsmath
sphinxcontrib-qthelp
sphinxcontrib-serializinghtml
sphinxcontrib-mermaid
sphinx-rtd-dark-mode
typing-extensions

0 comments on commit 4e85b0f

Please sign in to comment.