/src/mozilla-central/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsUrlClassifierPrefixSet.h" |
8 | | #include "nsIUrlClassifierPrefixSet.h" |
9 | | #include "crc32c.h" |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsDebug.h" |
12 | | #include "nsPrintfCString.h" |
13 | | #include "nsTArray.h" |
14 | | #include "nsString.h" |
15 | | #include "nsIFile.h" |
16 | | #include "nsToolkitCompsCID.h" |
17 | | #include "nsTArray.h" |
18 | | #include "nsThreadUtils.h" |
19 | | #include "nsNetUtil.h" |
20 | | #include "nsISeekableStream.h" |
21 | | #include "nsIBufferedStreams.h" |
22 | | #include "nsIFileStreams.h" |
23 | | #include "mozilla/MemoryReporting.h" |
24 | | #include "mozilla/Telemetry.h" |
25 | | #include "mozilla/FileUtils.h" |
26 | | #include "mozilla/Logging.h" |
27 | | #include "mozilla/Unused.h" |
28 | | #include <algorithm> |
29 | | |
30 | | using namespace mozilla; |
31 | | |
32 | | // MOZ_LOG=UrlClassifierPrefixSet:5 |
33 | | static LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet"); |
34 | 0 | #define LOG(args) MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args) |
35 | | #define LOG_ENABLED() MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug) |
36 | | |
37 | | NS_IMPL_ISUPPORTS( |
38 | | nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet, nsIMemoryReporter) |
39 | | |
40 | | // Definition required due to std::max<>() |
41 | | const uint32_t nsUrlClassifierPrefixSet::MAX_BUFFER_SIZE; |
42 | | |
43 | | template<typename T> |
44 | | static void |
45 | | CalculateTArrayChecksum(const nsTArray<T>& aArray, uint32_t* outChecksum) |
46 | 0 | { |
47 | 0 | *outChecksum = ~0; |
48 | 0 |
|
49 | 0 | for (size_t i = 0; i < aArray.Length(); i++) { |
50 | 0 | const T& element = aArray[i]; |
51 | 0 | const void* pointer = &element; |
52 | 0 | *outChecksum = ComputeCrc32c(*outChecksum, |
53 | 0 | reinterpret_cast<const uint8_t*>(pointer), |
54 | 0 | sizeof(void*)); |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet() |
59 | | : mLock("nsUrlClassifierPrefixSet.mLock") |
60 | | , mIndexDeltasChecksum(~0) |
61 | | , mTotalPrefixes(0) |
62 | 0 | { |
63 | 0 | } |
64 | | |
65 | | NS_IMETHODIMP |
66 | | nsUrlClassifierPrefixSet::Init(const nsACString& aName) |
67 | 0 | { |
68 | 0 | mName = aName; |
69 | 0 | mMemoryReportPath = |
70 | 0 | nsPrintfCString( |
71 | 0 | "explicit/storage/prefix-set/%s", |
72 | 0 | (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!") |
73 | 0 | ); |
74 | 0 |
|
75 | 0 | RegisterWeakMemoryReporter(this); |
76 | 0 |
|
77 | 0 | return NS_OK; |
78 | 0 | } |
79 | | |
80 | | nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() |
81 | 0 | { |
82 | 0 | UnregisterWeakMemoryReporter(this); |
83 | 0 | } |
84 | | |
85 | | void |
86 | | nsUrlClassifierPrefixSet::Clear() |
87 | 0 | { |
88 | 0 | LOG(("[%s] Clearing PrefixSet", mName.get())); |
89 | 0 | mIndexDeltas.Clear(); |
90 | 0 | mIndexDeltasChecksum = ~0; |
91 | 0 | mIndexPrefixes.Clear(); |
92 | 0 | mTotalPrefixes = 0; |
93 | 0 | } |
94 | | |
95 | | NS_IMETHODIMP |
96 | | nsUrlClassifierPrefixSet::SetPrefixes(const uint32_t* aArray, uint32_t aLength) |
97 | 0 | { |
98 | 0 | MutexAutoLock lock(mLock); |
99 | 0 |
|
100 | 0 | nsresult rv = NS_OK; |
101 | 0 | Clear(); |
102 | 0 |
|
103 | 0 | if (aLength > 0) { |
104 | 0 | rv = MakePrefixSet(aArray, aLength); |
105 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
106 | 0 | Clear(); // clear out any leftovers |
107 | 0 | } |
108 | 0 | } |
109 | 0 |
|
110 | 0 | MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length()); |
111 | 0 | return rv; |
112 | 0 | } |
113 | | |
114 | | nsresult |
115 | | nsUrlClassifierPrefixSet::MakePrefixSet(const uint32_t* aPrefixes, uint32_t aLength) |
116 | 0 | { |
117 | 0 | mLock.AssertCurrentThreadOwns(); |
118 | 0 |
|
119 | 0 | MOZ_ASSERT(aPrefixes); |
120 | 0 | MOZ_ASSERT(aLength > 0); |
121 | 0 |
|
122 | | #ifdef DEBUG |
123 | | for (uint32_t i = 1; i < aLength; i++) { |
124 | | MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]); |
125 | | } |
126 | | #endif |
127 | |
|
128 | 0 | mIndexPrefixes.AppendElement(aPrefixes[0]); |
129 | 0 | mIndexDeltas.AppendElement(); |
130 | 0 |
|
131 | 0 | uint32_t numOfDeltas = 0; |
132 | 0 | uint32_t totalDeltas = 0; |
133 | 0 | uint32_t previousItem = aPrefixes[0]; |
134 | 0 | for (uint32_t i = 1; i < aLength; i++) { |
135 | 0 | if ((numOfDeltas >= DELTAS_LIMIT) || |
136 | 0 | (aPrefixes[i] - previousItem >= MAX_INDEX_DIFF)) { |
137 | 0 | // Compact the previous element. |
138 | 0 | // Note there is always at least one element when we get here, |
139 | 0 | // because we created the first element before the loop. |
140 | 0 | if (!mIndexDeltas.AppendElement(fallible)) { |
141 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
142 | 0 | } |
143 | 0 | |
144 | 0 | if (!mIndexPrefixes.AppendElement(aPrefixes[i], fallible)) { |
145 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
146 | 0 | } |
147 | 0 | |
148 | 0 | numOfDeltas = 0; |
149 | 0 | } else { |
150 | 0 | uint16_t delta = aPrefixes[i] - previousItem; |
151 | 0 | if (!mIndexDeltas.LastElement().AppendElement(delta, fallible)) { |
152 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
153 | 0 | } |
154 | 0 | |
155 | 0 | numOfDeltas++; |
156 | 0 | totalDeltas++; |
157 | 0 | } |
158 | 0 | previousItem = aPrefixes[i]; |
159 | 0 | } |
160 | 0 |
|
161 | 0 | mTotalPrefixes = aLength; |
162 | 0 |
|
163 | 0 | mIndexDeltas.LastElement().Compact(); |
164 | 0 |
|
165 | 0 | // The hdr pointer of the last element of nsTArray may change after calling |
166 | 0 | // mIndexDeltas.LastElement().Compact(), so calculate checksum after the call. |
167 | 0 | CalculateTArrayChecksum(mIndexDeltas, &mIndexDeltasChecksum); |
168 | 0 |
|
169 | 0 | mIndexDeltas.Compact(); |
170 | 0 | mIndexPrefixes.Compact(); |
171 | 0 | MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length()); |
172 | 0 |
|
173 | 0 | LOG(("Total number of indices: %d (crc=%u)", aLength, mIndexDeltasChecksum)); |
174 | 0 | LOG(("Total number of deltas: %d", totalDeltas)); |
175 | 0 | LOG(("Total number of delta chunks: %zu", mIndexDeltas.Length())); |
176 | 0 |
|
177 | 0 | return NS_OK; |
178 | 0 | } |
179 | | |
180 | | nsresult |
181 | | nsUrlClassifierPrefixSet::GetPrefixesNative(FallibleTArray<uint32_t>& outArray) |
182 | 0 | { |
183 | 0 | MutexAutoLock lock(mLock); |
184 | 0 |
|
185 | 0 | if (!outArray.SetLength(mTotalPrefixes, fallible)) { |
186 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
187 | 0 | } |
188 | 0 | |
189 | 0 | uint32_t prefixIdxLength = mIndexPrefixes.Length(); |
190 | 0 | uint32_t prefixCnt = 0; |
191 | 0 |
|
192 | 0 | for (uint32_t i = 0; i < prefixIdxLength; i++) { |
193 | 0 | uint32_t prefix = mIndexPrefixes[i]; |
194 | 0 |
|
195 | 0 | if (prefixCnt >= mTotalPrefixes) { |
196 | 0 | return NS_ERROR_FAILURE; |
197 | 0 | } |
198 | 0 | outArray[prefixCnt++] = prefix; |
199 | 0 |
|
200 | 0 | for (uint32_t j = 0; j < mIndexDeltas[i].Length(); j++) { |
201 | 0 | prefix += mIndexDeltas[i][j]; |
202 | 0 | if (prefixCnt >= mTotalPrefixes) { |
203 | 0 | return NS_ERROR_FAILURE; |
204 | 0 | } |
205 | 0 | outArray[prefixCnt++] = prefix; |
206 | 0 | } |
207 | 0 | } |
208 | 0 |
|
209 | 0 | NS_ASSERTION(mTotalPrefixes == prefixCnt, "Lengths are inconsistent"); |
210 | 0 | return NS_OK; |
211 | 0 | } |
212 | | |
213 | | NS_IMETHODIMP |
214 | | nsUrlClassifierPrefixSet::GetPrefixes(uint32_t* aCount, |
215 | | uint32_t** aPrefixes) |
216 | 0 | { |
217 | 0 | // No need to get mLock here because this function does not directly touch |
218 | 0 | // the class's data members. (GetPrefixesNative() will get mLock, however.) |
219 | 0 |
|
220 | 0 | NS_ENSURE_ARG_POINTER(aCount); |
221 | 0 | *aCount = 0; |
222 | 0 | NS_ENSURE_ARG_POINTER(aPrefixes); |
223 | 0 | *aPrefixes = nullptr; |
224 | 0 |
|
225 | 0 | FallibleTArray<uint32_t> prefixes; |
226 | 0 | nsresult rv = GetPrefixesNative(prefixes); |
227 | 0 | if (NS_FAILED(rv)) { |
228 | 0 | return rv; |
229 | 0 | } |
230 | 0 | |
231 | 0 | uint64_t itemCount = prefixes.Length(); |
232 | 0 | uint32_t* prefixArray = static_cast<uint32_t*>(moz_xmalloc(itemCount * sizeof(uint32_t))); |
233 | 0 |
|
234 | 0 | memcpy(prefixArray, prefixes.Elements(), sizeof(uint32_t) * itemCount); |
235 | 0 |
|
236 | 0 | *aCount = itemCount; |
237 | 0 | *aPrefixes = prefixArray; |
238 | 0 |
|
239 | 0 | return NS_OK; |
240 | 0 | } |
241 | | |
242 | | uint32_t |
243 | | nsUrlClassifierPrefixSet::BinSearch(uint32_t start, uint32_t end, |
244 | | uint32_t target) const |
245 | 0 | { |
246 | 0 | mLock.AssertCurrentThreadOwns(); |
247 | 0 |
|
248 | 0 | while (start != end && end >= start) { |
249 | 0 | uint32_t i = start + ((end - start) >> 1); |
250 | 0 | uint32_t value = mIndexPrefixes[i]; |
251 | 0 | if (value < target) { |
252 | 0 | start = i + 1; |
253 | 0 | } else if (value > target) { |
254 | 0 | end = i - 1; |
255 | 0 | } else { |
256 | 0 | return i; |
257 | 0 | } |
258 | 0 | } |
259 | 0 | return end; |
260 | 0 | } |
261 | | |
262 | | NS_IMETHODIMP |
263 | | nsUrlClassifierPrefixSet::Contains(uint32_t aPrefix, bool* aFound) |
264 | 0 | { |
265 | 0 | MutexAutoLock lock(mLock); |
266 | 0 |
|
267 | 0 | *aFound = false; |
268 | 0 |
|
269 | 0 | if (IsEmptyInternal()) { |
270 | 0 | return NS_OK; |
271 | 0 | } |
272 | 0 | |
273 | 0 | uint32_t target = aPrefix; |
274 | 0 |
|
275 | 0 | // We want to do a "Price is Right" binary search, that is, we want to find |
276 | 0 | // the index of the value either equal to the target or the closest value |
277 | 0 | // that is less than the target. |
278 | 0 | // |
279 | 0 | if (target < mIndexPrefixes[0]) { |
280 | 0 | return NS_OK; |
281 | 0 | } |
282 | 0 | |
283 | 0 | // |binsearch| does not necessarily return the correct index (when the |
284 | 0 | // target is not found) but rather it returns an index at least one away |
285 | 0 | // from the correct index. |
286 | 0 | // Because of this, we need to check if the target lies before the beginning |
287 | 0 | // of the indices. |
288 | 0 | |
289 | 0 | uint32_t i = BinSearch(0, mIndexPrefixes.Length() - 1, target); |
290 | 0 | if (mIndexPrefixes[i] > target && i > 0) { |
291 | 0 | i--; |
292 | 0 | } |
293 | 0 |
|
294 | 0 | // Now search through the deltas for the target. |
295 | 0 | uint32_t diff = target - mIndexPrefixes[i]; |
296 | 0 | uint32_t deltaSize = mIndexDeltas[i].Length(); |
297 | 0 | uint32_t deltaIndex = 0; |
298 | 0 |
|
299 | 0 | while (diff > 0 && deltaIndex < deltaSize) { |
300 | 0 | diff -= mIndexDeltas[i][deltaIndex]; |
301 | 0 | deltaIndex++; |
302 | 0 | } |
303 | 0 |
|
304 | 0 | if (diff == 0) { |
305 | 0 | *aFound = true; |
306 | 0 | } |
307 | 0 |
|
308 | 0 | return NS_OK; |
309 | 0 | } |
310 | | |
311 | | MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf) |
312 | | |
313 | | NS_IMETHODIMP |
314 | | nsUrlClassifierPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport, |
315 | | nsISupports* aData, bool aAnonymize) |
316 | 0 | { |
317 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
318 | 0 |
|
319 | 0 | // No need to get mLock here because this function does not directly touch |
320 | 0 | // the class's data members. (SizeOfIncludingThis() will get mLock, however.) |
321 | 0 |
|
322 | 0 | aHandleReport->Callback( |
323 | 0 | EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES, |
324 | 0 | SizeOfIncludingThis(UrlClassifierMallocSizeOf), |
325 | 0 | NS_LITERAL_CSTRING("Memory used by the prefix set for a URL classifier."), |
326 | 0 | aData); |
327 | 0 |
|
328 | 0 | return NS_OK; |
329 | 0 | } |
330 | | |
331 | | size_t |
332 | | nsUrlClassifierPrefixSet::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const |
333 | 0 | { |
334 | 0 | MutexAutoLock lock(mLock); |
335 | 0 |
|
336 | 0 | size_t n = 0; |
337 | 0 | n += aMallocSizeOf(this); |
338 | 0 | n += mIndexDeltas.ShallowSizeOfExcludingThis(aMallocSizeOf); |
339 | 0 | for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) { |
340 | 0 | n += mIndexDeltas[i].ShallowSizeOfExcludingThis(aMallocSizeOf); |
341 | 0 | } |
342 | 0 | n += mIndexPrefixes.ShallowSizeOfExcludingThis(aMallocSizeOf); |
343 | 0 | return n; |
344 | 0 | } |
345 | | |
346 | | bool |
347 | | nsUrlClassifierPrefixSet::IsEmptyInternal() const |
348 | 0 | { |
349 | 0 | if (mIndexPrefixes.IsEmpty()) { |
350 | 0 | MOZ_ASSERT(mIndexDeltas.IsEmpty() && mTotalPrefixes == 0, |
351 | 0 | "If we're empty, there should be no leftovers."); |
352 | 0 | return true; |
353 | 0 | } |
354 | 0 |
|
355 | 0 | MOZ_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length()); |
356 | 0 | return false; |
357 | 0 | } |
358 | | |
359 | | NS_IMETHODIMP |
360 | | nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty) |
361 | 0 | { |
362 | 0 | MutexAutoLock lock(mLock); |
363 | 0 |
|
364 | 0 | *aEmpty = IsEmptyInternal(); |
365 | 0 | return NS_OK; |
366 | 0 | } |
367 | | |
368 | | NS_IMETHODIMP |
369 | | nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile) |
370 | 0 | { |
371 | 0 | MutexAutoLock lock(mLock); |
372 | 0 |
|
373 | 0 | Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> timer; |
374 | 0 |
|
375 | 0 | nsCOMPtr<nsIInputStream> localInFile; |
376 | 0 | nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(localInFile), aFile, |
377 | 0 | PR_RDONLY | nsIFile::OS_READAHEAD); |
378 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
379 | 0 |
|
380 | 0 | // Calculate how big the file is, make sure our read buffer isn't bigger |
381 | 0 | // than the file itself which is just wasting memory. |
382 | 0 | int64_t fileSize; |
383 | 0 | rv = aFile->GetFileSize(&fileSize); |
384 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
385 | 0 |
|
386 | 0 | if (fileSize < 0 || fileSize > UINT32_MAX) { |
387 | 0 | return NS_ERROR_FAILURE; |
388 | 0 | } |
389 | 0 | |
390 | 0 | uint32_t bufferSize = std::min<uint32_t>(static_cast<uint32_t>(fileSize), |
391 | 0 | MAX_BUFFER_SIZE); |
392 | 0 |
|
393 | 0 | // Convert to buffered stream |
394 | 0 | nsCOMPtr<nsIInputStream> in; |
395 | 0 | rv = NS_NewBufferedInputStream(getter_AddRefs(in), localInFile.forget(), |
396 | 0 | bufferSize); |
397 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
398 | 0 |
|
399 | 0 | rv = LoadPrefixes(in); |
400 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
401 | 0 |
|
402 | 0 | return NS_OK; |
403 | 0 | } |
404 | | |
405 | | NS_IMETHODIMP |
406 | | nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile) |
407 | 0 | { |
408 | 0 | MutexAutoLock lock(mLock); |
409 | 0 |
|
410 | 0 | nsCOMPtr<nsIOutputStream> localOutFile; |
411 | 0 | nsresult rv = NS_NewLocalFileOutputStream(getter_AddRefs(localOutFile), aFile, |
412 | 0 | PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE); |
413 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
414 | 0 |
|
415 | 0 | uint32_t fileSize; |
416 | 0 |
|
417 | 0 | // Preallocate the file storage |
418 | 0 | { |
419 | 0 | nsCOMPtr<nsIFileOutputStream> fos(do_QueryInterface(localOutFile)); |
420 | 0 | Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_FALLOCATE_TIME> timer; |
421 | 0 |
|
422 | 0 | fileSize = CalculatePreallocateSize(); |
423 | 0 |
|
424 | 0 | // Ignore failure, the preallocation is a hint and we write out the entire |
425 | 0 | // file later on |
426 | 0 | Unused << fos->Preallocate(fileSize); |
427 | 0 | } |
428 | 0 |
|
429 | 0 | // Convert to buffered stream |
430 | 0 | nsCOMPtr<nsIOutputStream> out; |
431 | 0 | rv = NS_NewBufferedOutputStream(getter_AddRefs(out), localOutFile.forget(), |
432 | 0 | std::min(fileSize, MAX_BUFFER_SIZE)); |
433 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
434 | 0 |
|
435 | 0 | rv = WritePrefixes(out); |
436 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
437 | 0 |
|
438 | 0 | LOG(("[%s] Storing PrefixSet successful", mName.get())); |
439 | 0 |
|
440 | 0 | return NS_OK; |
441 | 0 | } |
442 | | |
443 | | nsresult |
444 | | nsUrlClassifierPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) |
445 | 0 | { |
446 | 0 | mCanary.Check(); |
447 | 0 |
|
448 | 0 | Clear(); |
449 | 0 |
|
450 | 0 | uint32_t magic; |
451 | 0 | uint32_t read; |
452 | 0 |
|
453 | 0 | nsresult rv = in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read); |
454 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
455 | 0 | NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE); |
456 | 0 |
|
457 | 0 | if (magic == PREFIXSET_VERSION_MAGIC) { |
458 | 0 | uint32_t indexSize; |
459 | 0 | uint32_t deltaSize; |
460 | 0 |
|
461 | 0 | rv = in->Read(reinterpret_cast<char*>(&indexSize), sizeof(uint32_t), &read); |
462 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
463 | 0 | NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE); |
464 | 0 |
|
465 | 0 | rv = in->Read(reinterpret_cast<char*>(&deltaSize), sizeof(uint32_t), &read); |
466 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
467 | 0 | NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE); |
468 | 0 |
|
469 | 0 | if (indexSize == 0) { |
470 | 0 | LOG(("[%s] Stored PrefixSet is empty!", mName.get())); |
471 | 0 | return NS_OK; |
472 | 0 | } |
473 | 0 |
|
474 | 0 | if (deltaSize > (indexSize * DELTAS_LIMIT)) { |
475 | 0 | return NS_ERROR_FILE_CORRUPTED; |
476 | 0 | } |
477 | 0 | |
478 | 0 | nsTArray<uint32_t> indexStarts; |
479 | 0 | if (!indexStarts.SetLength(indexSize, fallible) || |
480 | 0 | !mIndexPrefixes.SetLength(indexSize, fallible) || |
481 | 0 | !mIndexDeltas.SetLength(indexSize, fallible)) { |
482 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
483 | 0 | } |
484 | 0 | |
485 | 0 | mTotalPrefixes = indexSize; |
486 | 0 |
|
487 | 0 | uint32_t toRead = indexSize*sizeof(uint32_t); |
488 | 0 | rv = in->Read(reinterpret_cast<char*>(mIndexPrefixes.Elements()), toRead, &read); |
489 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
490 | 0 | NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE); |
491 | 0 |
|
492 | 0 | rv = in->Read(reinterpret_cast<char*>(indexStarts.Elements()), toRead, &read); |
493 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
494 | 0 | NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE); |
495 | 0 |
|
496 | 0 | if (indexSize != 0 && indexStarts[0] != 0) { |
497 | 0 | return NS_ERROR_FILE_CORRUPTED; |
498 | 0 | } |
499 | 0 | for (uint32_t i = 0; i < indexSize; i++) { |
500 | 0 | uint32_t numInDelta = i == indexSize - 1 ? deltaSize - indexStarts[i] |
501 | 0 | : indexStarts[i + 1] - indexStarts[i]; |
502 | 0 | if (numInDelta > DELTAS_LIMIT) { |
503 | 0 | return NS_ERROR_FILE_CORRUPTED; |
504 | 0 | } |
505 | 0 | if (numInDelta > 0) { |
506 | 0 | if (!mIndexDeltas[i].SetLength(numInDelta, fallible)) { |
507 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
508 | 0 | } |
509 | 0 | mTotalPrefixes += numInDelta; |
510 | 0 | toRead = numInDelta * sizeof(uint16_t); |
511 | 0 | rv = in->Read(reinterpret_cast<char*>(mIndexDeltas[i].Elements()), toRead, &read); |
512 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
513 | 0 | NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE); |
514 | 0 | } |
515 | 0 | } |
516 | 0 | } else { |
517 | 0 | LOG(("[%s] Version magic mismatch, not loading", mName.get())); |
518 | 0 | return NS_ERROR_FILE_CORRUPTED; |
519 | 0 | } |
520 | 0 |
|
521 | 0 | MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length()); |
522 | 0 | LOG(("[%s] Loading PrefixSet successful", mName.get())); |
523 | 0 |
|
524 | 0 | return NS_OK; |
525 | 0 | } |
526 | | |
527 | | uint32_t |
528 | | nsUrlClassifierPrefixSet::CalculatePreallocateSize() const |
529 | 0 | { |
530 | 0 | uint32_t fileSize = 4 * sizeof(uint32_t); |
531 | 0 | MOZ_RELEASE_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length()); |
532 | 0 | uint32_t deltas = mTotalPrefixes - mIndexPrefixes.Length(); |
533 | 0 | fileSize += 2 * mIndexPrefixes.Length() * sizeof(uint32_t); |
534 | 0 | fileSize += deltas * sizeof(uint16_t); |
535 | 0 | return fileSize; |
536 | 0 | } |
537 | | |
538 | | nsresult |
539 | | nsUrlClassifierPrefixSet::WritePrefixes(nsCOMPtr<nsIOutputStream>& out) const |
540 | 0 | { |
541 | 0 | mCanary.Check(); |
542 | 0 |
|
543 | 0 | // In Bug 1362761, crashes happened while reading mIndexDeltas[i]. |
544 | 0 | // We suspect that this is due to memory corruption so to test this |
545 | 0 | // hypothesis, we will crash the browser. Once we have established |
546 | 0 | // memory corruption as the root cause, we can attempt to gracefully |
547 | 0 | // handle this. |
548 | 0 | uint32_t checksum; |
549 | 0 | CalculateTArrayChecksum(mIndexDeltas, &checksum); |
550 | 0 | if (checksum != mIndexDeltasChecksum) { |
551 | 0 | LOG(("[%s] The contents of mIndexDeltas doesn't match the checksum!", mName.get())); |
552 | 0 | MOZ_CRASH("Memory corruption detected in mIndexDeltas."); |
553 | 0 | } |
554 | 0 |
|
555 | 0 | uint32_t written; |
556 | 0 | uint32_t writelen = sizeof(uint32_t); |
557 | 0 | const uint32_t magic = PREFIXSET_VERSION_MAGIC; |
558 | 0 | nsresult rv = out->Write(reinterpret_cast<const char*>(&magic), writelen, &written); |
559 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
560 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
561 | 0 |
|
562 | 0 | const uint32_t indexSize = mIndexPrefixes.Length(); |
563 | 0 | if (NS_WARN_IF(mIndexDeltas.Length() != indexSize)) { |
564 | 0 | LOG(("[%s] mIndexPrefixes doesn't have the same length as mIndexDeltas", |
565 | 0 | mName.get())); |
566 | 0 | return NS_ERROR_FAILURE; |
567 | 0 | } |
568 | 0 | uint32_t totalDeltas = 0; |
569 | 0 |
|
570 | 0 | // Store the shape of mIndexDeltas by noting at which "count" of total |
571 | 0 | // indexes a new subarray starts. This is slightly cumbersome but keeps |
572 | 0 | // file format compatibility. |
573 | 0 | // If we ever update the format, we can gain space by storing the delta |
574 | 0 | // subarray sizes, which fit in bytes. |
575 | 0 | nsTArray<uint32_t> indexStarts; |
576 | 0 | if (!indexStarts.SetCapacity(indexSize + 1, fallible)) { |
577 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
578 | 0 | } |
579 | 0 | indexStarts.AppendElement(0); |
580 | 0 |
|
581 | 0 | for (uint32_t i = 0; i < indexSize; i++) { |
582 | 0 | uint32_t deltaLength = mIndexDeltas[i].Length(); |
583 | 0 | totalDeltas += deltaLength; |
584 | 0 | indexStarts.AppendElement(totalDeltas); |
585 | 0 | } |
586 | 0 | indexStarts.RemoveElementAt(indexSize); // we don't use the last element |
587 | 0 | MOZ_ASSERT(indexStarts.Length() == indexSize); |
588 | 0 |
|
589 | 0 | rv = out->Write(reinterpret_cast<const char*>(&indexSize), writelen, &written); |
590 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
591 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
592 | 0 |
|
593 | 0 | rv = out->Write(reinterpret_cast<const char*>(&totalDeltas), writelen, &written); |
594 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
595 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
596 | 0 |
|
597 | 0 | writelen = indexSize * sizeof(uint32_t); |
598 | 0 | rv = out->Write(reinterpret_cast<const char*>(mIndexPrefixes.Elements()), writelen, &written); |
599 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
600 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
601 | 0 |
|
602 | 0 | rv = out->Write(reinterpret_cast<const char*>(indexStarts.Elements()), writelen, &written); |
603 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
604 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
605 | 0 |
|
606 | 0 | if (totalDeltas > 0) { |
607 | 0 | for (uint32_t i = 0; i < indexSize; i++) { |
608 | 0 | writelen = mIndexDeltas[i].Length() * sizeof(uint16_t); |
609 | 0 | rv = out->Write(reinterpret_cast<const char*>(mIndexDeltas[i].Elements()), writelen, &written); |
610 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
611 | 0 | NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE); |
612 | 0 | } |
613 | 0 | } |
614 | 0 |
|
615 | 0 | LOG(("[%s] Writing PrefixSet successful", mName.get())); |
616 | 0 |
|
617 | 0 | return NS_OK; |
618 | 0 | } |