blob: 14ed8c69d66702b7fa21dedef57e757c3491180d [file]
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:test/test.dart';
void main() {
const hashes = {
'md5': md5,
'sha1': sha1,
'sha224': sha224,
'sha256': sha256,
'sha384': sha384,
'sha512': sha512,
'sha512/224': sha512224,
'sha512/256': sha512256,
};
// Non-uniform bytes, so that stale buffer contents would change the digest.
final data = Uint8List.fromList(
List.generate(300, (i) => (i * 131 + 17) & 0xff),
);
for (final MapEntry(key: name, value: hash) in hashes.entries) {
test('$name: chunked conversion matches convert for every length', () {
for (var length = 0; length <= data.length; length++) {
final expected =
hash.convert(Uint8List.sublistView(data, 0, length)).toString();
for (final chunkSize in const [1, 3, 64, 65, 127, 129]) {
expect(
_added(hash, data, length, chunkSize).toString(),
expected,
reason: 'length $length added $chunkSize bytes at a time',
);
expect(
_sliced(hash, data, length, chunkSize).toString(),
expected,
reason: 'length $length sliced $chunkSize bytes at a time',
);
}
}
});
}
}
/// Hashes the first [length] bytes of [data] by passing [chunkSize] bytes at a
/// time to [ByteConversionSink.add].
Digest _added(Hash hash, Uint8List data, int length, int chunkSize) {
late Digest digest;
final sink = hash.startChunkedConversion(
ChunkedConversionSink<Digest>.withCallback((digests) {
digest = digests.single;
}),
);
for (var start = 0; start < length; start += chunkSize) {
sink.add(
Uint8List.sublistView(data, start, min(start + chunkSize, length)));
}
sink.close();
return digest;
}
/// Hashes the first [length] bytes of [data] by passing [chunkSize] bytes at a
/// time to [ByteConversionSink.addSlice], closing with `isLast`.
Digest _sliced(Hash hash, Uint8List data, int length, int chunkSize) {
late Digest digest;
final sink = hash.startChunkedConversion(
ChunkedConversionSink<Digest>.withCallback((digests) {
digest = digests.single;
}),
);
var start = 0;
do {
final end = min(start + chunkSize, length);
sink.addSlice(data, start, end, end == length);
start = end;
} while (start < length);
return digest;
}