Files
Crosspoint/docs/contributing/koreader-synchronization.md
T
2026-04-01 10:38:02 +02:00

4.3 KiB

KOReader Synchronization Architecture

This document explains the intent and internal structure of the KOReader synchronization code in CrossPoint.

Scope:

  • Synchronization logic that maps between CrossPoint reading position and KOReader sync payloads.
  • Module boundaries and responsibilities.
  • Matching rules, fallback strategy, and expected behavior.

For XPath-specific details and examples, see koreader-sync-xpath-mapping.md.

Goals

The synchronization layer is designed to:

  • Be robust on constrained devices (ESP32-C3 memory constraints).
  • Be deterministic and debuggable when mapping positions.
  • Keep transport/client logic separated from parsing/mapping logic.
  • Prefer precise anchors when available, but degrade gracefully.

Data Model Mismatch

CrossPoint stores position as chapter/page-centric state. KOReader sync payload stores position as XPath-like anchor plus percentage.

Because layout engines differ, page equality cannot be guaranteed across devices. The synchronization strategy therefore combines:

  • Structural anchor mapping (XPath).
  • Percent-based fallback.
  • Paragraph LUT refinement when available.

Module Responsibilities

Client / orchestration

XPath indexing facade

Forward mapping engine

Reverse mapping engine

Shared parser/state/utilities

Core Logic

Forward (CrossPoint -> KOReader)

  1. Decompress one spine XHTML to a temporary file.
  2. Count total visible text bytes.
  3. Cache that total per spine (cache-path + spine index + href) so repeated mappings for the same chapter can skip the expensive counting pass.
  4. Convert intra-spine progress to target visible-byte offset.
  5. Stream parse and stop at target.
  6. Emit anchor:
    • element XPath, or
    • /text()[N].M when in body-level text-node context.

Reverse (KOReader -> CrossPoint)

  1. Decompress one spine XHTML to a temporary file.
  2. Stream parse chapter while evaluating candidate matches.
  3. Resolve best tier in this order:
    • exact
    • exact-no-index
    • ancestor
    • ancestor-no-index
  4. Convert resolved byte offset to intra-spine progress.

For text-node anchors /text()[N].M:

  • N is treated as 1-based text node index.
  • M is treated as 0-based codepoint offset.

Fallback Strategy

When XPath mapping fails or is ambiguous:

  • Fall back to percentage-driven chapter/page estimation.
  • Use paragraph LUT refinement where available.

This guarantees user progress continuity even for malformed or sparse content.

Constraints and Non-Goals

  • No full DOM materialization for entire books.
  • Parse only one spine item on demand.
  • Keep memory usage bounded and transient.
  • Do not attempt pixel-perfect page parity with KOReader.