cleanup comments, update translations, bump to 0.4.1

This commit is contained in:
olcxja 2026-10-05 15:42:44 +02:00
commit 7d8ab87eee
28 changed files with 412 additions and 1114 deletions

View file

@ -1,19 +1,14 @@
'use strict'
// Content hashing for the LAN sync, plus the per-file digest cache.
//
// Syncing a library means hashing tens of thousands of files, so the digest of
// every file is remembered on disk (files/data/lansync-hashes.json), keyed by
// its FILES-relative path with the size and mtime it was computed from: a file
// whose size and mtime are unchanged is never read again, only stat'ed. The
// Content hashing for the LAN sync, plus the per-file digest cache
// (files/data/lansync-hashes.json, keyed by path with size + mtime). The
// cache is machine-local and never travels in a sync.
const crypto = require('crypto')
const fs = require('fs')
const path = require('path')
// BLAKE2b resists collisions like SHA-2 but is noticeably faster. Older
// runtimes fall back to SHA-2.
// BLAKE2b is faster than SHA-2; older runtimes fall back to SHA-2.
const HASH_ALGO = (() => {
for (const algo of ['blake2b512', 'sha512', 'sha256']) {
try {
@ -24,9 +19,7 @@ const HASH_ALGO = (() => {
return 'sha256'
})()
// Streaming through one callback instead of accumulating 'data' chunks keeps
// memory flat for huge files, and a small highWaterMark gives the read pump
// real backpressure. Digest speed does not care about the chunk size.
// Streamed hashing keeps memory flat on huge files.
function hashFile(file, onBytes) {
return new Promise((resolve, reject) => {
const hash = crypto.createHash(HASH_ALGO)
@ -39,11 +32,9 @@ function hashFile(file, onBytes) {
})
}
// A quarter million entries is a whole game library; beyond that, entries the
// longest time untouched are dropped first.
// Beyond a quarter million entries, the least recently touched are dropped.
const HASH_CACHE_MAX = 250000
// Mid-sync saves are throttled to this (the file can hold tens of megabytes);
// callers that must not lose entries (sync end, shutdown) pass force=true.
// Mid-sync saves are throttled; callers that must not lose entries pass force=true.
const SAVE_MIN_MS = 60000
function createHashCache(dataDir) {
@ -64,8 +55,7 @@ function createHashCache(dataDir) {
return cache
}
// Compact JSON (the entries are arrays) keeps a full cache at tens of
// milliseconds instead of the seconds pretty-printed JSON needs.
// Compact JSON: a full cache writes in tens of ms.
function save(force) {
if (!dirty || !cache) return
if (!force && Date.now() - savedAt < SAVE_MIN_MS) return
@ -86,10 +76,8 @@ function createHashCache(dataDir) {
} catch {}
}
// The remembered digest of a file, or null when it has to be read again.
// A hit only marks the cache dirty when its timestamp is actually stale:
// marking it on every lookup would force a full rewrite of the file per
// sync even though nothing a second sync could reuse has changed.
// The remembered digest, or null when it has to be read again. A hit marks
// the cache dirty only when the timestamp is stale.
function hit(rel, size, mtimeMs) {
const known = load()[rel]
if (!known || known[0] !== size || known[1] !== mtimeMs || typeof known[2] !== 'string') {
@ -109,9 +97,7 @@ function createHashCache(dataDir) {
dirty = true
}
// Digest of a file we already stat'ed: size and mtime unchanged means the
// bytes are the same, and the digest from an earlier sync still describes
// them. Otherwise the file is read (progress through onBytes) and stored.
// Unchanged size + mtime -> cached digest; otherwise read and store.
async function cachedHash(rel, abs, size, mtimeMs, onBytes) {
const known = hit(rel, size, mtimeMs)
if (known) return known
@ -120,8 +106,7 @@ function createHashCache(dataDir) {
return hash
}
// The user can drop the cache from the settings view; a corrupted or stale
// file is also replaced wholesale this way.
// Settings view "clear cache" and stale files go through here.
function clear() {
cache = null
dirty = false
@ -134,8 +119,7 @@ function createHashCache(dataDir) {
} catch {}
}
// Dropping the in-memory copy makes the next lookup re-read the file from
// disk - what tests use to prove a file was hashed instead of remembered.
// Dropping the memory copy makes the next lookup re-read from disk (tests).
function forget() {
cache = null
dirty = false