|
2 | 2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | 3 | // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
|
5 | | -import 'dart:typed_data'; |
| 5 | +import 'dart:io'; |
6 | 6 |
|
7 | 7 | import 'package:crypto/crypto.dart'; |
8 | 8 | import 'package:gcloud/storage.dart'; |
@@ -69,26 +69,43 @@ class TarballStorage { |
69 | 69 | Future<ContentMatchStatus> matchArchiveContentInCanonical( |
70 | 70 | String package, |
71 | 71 | String version, |
72 | | - Uint8List bytes, |
| 72 | + File file, |
73 | 73 | ) async { |
74 | 74 | final objectName = tarballObjectName(package, version); |
75 | 75 | final info = await _canonicalBucket.tryInfo(objectName); |
76 | 76 | if (info == null) { |
77 | 77 | return ContentMatchStatus.missing; |
78 | 78 | } |
79 | | - if (info.length != bytes.length) { |
| 79 | + if (info.length != await file.length()) { |
80 | 80 | return ContentMatchStatus.different; |
81 | 81 | } |
82 | | - final md5hash = md5.convert(bytes).bytes; |
| 82 | + final md5hash = (await file.openRead().transform(md5).single).bytes; |
83 | 83 | if (!md5hash.byteToByteEquals(info.md5Hash)) { |
84 | 84 | return ContentMatchStatus.different; |
85 | 85 | } |
86 | | - final objectBytes = await _canonicalBucket.readAsBytes(objectName); |
87 | | - if (bytes.byteToByteEquals(objectBytes)) { |
88 | | - return ContentMatchStatus.same; |
89 | | - } else { |
| 86 | + // limit memory use while doing the byte-to-byte comparison |
| 87 | + final raf = await file.open(); |
| 88 | + var remainingLength = info.length; |
| 89 | + try { |
| 90 | + await for (final chunk in _canonicalBucket.read(objectName)) { |
| 91 | + if (chunk.isEmpty) continue; |
| 92 | + remainingLength -= chunk.length; |
| 93 | + if (remainingLength < 0) { |
| 94 | + return ContentMatchStatus.different; |
| 95 | + } |
| 96 | + // TODO: consider rewriting to fixed-length chunk comparison |
| 97 | + final fileChunk = await raf.read(chunk.length); |
| 98 | + if (!fileChunk.byteToByteEquals(chunk)) { |
| 99 | + return ContentMatchStatus.different; |
| 100 | + } |
| 101 | + } |
| 102 | + } finally { |
| 103 | + await raf.close(); |
| 104 | + } |
| 105 | + if (remainingLength != 0) { |
90 | 106 | return ContentMatchStatus.different; |
91 | 107 | } |
| 108 | + return ContentMatchStatus.same; |
92 | 109 | } |
93 | 110 |
|
94 | 111 | /// Copies the uploaded object from the temp bucket to the canonical bucket. |
|
0 commit comments