Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
4db080f
Fix format-type mismatch in HARD_DISK_METADATA_FORMAT snprintf
rtissera Sep 2, 2026
7e1e56c
Fix ESP32 ROM miniz symbol collision capturing libchdr's decoder
rtissera Sep 2, 2026
a679db2
Make CHDR_DEBUG_ZLIB diagnostics failure-only and stage-tagged
rtissera Sep 2, 2026
351ac80
Report FLAC allocation failures as CHDERR_OUT_OF_MEMORY
rtissera Sep 2, 2026
a208641
Add ESP32-P4 real-hardware SD-card benchmark
rtissera Sep 2, 2026
6355d39
Benchmark: build with LOWRAM_TARGET=1, and document why
rtissera Sep 2, 2026
6dedc21
Reset the huffman subtable arena on every lookup-table rebuild
rtissera Sep 2, 2026
b172184
Add a decoded-hunk cache for COMPRESSION_SELF back-references
rtissera Sep 2, 2026
cdcdc90
Add CHDR_PROFILE_CDFL: per-stage timing for the CD-FLAC codec
rtissera Sep 2, 2026
9301cd9
Benchmark: bottleneck attribution, FATFS fast seek, granularity sweep
rtissera Sep 2, 2026
268cbdb
Benchmark README: correct the FLAC attribution and the RAM story
rtissera Sep 2, 2026
e3f7202
Benchmark: fix build with BENCH_PROGRESS_EVERY enabled
rtissera Sep 2, 2026
d981a9e
Add a caller-budgeted compressed read-ahead window
rtissera Sep 2, 2026
fe35784
Drop miniz's 32KB LZ dictionary from every zlib codec instance
rtissera Sep 2, 2026
1e56b3d
cdzs: share one zstd context between sector data and subcode under LO…
rtissera Sep 2, 2026
21a4a55
Benchmark: raw-IO probes that locate the throughput ceiling
rtissera Sep 2, 2026
b2a9899
Benchmark: FatFs-backed core_file, 1.36x throughput for no libchdr ch…
rtissera Sep 2, 2026
2ab905a
README: document the 8.4x storage-stack loss and the read-ahead result
rtissera Sep 2, 2026
070341a
Benchmark: build a cluster link map in the FatFs backend
rtissera Sep 3, 2026
0bcf624
README: final uncapped numbers, and read-ahead's verdict depends on t…
rtissera Sep 3, 2026
86a4404
Speed up CD sector ECC regeneration by 1.60x
rtissera Sep 3, 2026
b704242
Benchmark: add an integer multiply latency probe
rtissera Sep 3, 2026
0765eec
Update dr_flac to v0.13.4
rtissera Sep 3, 2026
e8bcaec
Benchmark: time the ESP-IDF allocator alongside the multiply probe
rtissera Sep 3, 2026
c66b52a
Benchmark: compare five ECC inner-loop variants on real hardware
rtissera Sep 3, 2026
f1a0600
Compute CD sector P parity four rows at a time
rtissera Sep 3, 2026
d0ff583
Fold four bytes per iteration in the hunk CRC
rtissera Sep 3, 2026
45661a7
Document the ESP32-P4 CPU findings
rtissera Sep 3, 2026
149dc57
Avoid signed overflow reading a big-endian uint32
rtissera Sep 3, 2026
222f468
Skip dr_flac's per-frame CRC when the hunk CRC already covers it
rtissera Sep 3, 2026
56273ca
Reject out-of-range map bit widths instead of shifting by them
rtissera Sep 3, 2026
598424f
Do not shift by 32 when refilling an over-consumed bitstream
rtissera Sep 3, 2026
68aab45
Document the full-corpus result and the fuzzing findings
rtissera Sep 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ tests/esp32p4/embed/
tests/esp32p4/fw.elf
tests/esp32p4/*.log
contrib/esp32p4/smoke_test.elf
contrib/esp32p4/idf-benchmark/main/embed/
contrib/esp32p4/idf-benchmark/main/embed_list.inc
contrib/esp32p4/idf-benchmark/main/embed_includes.inc
contrib/esp32p4/idf-benchmark/build/
contrib/esp32p4/idf-benchmark/sdkconfig
contrib/esp32p4/idf-benchmark/sdkconfig.old
26 changes: 26 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ if (WITH_SYSTEM_ZLIB)
else()
if(NOT TARGET miniz)
add_subdirectory(deps/miniz-3.1.2 EXCLUDE_FROM_ALL)
# ESP ROMs export an older miniz that would otherwise capture this one
# at link time; applied here rather than inside deps/ so a miniz bump
# can't drop it. No-op off ESP-IDF.
include(${CMAKE_CURRENT_LIST_DIR}/cmake/EspRomMinizWorkaround.cmake)
libchdr_apply_esp_rom_miniz_workaround(miniz)
set(CHDR_NEEDS_MINIZ_RENAME TRUE)
endif()
list(APPEND CHDR_LIBS miniz)
endif()
Expand Down Expand Up @@ -88,6 +94,17 @@ else()
endif()

if(CHDR_VERIFY_BLOCK_CRC)
# The per-hunk CRC chdman stores covers the fully reconstituted hunk, ECC and
# sync header included, so a build that skips ecc_generate() cannot match it.
# The failure is content-dependent - hunks holding only audio frames have no
# ECC to regenerate and still verify - which makes it look like sporadic file
# corruption rather than a build misconfiguration. Refuse the combination.
if(NOT CHDR_WANT_RAW_DATA_SECTOR)
message(FATAL_ERROR
"CHDR_VERIFY_BLOCK_CRC=ON requires CHDR_WANT_RAW_DATA_SECTOR=ON: without the "
"regenerated ECC and sync header the decoded hunk cannot match the CRC stored "
"in the file. Turn CHDR_VERIFY_BLOCK_CRC off as well to build without raw sectors.")
endif()
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=1)
else()
list(APPEND CHDR_DEFINES VERIFY_BLOCK_CRC=0)
Expand Down Expand Up @@ -124,6 +141,12 @@ set(CHDR_SOURCES
add_library(chdr-static STATIC ${CHDR_SOURCES})
target_include_directories(chdr-static INTERFACE include)
target_link_libraries(chdr-static PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
# libchdr_codec_zlib.c calls tinfl_decompress() directly, so it must be renamed
# alongside miniz's definition - otherwise on ESP-IDF this call binds to the
# ROM's older copy and we are back to the split decoder this branch fixed.
if(CHDR_NEEDS_MINIZ_RENAME)
libchdr_apply_esp_rom_miniz_workaround(chdr-static)
endif()
target_compile_definitions(chdr-static PRIVATE ${CHDR_DEFINES})

if(MSVC)
Expand All @@ -140,6 +163,9 @@ if (BUILD_SHARED_LIBS)
add_library(chdr SHARED ${CHDR_SOURCES})
target_include_directories(chdr INTERFACE include)
target_link_libraries(chdr PRIVATE ${CHDR_LIBS} ${PLATFORM_LIBS})
if(CHDR_NEEDS_MINIZ_RENAME)
libchdr_apply_esp_rom_miniz_workaround(chdr)
endif()
target_compile_definitions(chdr PRIVATE ${CHDR_DEFINES})

if(MSVC)
Expand Down
70 changes: 70 additions & 0 deletions cmake/EspRomMinizWorkaround.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Work around Espressif ROMs exporting their own, older miniz.
#
# ESP32 ROMs (S3/C3/C6/P4/...) bake in an older miniz and export its tinfl
# entry points from the target's ROM linker script as *absolute* symbols -
# see the "Group miniz" block in
# $IDF_PATH/components/esp_rom/<target>/ld/<target>.rom.ld, e.g.
#
# tinfl_decompress = 0x4fc000f8;
#
# A linker-script assignment outranks an ordinary object definition, so an
# ESP-IDF link silently binds those names to ROM and drops the copies
# compiled from deps/miniz-3.1.2/miniz.c - even though both are present in
# the archive. The result is a *split decoder*: mz_inflateInit2()/mz_inflate()
# from miniz 3.1.2 build and interpret a 3.1.2-layout tinfl_decompressor, then
# hand it to a ROM tinfl_decompress() that lays that struct out differently
# (miniz 3.0 reworked the Huffman tables from tinfl_huff_table m_tables[3] to
# the flattened m_look_up/m_tree_N form, changing field offsets and total
# size). The ROM decoder writes past the end of the smaller m_decomp and
# corrupts the enclosing inflate_state.
#
# Observed on an ESP32-P4 (rev v3.1) against a 128-file CHD corpus: the first
# inflate of a stream mostly survives, then every later one fails, because the
# overrun lands on inflate_state::m_window_bits (which sits just before
# m_dict[32768]). mz_inflate() then sees m_window_bits > 0, sets
# TINFL_FLAG_PARSE_ZLIB_HEADER on a raw-deflate stream opened with
# inflateInit2(..., -MAX_WBITS), consumes exactly 2 bytes on the CMF/FLG check
# and returns MZ_DATA_ERROR. It presents as CHDERR_DECOMPRESSION_ERROR and
# looks exactly like corrupt input or a silicon/codegen bug.
#
# Renaming the colliding symbols keeps miniz.c's own definitions reachable.
# Only miniz.c references these names, so applying the defines to whatever
# target compiles miniz.c is sufficient. mz_free matters independently of the
# decoder mismatch: bound to ROM it would hand ESP-IDF-heap pointers to the
# ROM allocator. mz_adler32 is benign but renamed for consistency.
#
# Deliberately NOT patched into deps/miniz-3.1.2/miniz.h - that tree is
# vendored verbatim so it can be re-synced from upstream, and a local edit
# there would be silently dropped by the next version bump. Keep this file as
# the single definition; both build paths below include it.
#
# Regression check (cheap, no flashing) - this must print nothing:
#
# grep -hoE '^[A-Za-z_][A-Za-z0-9_]* = 0x' \
# "$IDF_PATH"/components/esp_rom/<target>/ld/<target>.rom*.ld \
# | sed 's/ = 0x//' | sort -u > /tmp/rom_syms.txt
# <target>-nm <libchdr archive> \
# | awk '$2 ~ /^[TDBR]$/ {print $3}' | sort -u > /tmp/chdr_syms.txt
# comm -12 /tmp/rom_syms.txt /tmp/chdr_syms.txt
#
# Re-run it after any miniz bump, and after adding any dep the ROM also
# ships - the rom.ld files list them by group.

set(LIBCHDR_ESP_ROM_MINIZ_COLLISIONS
tinfl_decompress
tinfl_decompress_mem_to_heap
tinfl_decompress_mem_to_mem
tinfl_decompress_mem_to_callback
mz_adler32
mz_free
)

# Apply the renames to a target that compiles miniz.c. No-op off ESP-IDF.
function(libchdr_apply_esp_rom_miniz_workaround target)
if(NOT ESP_PLATFORM)
return()
endif()
foreach(sym IN LISTS LIBCHDR_ESP_ROM_MINIZ_COLLISIONS)
target_compile_definitions(${target} PRIVATE "${sym}=libchdr_${sym}")
endforeach()
endfunction()
3 changes: 3 additions & 0 deletions contrib/esp32p4/idf-benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(libchdr_esp32p4_benchmark)
Loading
Loading