mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-21 15:02:21 +02:00
refactor(utils): share the FNV-1a 32-bit hash
Add fnv1a32 in hash_utils.dart for the scan output name (UTF-8 bytes) and qualityVariantStagingLabel (UTF-16 code units). Unit tests pin outputs computed from the original inline code.
This commit is contained in:
1 parent
036589a4b1
commit
adf2047df6
4 files changed
+47
-10
No files matched your search
@@ -8,6 +8,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotiflac_android/services/download_request_payload.dart';
|
||||
import 'package:spotiflac_android/utils/audio_format_utils.dart';
|
||||
import 'package:spotiflac_android/utils/file_access.dart';
|
||||
import 'package:spotiflac_android/utils/hash_utils.dart';
|
||||
import 'package:spotiflac_android/utils/logger.dart';
|
||||
|
||||
part 'platform_bridge/bridge_codec.dart';
|
||||
@@ -2326,11 +2327,7 @@ class PlatformBridge {
|
||||
'method': method,
|
||||
'arguments': arguments,
|
||||
});
|
||||
var hash = 0x811c9dc5;
|
||||
for (final byte in utf8.encode(identity)) {
|
||||
hash ^= byte;
|
||||
hash = (hash * 0x01000193) & 0xffffffff;
|
||||
}
|
||||
final hash = fnv1a32(utf8.encode(identity));
|
||||
final output = File(
|
||||
'${scanDir.path}${Platform.pathSeparator}'
|
||||
'library_scan_${hash.toRadixString(16).padLeft(8, '0')}.ndjson',
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:spotiflac_android/utils/hash_utils.dart';
|
||||
import 'package:spotiflac_android/utils/int_utils.dart';
|
||||
import 'package:spotiflac_android/utils/string_utils.dart';
|
||||
|
||||
@@ -290,11 +291,7 @@ int? _bitrateFromQuality(String? quality) {
|
||||
}
|
||||
|
||||
String qualityVariantStagingLabel(String itemId) {
|
||||
var hash = 0x811c9dc5;
|
||||
for (final byte in itemId.codeUnits) {
|
||||
hash ^= byte;
|
||||
hash = (hash * 0x01000193) & 0xffffffff;
|
||||
}
|
||||
final hash = fnv1a32(itemId.codeUnits);
|
||||
return 'qv_${hash.toRadixString(16).padLeft(8, '0')}';
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
/// FNV-1a 32-bit hash over [units] (UTF-8 bytes or UTF-16 code units).
|
||||
///
|
||||
/// The result feeds on-disk file names, so the algorithm must not change.
|
||||
int fnv1a32(Iterable<int> units) {
|
||||
var hash = 0x811c9dc5;
|
||||
for (final unit in units) {
|
||||
hash ^= unit;
|
||||
hash = (hash * 0x01000193) & 0xffffffff;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:spotiflac_android/utils/audio_format_utils.dart';
|
||||
import 'package:spotiflac_android/utils/hash_utils.dart';
|
||||
|
||||
String _hex(int hash) => hash.toRadixString(16).padLeft(8, '0');
|
||||
|
||||
void main() {
|
||||
// Expected values were computed from the original inline implementations
|
||||
// (scan output name over UTF-8 bytes, staging label over UTF-16 units).
|
||||
test('fnv1a32 over UTF-8 bytes matches the scan output name hash', () {
|
||||
expect(_hex(fnv1a32(utf8.encode(''))), '811c9dc5');
|
||||
expect(_hex(fnv1a32(utf8.encode('track-123'))), '8277aead');
|
||||
expect(
|
||||
_hex(
|
||||
fnv1a32(
|
||||
utf8.encode('{"method":"scan","arguments":{"path":"content://x/y"}}'),
|
||||
),
|
||||
),
|
||||
'94bc90df',
|
||||
);
|
||||
expect(_hex(fnv1a32(utf8.encode('café 日本'))), 'c843754b');
|
||||
});
|
||||
|
||||
test('qualityVariantStagingLabel matches the original code units hash', () {
|
||||
expect(qualityVariantStagingLabel(''), 'qv_811c9dc5');
|
||||
expect(qualityVariantStagingLabel('a'), 'qv_e40c292c');
|
||||
expect(qualityVariantStagingLabel('track-123'), 'qv_8277aead');
|
||||
expect(qualityVariantStagingLabel('café 日本'), 'qv_73a64e9d');
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user