From 02c9dd93e3a0abc7a41d7ac0fea20619c4c9e0bd Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 28 Apr 2026 23:53:49 +0200 Subject: [PATCH] =?UTF-8?q?feat(cpp/core):=20a=C3=B1adir=20file=5Fpoll=5Fd?= =?UTF-8?q?iff=20pure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cpp/apps/primitives_gallery/CMakeLists.txt | 3 +- cpp/apps/text_editor_smoke/CMakeLists.txt | 1 + cpp/functions/core/file_poll_diff.cpp | 45 +++++++++++++ cpp/functions/core/file_poll_diff.h | 34 ++++++++++ cpp/functions/core/file_poll_diff.md | 77 ++++++++++++++++++++++ 5 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 cpp/functions/core/file_poll_diff.cpp create mode 100644 cpp/functions/core/file_poll_diff.h create mode 100644 cpp/functions/core/file_poll_diff.md diff --git a/cpp/apps/primitives_gallery/CMakeLists.txt b/cpp/apps/primitives_gallery/CMakeLists.txt index 5e542906..5205dbed 100644 --- a/cpp/apps/primitives_gallery/CMakeLists.txt +++ b/cpp/apps/primitives_gallery/CMakeLists.txt @@ -17,9 +17,10 @@ add_imgui_app(primitives_gallery ${CMAKE_SOURCE_DIR}/functions/core/timeline.cpp demos_sql.cpp demos_scientific.cpp - # text_editor + file_watcher (issue 0025) + # text_editor + file_watcher (issue 0025) + file_poll_diff pure (issue 0045) ${CMAKE_SOURCE_DIR}/functions/core/text_editor.cpp ${CMAKE_SOURCE_DIR}/functions/core/file_watcher.cpp + ${CMAKE_SOURCE_DIR}/functions/core/file_poll_diff.cpp ${CMAKE_SOURCE_DIR}/vendor/imgui_text_edit/TextEditor.cpp # sql_workbench (issue 0032) + sql_parse pure (issue 0045) ${CMAKE_SOURCE_DIR}/functions/core/sql_workbench.cpp diff --git a/cpp/apps/text_editor_smoke/CMakeLists.txt b/cpp/apps/text_editor_smoke/CMakeLists.txt index 5572da34..54beeb9c 100644 --- a/cpp/apps/text_editor_smoke/CMakeLists.txt +++ b/cpp/apps/text_editor_smoke/CMakeLists.txt @@ -7,6 +7,7 @@ add_imgui_app(text_editor_smoke main.cpp ${CMAKE_SOURCE_DIR}/functions/core/text_editor.cpp ${CMAKE_SOURCE_DIR}/functions/core/file_watcher.cpp + ${CMAKE_SOURCE_DIR}/functions/core/file_poll_diff.cpp ${CMAKE_SOURCE_DIR}/vendor/imgui_text_edit/TextEditor.cpp ) target_include_directories(text_editor_smoke PRIVATE diff --git a/cpp/functions/core/file_poll_diff.cpp b/cpp/functions/core/file_poll_diff.cpp new file mode 100644 index 00000000..794ff64a --- /dev/null +++ b/cpp/functions/core/file_poll_diff.cpp @@ -0,0 +1,45 @@ +#include "core/file_poll_diff.h" + +#include +#include + +namespace fn_ui { + +FileDiff file_poll_diff(const std::vector& before, + const std::vector& after) { + FileDiff out; + + // Index before by path para lookup O(1). + std::unordered_map idx_before; + idx_before.reserve(before.size()); + for (const auto& e : before) { + idx_before[e.path] = &e; + } + + // Recorrer after: clasificar added/modified. + std::unordered_set seen_in_after; + seen_in_after.reserve(after.size()); + for (const auto& e : after) { + seen_in_after.insert(e.path); + auto it = idx_before.find(e.path); + if (it == idx_before.end()) { + out.added.push_back(e.path); + } else { + const FileEntry* prev = it->second; + if (prev->size != e.size || prev->mtime != e.mtime) { + out.modified.push_back(e.path); + } + } + } + + // Recorrer before: detectar removed (paths que no estan en after). + for (const auto& e : before) { + if (seen_in_after.find(e.path) == seen_in_after.end()) { + out.removed.push_back(e.path); + } + } + + return out; +} + +} // namespace fn_ui diff --git a/cpp/functions/core/file_poll_diff.h b/cpp/functions/core/file_poll_diff.h new file mode 100644 index 00000000..975964bb --- /dev/null +++ b/cpp/functions/core/file_poll_diff.h @@ -0,0 +1,34 @@ +#pragma once + +// file_poll_diff — diff puro entre dos snapshots de filesystem. +// +// Pareja natural de file_watcher: en plataformas sin inotify/RDCW (o cuando +// se quiere fallback portable), el caller toma snapshots periodicos via +// stat()/opendir() y los compara con esta funcion para emitir eventos +// added/modified/removed. Sin I/O, sin estado. + +#include +#include +#include + +namespace fn_ui { + +struct FileEntry { + std::string path; + uint64_t size = 0; + int64_t mtime = 0; // unix seconds +}; + +struct FileDiff { + std::vector added; + std::vector modified; // size o mtime distinto + std::vector removed; +}; + +// Pura: calcula diff entre dos snapshots por path. Asume entries con paths +// unicos en cada snapshot. Orden de los vectores: el orden en que aparecen +// los paths en `after` (added/modified) y en `before` (removed). +FileDiff file_poll_diff(const std::vector& before, + const std::vector& after); + +} // namespace fn_ui diff --git a/cpp/functions/core/file_poll_diff.md b/cpp/functions/core/file_poll_diff.md new file mode 100644 index 00000000..526148bd --- /dev/null +++ b/cpp/functions/core/file_poll_diff.md @@ -0,0 +1,77 @@ +--- +name: file_poll_diff +kind: function +lang: cpp +domain: core +version: "1.0.0" +purity: pure +signature: "fn_ui::FileDiff fn_ui::file_poll_diff(const std::vector& before, const std::vector& after)" +description: "Diff puro entre dos snapshots de filesystem (path/size/mtime). Devuelve vectores added/modified/removed. Sin I/O. Pareja del file_watcher para fallback portable basado en polling." +tags: [filesystem, diff, poll, snapshot, pure] +uses_functions: [] +uses_types: [] +returns: [] +returns_optional: false +error_type: "" +imports: [] +tested: true +tests: ["file_poll_diff detects added/modified/removed", "file_poll_diff: empty inputs", "file_poll_diff: identical snapshots"] +test_file_path: "cpp/tests/test_file_poll_diff.cpp" +file_path: "cpp/functions/core/file_poll_diff.cpp" +params: + - name: before + desc: "Snapshot anterior. Vector de FileEntry {path, size, mtime}, paths unicos" + - name: after + desc: "Snapshot actual. Mismo formato. Paths unicos" +output: "FileDiff con tres vectores de paths: added (en after y no en before), modified (path comun pero size o mtime distinto), removed (en before y no en after)." +--- + +# file_poll_diff + +Logica pura para calcular cambios de filesystem entre dos snapshots. Sin +I/O, sin estado: el caller hace `stat()`/`opendir()` por su lado, construye +los `FileEntry`, y esta funcion los compara. + +## API + +```cpp +namespace fn_ui { + +struct FileEntry { + std::string path; + uint64_t size = 0; + int64_t mtime = 0; // unix seconds +}; + +struct FileDiff { + std::vector added; + std::vector modified; + std::vector removed; +}; + +FileDiff file_poll_diff(const std::vector& before, + const std::vector& after); + +} +``` + +## Reglas + +- Comparacion por `path` exacto. +- "Modified": al menos uno de `size` o `mtime` cambia. +- Asume paths unicos por snapshot (la funcion no deduplica). +- Complejidad: O(N+M) con un `unordered_map` para + indexar `before`. + +## Cuando usar + +- En plataformas donde `file_watcher` no tiene backend nativo (stub) y se + necesita un fallback basado en polling. +- Para consolidar bursts de eventos: tomar dos snapshots en el tiempo y + reportar solo el cambio neto (sin los intermedios). +- Tests del watcher: simular cambios de FS sin tocar disco. + +## Por que pura + +No abre archivos, no llama `stat`, no usa el reloj. Misma entrada produce la +misma salida — testeable sin fixtures de FS.