storage: map_file - read a file straight from memory-mapped flash - #11363
Open
lynt-smitka wants to merge 1 commit into
Open
storage: map_file - read a file straight from memory-mapped flash#11363lynt-smitka wants to merge 1 commit into
lynt-smitka wants to merge 1 commit into
Conversation
lynt-smitka
force-pushed
the
storage-map-file
branch
from
September 13, 2026 13:39
7f1c648 to
070fbc8
Compare
Author
|
Trying to turn off a module on the two boards that are tight on flash |
storage.map_file(f) returns a tuple of read-only memoryviews over the flash bytes of an open file on the internal CIRCUITPY drive, one per contiguous cluster run, in file order and 0 copy. Anything that takes a buffer can then use the file without reading it into RAM: a synthio.MidiTrack, a RawSample, a wavetable, a ulab array, a bitmap. Assets stay ordinary files on the drive. Opening a file for reading already builds its FatFs cluster-link map, so the function only reads that map. The supervisor maps a FatFs sector to a flash address through a port hook that also reports how far the mapping stays contiguous, so a run is split where it is not. raspberrypi returns the execute-in-place address (the drive is XIP on every RP2 board); espressif esp_partition_mmap's each drive partition on first use and reports the seam of an extended drive. The function is always present; on a port whose drive is not mapped (CIRCUITPY_STORAGE_MAP_FILE off) it raises NotImplementedError. 600 B of text on pajenicko_picopad, 496 B on adafruit_feather_esp32s3_tft.
lynt-smitka
force-pushed
the
storage-map-file
branch
from
September 13, 2026 19:52
070fbc8 to
00c8c71
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
storage.map_file(f)lets use an asset straight from the CIRCUITPY drive's flash, withoutcopying it into RAM. FatFs fragmented file is not a problem: the function returns one read-only
memoryview per contiguous cluster run, in file order, and the caller composes them. A bitmap, a
sample bank or a MIDI track that does not fit in RAM stays an ordinary file on the drive. This
started as
picogame.xip_mapfor the picogame's backgrounds loading. The same works foraudiocore.RawSample,synthio.MidiTrackandsynthio.Notewavetables, which keep their bufferfor as long as they exist, so I moved the function in
storage.In a test with a FAT image written by Linux, 16 % of new files were contiguous after every other
file had been deleted and 58 % after random deletes, and a fragmented file had 2, at most 3 runs. So
the function returns every run. A record inside one run is a slice of that run's memoryview. A
record that straddles a run boundary is copied.
Opening a file for reading already builds its FatFs cluster-link map, so the function only reads
that map. On RP2 the last written 4 KB flash sector can still sit in a RAM copy, so the function
flushes it to flash first and the memoryviews see what was last written. A port hook maps a drive
block to a flash address and says how far the mapping stays contiguous, so a run is split where it
is not. raspberrypi returns the execute-in-place address. espressif maps each drive partition with
esp_partition_mmapon first use and reports the seam of an extended drive, the default on 8 MB andlarger flash. Other ports raise
NotImplementedError. Errors:EINVALfor a closed file or one notopen for reading only,
EOPNOTSUPPon another mount,EIOfor a corrupt chain,MemoryErrorifopen()could not allocate the map.Measured with the 18 background strips (92 KB) of a picogame game, heap used:
map_fileopen().read()Flash: +0.5 kB
The memoryviews are not copies of the file. When the file is rewritten they show the new bytes, and
a mix of old and new while the write runs. On RP2
AudioOutandI2SOutcopy a wholeaudiocore.RawSampleinto a staging buffer, so a mapped sample saves no RAM there. Through anaudiomixer.Mixerthe voice reads the memoryview in place.displayio.Bitmapalways allocates itsown pixel buffer, so a mapped image still has to be copied into one. Letting a Bitmap use the mapped
bytes directly, through the existing
construct_from_bufferwithread_only, is a follow-up.