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 "nsCOMPtr.h"
10 #include "nsDebug.h"
11 #include "nsIInputStream.h"
12 #include "nsIOutputStream.h"
13 #include "nsPrintfCString.h"
14 #include "nsTArray.h"
15 #include "nsString.h"
16 #include "nsTArray.h"
17 #include "nsThreadUtils.h"
18 #include "nsNetUtil.h"
19 #include "mozilla/MemoryReporting.h"
20 #include "mozilla/StaticPrefs_browser.h"
21 #include "mozilla/Telemetry.h"
22 #include "mozilla/Logging.h"
23 #include "mozilla/Unused.h"
24 #include <algorithm>
25 
26 using namespace mozilla;
27 
28 // MOZ_LOG=UrlClassifierPrefixSet:5
29 static LazyLogModule gUrlClassifierPrefixSetLog("UrlClassifierPrefixSet");
30 #define LOG(args) \
31   MOZ_LOG(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug, args)
32 #define LOG_ENABLED() \
33   MOZ_LOG_TEST(gUrlClassifierPrefixSetLog, mozilla::LogLevel::Debug)
34 
NS_IMPL_ISUPPORTS(nsUrlClassifierPrefixSet,nsIUrlClassifierPrefixSet,nsIMemoryReporter)35 NS_IMPL_ISUPPORTS(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet,
36                   nsIMemoryReporter)
37 
38 nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
39     : mLock("nsUrlClassifierPrefixSet.mLock"), mTotalPrefixes(0) {}
40 
41 NS_IMETHODIMP
Init(const nsACString & aName)42 nsUrlClassifierPrefixSet::Init(const nsACString& aName) {
43   mName = aName;
44   mMemoryReportPath = nsPrintfCString(
45       "explicit/storage/prefix-set/%s",
46       (!aName.IsEmpty() ? PromiseFlatCString(aName).get() : "?!"));
47 
48   RegisterWeakMemoryReporter(this);
49 
50   return NS_OK;
51 }
52 
~nsUrlClassifierPrefixSet()53 nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() {
54   UnregisterWeakMemoryReporter(this);
55 }
56 
Clear()57 void nsUrlClassifierPrefixSet::Clear() {
58   LOG(("[%s] Clearing PrefixSet", mName.get()));
59   mIndexDeltas.Clear();
60   mIndexPrefixes.Clear();
61   mTotalPrefixes = 0;
62 }
63 
64 NS_IMETHODIMP
SetPrefixes(const uint32_t * aArray,uint32_t aLength)65 nsUrlClassifierPrefixSet::SetPrefixes(const uint32_t* aArray,
66                                       uint32_t aLength) {
67   MutexAutoLock lock(mLock);
68 
69   nsresult rv = NS_OK;
70   Clear();
71 
72   if (aLength > 0) {
73     rv = MakePrefixSet(aArray, aLength);
74     if (NS_WARN_IF(NS_FAILED(rv))) {
75       Clear();  // clear out any leftovers
76     }
77   }
78 
79   MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
80                 mIndexPrefixes.Length() == mTotalPrefixes);
81   MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
82                 mIndexPrefixes.Length() == mIndexDeltas.Length());
83   return rv;
84 }
85 
MakePrefixSet(const uint32_t * aPrefixes,uint32_t aLength)86 nsresult nsUrlClassifierPrefixSet::MakePrefixSet(const uint32_t* aPrefixes,
87                                                  uint32_t aLength) {
88   mLock.AssertCurrentThreadOwns();
89 
90   MOZ_ASSERT(aPrefixes);
91   MOZ_ASSERT(aLength > 0);
92 
93 #ifdef DEBUG
94   for (uint32_t i = 1; i < aLength; i++) {
95     MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i - 1]);
96   }
97 #endif
98 
99   uint32_t totalDeltas = 0;
100 
101   // Request one memory space to store all the prefixes may lead to
102   // memory allocation failure on certain platforms(See Bug 1046038).
103   // So if required size to store all the prefixes exceeds defined
104   // threshold(512k), we divide prefixes into delta chunks instead. Note that
105   // the potential overhead of this approach is that it may reuqire more memory
106   // compared to store all prefixes in one array because of jemalloc's
107   // implementation.
108   if (aLength * sizeof(uint32_t) <
109       StaticPrefs::browser_safebrowsing_prefixset_max_array_size()) {
110     // Not over the threshold, store all prefixes into mIndexPrefixes.
111     // mIndexDeltas is empty in this case.
112     mIndexPrefixes.SetCapacity(aLength);
113     for (uint32_t i = 0; i < aLength; i++) {
114       mIndexPrefixes.AppendElement(aPrefixes[i]);
115     }
116   } else {
117     // Apply delta algorithm to split prefixes into smaller delta chunk.
118 
119     // We estimate the capacity of mIndexPrefixes & mIndexDeltas by assuming
120     // each element in mIndexDeltas stores DELTAS_LIMITS deltas, so the
121     // number of indexed prefixes is round up of
122     // TotalPrefixes / (DELTA_LIMIT + 1)
123     // The estimation only works when the number of prefixes are over a
124     // certain limit, which means, arrays in mIndexDeltas are always full.
125     uint32_t estimateCapacity =
126         (aLength + (DELTAS_LIMIT + 1) - 1) / (DELTAS_LIMIT + 1);
127     mIndexPrefixes.SetCapacity(estimateCapacity);
128     mIndexDeltas.SetCapacity(estimateCapacity);
129 
130     mIndexPrefixes.AppendElement(aPrefixes[0]);
131     mIndexDeltas.AppendElement();
132     mIndexDeltas.LastElement().SetCapacity(DELTAS_LIMIT);
133 
134     uint32_t numOfDeltas = 0;
135     uint32_t previousItem = aPrefixes[0];
136     for (uint32_t i = 1; i < aLength; i++) {
137       if ((numOfDeltas >= DELTAS_LIMIT) ||
138           (aPrefixes[i] - previousItem >= MAX_INDEX_DIFF)) {
139         // Compact the previous element.
140         // Note there is always at least one element when we get here,
141         // because we created the first element before the loop.
142         mIndexDeltas.LastElement().Compact();
143         if (!mIndexDeltas.AppendElement(fallible)) {
144           return NS_ERROR_OUT_OF_MEMORY;
145         }
146         mIndexDeltas.LastElement().SetCapacity(DELTAS_LIMIT);
147 
148         if (!mIndexPrefixes.AppendElement(aPrefixes[i], fallible)) {
149           return NS_ERROR_OUT_OF_MEMORY;
150         }
151 
152         numOfDeltas = 0;
153       } else {
154         uint16_t delta = aPrefixes[i] - previousItem;
155         if (!mIndexDeltas.LastElement().AppendElement(delta, fallible)) {
156           return NS_ERROR_OUT_OF_MEMORY;
157         }
158 
159         numOfDeltas++;
160         totalDeltas++;
161       }
162       previousItem = aPrefixes[i];
163     }
164 
165     mIndexDeltas.LastElement().Compact();
166     mIndexDeltas.Compact();
167     mIndexPrefixes.Compact();
168 
169     MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
170   }
171 
172   if (totalDeltas == 0) {
173     // We have to clear mIndexDeltas here because it is still possible
174     // that the delta generation algorithm produces no deltas at all. When that
175     // happens, mIndexDeltas is not empty, which conflicts with the assumption
176     // that when there is no delta, mIndexDeltas is empty.
177     mIndexDeltas.Clear();
178   }
179   mTotalPrefixes = aLength;
180 
181   LOG(("Total number of indices: %d", aLength));
182   LOG(("Total number of deltas: %d", totalDeltas));
183   LOG(("Total number of delta chunks: %zu", mIndexDeltas.Length()));
184 
185   return NS_OK;
186 }
187 
GetPrefixesNative(FallibleTArray<uint32_t> & outArray)188 nsresult nsUrlClassifierPrefixSet::GetPrefixesNative(
189     FallibleTArray<uint32_t>& outArray) {
190   MutexAutoLock lock(mLock);
191 
192   if (!outArray.SetLength(mTotalPrefixes, fallible)) {
193     return NS_ERROR_OUT_OF_MEMORY;
194   }
195 
196   uint32_t prefixIdxLength = mIndexPrefixes.Length();
197   uint32_t prefixCnt = 0;
198 
199   for (uint32_t i = 0; i < prefixIdxLength; i++) {
200     uint32_t prefix = mIndexPrefixes[i];
201 
202     if (prefixCnt >= mTotalPrefixes) {
203       return NS_ERROR_FAILURE;
204     }
205     outArray[prefixCnt++] = prefix;
206 
207     if (mIndexDeltas.IsEmpty()) {
208       continue;
209     }
210 
211     for (uint32_t j = 0; j < mIndexDeltas[i].Length(); j++) {
212       prefix += mIndexDeltas[i][j];
213       if (prefixCnt >= mTotalPrefixes) {
214         return NS_ERROR_FAILURE;
215       }
216       outArray[prefixCnt++] = prefix;
217     }
218   }
219 
220   NS_ASSERTION(mTotalPrefixes == prefixCnt, "Lengths are inconsistent");
221   return NS_OK;
222 }
223 
224 NS_IMETHODIMP
GetPrefixes(uint32_t * aCount,uint32_t ** aPrefixes)225 nsUrlClassifierPrefixSet::GetPrefixes(uint32_t* aCount, uint32_t** aPrefixes) {
226   // No need to get mLock here because this function does not directly touch
227   // the class's data members. (GetPrefixesNative() will get mLock, however.)
228 
229   NS_ENSURE_ARG_POINTER(aCount);
230   *aCount = 0;
231   NS_ENSURE_ARG_POINTER(aPrefixes);
232   *aPrefixes = nullptr;
233 
234   FallibleTArray<uint32_t> prefixes;
235   nsresult rv = GetPrefixesNative(prefixes);
236   if (NS_FAILED(rv)) {
237     return rv;
238   }
239 
240   uint64_t itemCount = prefixes.Length();
241   uint32_t* prefixArray =
242       static_cast<uint32_t*>(moz_xmalloc(itemCount * sizeof(uint32_t)));
243 
244   memcpy(prefixArray, prefixes.Elements(), sizeof(uint32_t) * itemCount);
245 
246   *aCount = itemCount;
247   *aPrefixes = prefixArray;
248 
249   return NS_OK;
250 }
251 
BinSearch(uint32_t start,uint32_t end,uint32_t target) const252 uint32_t nsUrlClassifierPrefixSet::BinSearch(uint32_t start, uint32_t end,
253                                              uint32_t target) const {
254   mLock.AssertCurrentThreadOwns();
255 
256   while (start != end && end >= start) {
257     uint32_t i = start + ((end - start) >> 1);
258     uint32_t value = mIndexPrefixes[i];
259     if (value < target) {
260       start = i + 1;
261     } else if (value > target) {
262       end = i - 1;
263     } else {
264       return i;
265     }
266   }
267   return end;
268 }
269 
270 NS_IMETHODIMP
Contains(uint32_t aPrefix,bool * aFound)271 nsUrlClassifierPrefixSet::Contains(uint32_t aPrefix, bool* aFound) {
272   MutexAutoLock lock(mLock);
273 
274   *aFound = false;
275 
276   if (IsEmptyInternal()) {
277     return NS_OK;
278   }
279 
280   uint32_t target = aPrefix;
281 
282   // We want to do a "Price is Right" binary search, that is, we want to find
283   // the index of the value either equal to the target or the closest value
284   // that is less than the target.
285   //
286   if (target < mIndexPrefixes[0]) {
287     return NS_OK;
288   }
289 
290   // |binsearch| does not necessarily return the correct index (when the
291   // target is not found) but rather it returns an index at least one away
292   // from the correct index.
293   // Because of this, we need to check if the target lies before the beginning
294   // of the indices.
295 
296   uint32_t i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
297   if (mIndexPrefixes[i] > target && i > 0) {
298     i--;
299   }
300 
301   // Now search through the deltas for the target.
302   uint32_t diff = target - mIndexPrefixes[i];
303 
304   if (!mIndexDeltas.IsEmpty()) {
305     uint32_t deltaSize = mIndexDeltas[i].Length();
306     uint32_t deltaIndex = 0;
307 
308     while (diff > 0 && deltaIndex < deltaSize) {
309       diff -= mIndexDeltas[i][deltaIndex];
310       deltaIndex++;
311     }
312   }
313 
314   if (diff == 0) {
315     *aFound = true;
316   }
317 
318   return NS_OK;
319 }
320 
MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)321 MOZ_DEFINE_MALLOC_SIZE_OF(UrlClassifierMallocSizeOf)
322 
323 NS_IMETHODIMP
324 nsUrlClassifierPrefixSet::CollectReports(nsIHandleReportCallback* aHandleReport,
325                                          nsISupports* aData, bool aAnonymize) {
326   MOZ_ASSERT(NS_IsMainThread());
327 
328   // No need to get mLock here because this function does not directly touch
329   // the class's data members. (SizeOfIncludingThis() will get mLock, however.)
330 
331   aHandleReport->Callback(
332       EmptyCString(), mMemoryReportPath, KIND_HEAP, UNITS_BYTES,
333       SizeOfIncludingThis(UrlClassifierMallocSizeOf),
334       NS_LITERAL_CSTRING("Memory used by the prefix set for a URL classifier."),
335       aData);
336 
337   return NS_OK;
338 }
339 
SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const340 size_t nsUrlClassifierPrefixSet::SizeOfIncludingThis(
341     mozilla::MallocSizeOf aMallocSizeOf) const {
342   MutexAutoLock lock(mLock);
343 
344   size_t n = 0;
345   n += aMallocSizeOf(this);
346   n += mIndexDeltas.ShallowSizeOfExcludingThis(aMallocSizeOf);
347   for (uint32_t i = 0; i < mIndexDeltas.Length(); i++) {
348     n += mIndexDeltas[i].ShallowSizeOfExcludingThis(aMallocSizeOf);
349   }
350   n += mIndexPrefixes.ShallowSizeOfExcludingThis(aMallocSizeOf);
351   return n;
352 }
353 
IsEmptyInternal() const354 bool nsUrlClassifierPrefixSet::IsEmptyInternal() const {
355   if (mIndexPrefixes.IsEmpty()) {
356     MOZ_ASSERT(mIndexDeltas.IsEmpty() && mTotalPrefixes == 0,
357                "If we're empty, there should be no leftovers.");
358     return true;
359   }
360 
361   MOZ_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length());
362   return false;
363 }
364 
365 NS_IMETHODIMP
IsEmpty(bool * aEmpty)366 nsUrlClassifierPrefixSet::IsEmpty(bool* aEmpty) {
367   MutexAutoLock lock(mLock);
368 
369   *aEmpty = IsEmptyInternal();
370   return NS_OK;
371 }
372 
LoadPrefixes(nsCOMPtr<nsIInputStream> & in)373 nsresult nsUrlClassifierPrefixSet::LoadPrefixes(nsCOMPtr<nsIInputStream>& in) {
374   MutexAutoLock lock(mLock);
375 
376   mCanary.Check();
377   Clear();
378 
379   uint32_t magic;
380   uint32_t read;
381 
382   nsresult rv =
383       in->Read(reinterpret_cast<char*>(&magic), sizeof(uint32_t), &read);
384   NS_ENSURE_SUCCESS(rv, rv);
385   NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
386 
387   if (magic == PREFIXSET_VERSION_MAGIC) {
388     // Read the number of indexed prefixes
389     uint32_t indexSize;
390     rv = in->Read(reinterpret_cast<char*>(&indexSize), sizeof(uint32_t), &read);
391     NS_ENSURE_SUCCESS(rv, rv);
392     NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
393 
394     // Read the number of delta prefixes
395     uint32_t deltaSize;
396     rv = in->Read(reinterpret_cast<char*>(&deltaSize), sizeof(uint32_t), &read);
397     NS_ENSURE_SUCCESS(rv, rv);
398     NS_ENSURE_TRUE(read == sizeof(uint32_t), NS_ERROR_FAILURE);
399 
400     if (indexSize == 0) {
401       LOG(("[%s] Stored PrefixSet is empty!", mName.get()));
402       return NS_OK;
403     }
404 
405     if (!mIndexPrefixes.SetLength(indexSize, fallible)) {
406       return NS_ERROR_OUT_OF_MEMORY;
407     }
408 
409     mTotalPrefixes = indexSize;
410     if (deltaSize > (indexSize * DELTAS_LIMIT)) {
411       return NS_ERROR_FILE_CORRUPTED;
412     }
413 
414     // Read index prefixes
415     uint32_t toRead = indexSize * sizeof(uint32_t);
416     rv = in->Read(reinterpret_cast<char*>(mIndexPrefixes.Elements()), toRead,
417                   &read);
418     NS_ENSURE_SUCCESS(rv, rv);
419     NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
420 
421     if (deltaSize) {
422       nsTArray<uint32_t> indexStarts;
423 
424       if (!indexStarts.SetLength(indexSize, fallible) ||
425           !mIndexDeltas.SetLength(indexSize, fallible)) {
426         return NS_ERROR_OUT_OF_MEMORY;
427       }
428 
429       // Read index start array to construct mIndexDeltas
430       rv = in->Read(reinterpret_cast<char*>(indexStarts.Elements()), toRead,
431                     &read);
432       NS_ENSURE_SUCCESS(rv, rv);
433       NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
434 
435       if (indexStarts[0] != 0) {
436         return NS_ERROR_FILE_CORRUPTED;
437       }
438 
439       for (uint32_t i = 0; i < indexSize; i++) {
440         uint32_t numInDelta = i == indexSize - 1
441                                   ? deltaSize - indexStarts[i]
442                                   : indexStarts[i + 1] - indexStarts[i];
443         if (numInDelta > DELTAS_LIMIT) {
444           return NS_ERROR_FILE_CORRUPTED;
445         }
446         if (numInDelta > 0) {
447           if (!mIndexDeltas[i].SetLength(numInDelta, fallible)) {
448             return NS_ERROR_OUT_OF_MEMORY;
449           }
450           mTotalPrefixes += numInDelta;
451           toRead = numInDelta * sizeof(uint16_t);
452           rv = in->Read(reinterpret_cast<char*>(mIndexDeltas[i].Elements()),
453                         toRead, &read);
454           NS_ENSURE_SUCCESS(rv, rv);
455           NS_ENSURE_TRUE(read == toRead, NS_ERROR_FAILURE);
456         }
457       }
458     } else {
459       mIndexDeltas.Clear();
460     }
461   } else {
462     LOG(("[%s] Version magic mismatch, not loading", mName.get()));
463     return NS_ERROR_FILE_CORRUPTED;
464   }
465 
466   MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
467                 mIndexPrefixes.Length() == mTotalPrefixes);
468   MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
469                 mIndexPrefixes.Length() == mIndexDeltas.Length());
470   LOG(("[%s] Loading PrefixSet successful (%u total prefixes)", mName.get(),
471        mTotalPrefixes));
472 
473   return NS_OK;
474 }
475 
CalculatePreallocateSize() const476 uint32_t nsUrlClassifierPrefixSet::CalculatePreallocateSize() const {
477   uint32_t fileSize = 4 * sizeof(uint32_t);
478   MOZ_RELEASE_ASSERT(mTotalPrefixes >= mIndexPrefixes.Length());
479   uint32_t deltas = mTotalPrefixes - mIndexPrefixes.Length();
480   fileSize += mIndexPrefixes.Length() * sizeof(uint32_t);
481   if (deltas) {
482     MOZ_ASSERT(mIndexPrefixes.Length() == mIndexDeltas.Length());
483 
484     fileSize += mIndexPrefixes.Length() * sizeof(uint32_t);
485     fileSize += mIndexDeltas.Length() * sizeof(uint32_t);
486     fileSize += deltas * sizeof(uint16_t);
487   }
488   return fileSize;
489 }
490 
WritePrefixes(nsCOMPtr<nsIOutputStream> & out) const491 nsresult nsUrlClassifierPrefixSet::WritePrefixes(
492     nsCOMPtr<nsIOutputStream>& out) const {
493   MutexAutoLock lock(mLock);
494 
495   MOZ_ASSERT_IF(mIndexDeltas.IsEmpty(),
496                 mIndexPrefixes.Length() == mTotalPrefixes);
497   MOZ_ASSERT_IF(!mIndexDeltas.IsEmpty(),
498                 mIndexPrefixes.Length() == mIndexDeltas.Length());
499 
500   mCanary.Check();
501 
502   uint32_t written;
503   uint32_t writelen = sizeof(uint32_t);
504   const uint32_t magic = PREFIXSET_VERSION_MAGIC;
505   nsresult rv =
506       out->Write(reinterpret_cast<const char*>(&magic), writelen, &written);
507   NS_ENSURE_SUCCESS(rv, rv);
508   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
509 
510   const uint32_t indexSize = mIndexPrefixes.Length();
511   if (mIndexDeltas.IsEmpty()) {
512     if (NS_WARN_IF(mTotalPrefixes != indexSize)) {
513       LOG(("[%s] mIndexPrefixes doesn't have the same length as mTotalPrefixes",
514            mName.get()));
515       return NS_ERROR_FAILURE;
516     }
517   } else {
518     if (NS_WARN_IF(mIndexDeltas.Length() != indexSize)) {
519       LOG(("[%s] mIndexPrefixes doesn't have the same length as mIndexDeltas",
520            mName.get()));
521       return NS_ERROR_FAILURE;
522     }
523   }
524   uint32_t totalDeltas = 0;
525 
526   // Store the shape of mIndexDeltas by noting at which "count" of total
527   // indexes a new subarray starts. This is slightly cumbersome but keeps
528   // file format compatibility.
529   // If we ever update the format, we can gain space by storing the delta
530   // subarray sizes, which fit in bytes.
531   nsTArray<uint32_t> indexStarts;
532   if (!mIndexDeltas.IsEmpty()) {
533     if (!indexStarts.SetCapacity(indexSize + 1, fallible)) {
534       return NS_ERROR_OUT_OF_MEMORY;
535     }
536     indexStarts.AppendElement(0);
537 
538     for (uint32_t i = 0; i < indexSize; i++) {
539       uint32_t deltaLength = mIndexDeltas[i].Length();
540       totalDeltas += deltaLength;
541       indexStarts.AppendElement(totalDeltas);
542     }
543     indexStarts.RemoveElementAt(indexSize);  // we don't use the last element
544     MOZ_ASSERT(indexStarts.Length() == indexSize);
545   }
546 
547   rv =
548       out->Write(reinterpret_cast<const char*>(&indexSize), writelen, &written);
549   NS_ENSURE_SUCCESS(rv, rv);
550   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
551 
552   rv = out->Write(reinterpret_cast<const char*>(&totalDeltas), writelen,
553                   &written);
554   NS_ENSURE_SUCCESS(rv, rv);
555   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
556 
557   writelen = indexSize * sizeof(uint32_t);
558   rv = out->Write(reinterpret_cast<const char*>(mIndexPrefixes.Elements()),
559                   writelen, &written);
560   NS_ENSURE_SUCCESS(rv, rv);
561   NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
562 
563   if (!mIndexDeltas.IsEmpty()) {
564     MOZ_ASSERT(!indexStarts.IsEmpty() && totalDeltas > 0);
565     rv = out->Write(reinterpret_cast<const char*>(indexStarts.Elements()),
566                     writelen, &written);
567     NS_ENSURE_SUCCESS(rv, rv);
568     NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
569 
570     for (uint32_t i = 0; i < indexSize; i++) {
571       writelen = mIndexDeltas[i].Length() * sizeof(uint16_t);
572       rv = out->Write(reinterpret_cast<const char*>(mIndexDeltas[i].Elements()),
573                       writelen, &written);
574       NS_ENSURE_SUCCESS(rv, rv);
575       NS_ENSURE_TRUE(written == writelen, NS_ERROR_FAILURE);
576     }
577   }
578 
579   LOG(("[%s] Writing PrefixSet successful", mName.get()));
580 
581   return NS_OK;
582 }
583