toolkit/components/url-classifier/HashStore.cpp

Fri, 16 Jan 2015 18:13:44 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 18:13:44 +0100
branch
TOR_BUG_9701
changeset 14
925c144e1f1f
permissions
-rw-r--r--

Integrate suggestion from review to improve consistency with existing code.

michael@0 1 //* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 // Originally based on Chrome sources:
michael@0 3 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
michael@0 4 //
michael@0 5 // Redistribution and use in source and binary forms, with or without
michael@0 6 // modification, are permitted provided that the following conditions are
michael@0 7 // met:
michael@0 8 //
michael@0 9 // * Redistributions of source code must retain the above copyright
michael@0 10 // notice, this list of conditions and the following disclaimer.
michael@0 11 // * Redistributions in binary form must reproduce the above
michael@0 12 // copyright notice, this list of conditions and the following disclaimer
michael@0 13 // in the documentation and/or other materials provided with the
michael@0 14 // distribution.
michael@0 15 // * Neither the name of Google Inc. nor the names of its
michael@0 16 // contributors may be used to endorse or promote products derived from
michael@0 17 // this software without specific prior written permission.
michael@0 18 //
michael@0 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
michael@0 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
michael@0 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
michael@0 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
michael@0 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
michael@0 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
michael@0 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
michael@0 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
michael@0 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
michael@0 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
michael@0 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
michael@0 30
michael@0 31
michael@0 32 #include "HashStore.h"
michael@0 33 #include "nsAutoPtr.h"
michael@0 34 #include "nsICryptoHash.h"
michael@0 35 #include "nsISeekableStream.h"
michael@0 36 #include "nsIStreamConverterService.h"
michael@0 37 #include "nsNetUtil.h"
michael@0 38 #include "nsCheckSummedOutputStream.h"
michael@0 39 #include "prlog.h"
michael@0 40 #include "zlib.h"
michael@0 41
michael@0 42 // Main store for SafeBrowsing protocol data. We store
michael@0 43 // known add/sub chunks, prefixes and completions in memory
michael@0 44 // during an update, and serialize to disk.
michael@0 45 // We do not store the add prefixes, those are retrieved by
michael@0 46 // decompressing the PrefixSet cache whenever we need to apply
michael@0 47 // an update.
michael@0 48 //
michael@0 49 // byte slicing: Many of the 4-byte values stored here are strongly
michael@0 50 // correlated in the upper bytes, and uncorrelated in the lower
michael@0 51 // bytes. Because zlib/DEFLATE requires match lengths of at least
michael@0 52 // 3 to achieve good compression, and we don't get those if only
michael@0 53 // the upper 16-bits are correlated, it is worthwhile to slice 32-bit
michael@0 54 // values into 4 1-byte slices and compress the slices individually.
michael@0 55 // The slices corresponding to MSBs will compress very well, and the
michael@0 56 // slice corresponding to LSB almost nothing. Because of this, we
michael@0 57 // only apply DEFLATE to the 3 most significant bytes, and store the
michael@0 58 // LSB uncompressed.
michael@0 59 //
michael@0 60 // byte sliced (numValues) data format:
michael@0 61 // uint32_t compressed-size
michael@0 62 // compressed-size bytes zlib DEFLATE data
michael@0 63 // 0...numValues byte MSB of 4-byte numValues data
michael@0 64 // uint32_t compressed-size
michael@0 65 // compressed-size bytes zlib DEFLATE data
michael@0 66 // 0...numValues byte 2nd byte of 4-byte numValues data
michael@0 67 // uint32_t compressed-size
michael@0 68 // compressed-size bytes zlib DEFLATE data
michael@0 69 // 0...numValues byte 3rd byte of 4-byte numValues data
michael@0 70 // 0...numValues byte LSB of 4-byte numValues data
michael@0 71 //
michael@0 72 // Store data format:
michael@0 73 // uint32_t magic
michael@0 74 // uint32_t version
michael@0 75 // uint32_t numAddChunks
michael@0 76 // uint32_t numSubChunks
michael@0 77 // uint32_t numAddPrefixes
michael@0 78 // uint32_t numSubPrefixes
michael@0 79 // uint32_t numAddCompletes
michael@0 80 // uint32_t numSubCompletes
michael@0 81 // 0...numAddChunks uint32_t addChunk
michael@0 82 // 0...numSubChunks uint32_t subChunk
michael@0 83 // byte sliced (numAddPrefixes) uint32_t add chunk of AddPrefixes
michael@0 84 // byte sliced (numSubPrefixes) uint32_t add chunk of SubPrefixes
michael@0 85 // byte sliced (numSubPrefixes) uint32_t sub chunk of SubPrefixes
michael@0 86 // byte sliced (numSubPrefixes) uint32_t SubPrefixes
michael@0 87 // 0...numAddCompletes 32-byte Completions + uint32_t addChunk
michael@0 88 // 0...numSubCompletes 32-byte Completions + uint32_t addChunk
michael@0 89 // + uint32_t subChunk
michael@0 90 // 16-byte MD5 of all preceding data
michael@0 91
michael@0 92 // Name of the SafeBrowsing store
michael@0 93 #define STORE_SUFFIX ".sbstore"
michael@0 94
michael@0 95 // NSPR_LOG_MODULES=UrlClassifierDbService:5
michael@0 96 extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
michael@0 97 #if defined(PR_LOGGING)
michael@0 98 #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
michael@0 99 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
michael@0 100 #else
michael@0 101 #define LOG(args)
michael@0 102 #define LOG_ENABLED() (false)
michael@0 103 #endif
michael@0 104
michael@0 105 // Either the return was successful or we call the Reset function (unless we
michael@0 106 // hit an OOM). Used while reading in the store.
michael@0 107 #define SUCCESS_OR_RESET(res) \
michael@0 108 do { \
michael@0 109 nsresult __rv = res; /* Don't evaluate |res| more than once */ \
michael@0 110 if (__rv == NS_ERROR_OUT_OF_MEMORY) { \
michael@0 111 NS_WARNING("SafeBrowsing OOM."); \
michael@0 112 return __rv; \
michael@0 113 } \
michael@0 114 if (NS_FAILED(__rv)) { \
michael@0 115 NS_WARNING("SafeBrowsing store corrupted or out of date."); \
michael@0 116 Reset(); \
michael@0 117 return __rv; \
michael@0 118 } \
michael@0 119 } while(0)
michael@0 120
michael@0 121 namespace mozilla {
michael@0 122 namespace safebrowsing {
michael@0 123
michael@0 124 const uint32_t STORE_MAGIC = 0x1231af3b;
michael@0 125 const uint32_t CURRENT_VERSION = 3;
michael@0 126
michael@0 127 void
michael@0 128 TableUpdate::NewAddPrefix(uint32_t aAddChunk, const Prefix& aHash)
michael@0 129 {
michael@0 130 AddPrefix *add = mAddPrefixes.AppendElement();
michael@0 131 add->addChunk = aAddChunk;
michael@0 132 add->prefix = aHash;
michael@0 133 }
michael@0 134
michael@0 135 void
michael@0 136 TableUpdate::NewSubPrefix(uint32_t aAddChunk, const Prefix& aHash, uint32_t aSubChunk)
michael@0 137 {
michael@0 138 SubPrefix *sub = mSubPrefixes.AppendElement();
michael@0 139 sub->addChunk = aAddChunk;
michael@0 140 sub->prefix = aHash;
michael@0 141 sub->subChunk = aSubChunk;
michael@0 142 }
michael@0 143
michael@0 144 void
michael@0 145 TableUpdate::NewAddComplete(uint32_t aAddChunk, const Completion& aHash)
michael@0 146 {
michael@0 147 AddComplete *add = mAddCompletes.AppendElement();
michael@0 148 add->addChunk = aAddChunk;
michael@0 149 add->complete = aHash;
michael@0 150 }
michael@0 151
michael@0 152 void
michael@0 153 TableUpdate::NewSubComplete(uint32_t aAddChunk, const Completion& aHash, uint32_t aSubChunk)
michael@0 154 {
michael@0 155 SubComplete *sub = mSubCompletes.AppendElement();
michael@0 156 sub->addChunk = aAddChunk;
michael@0 157 sub->complete = aHash;
michael@0 158 sub->subChunk = aSubChunk;
michael@0 159 }
michael@0 160
michael@0 161
michael@0 162 HashStore::HashStore(const nsACString& aTableName, nsIFile* aStoreDir)
michael@0 163 : mTableName(aTableName)
michael@0 164 , mStoreDirectory(aStoreDir)
michael@0 165 , mInUpdate(false)
michael@0 166 {
michael@0 167 }
michael@0 168
michael@0 169 HashStore::~HashStore()
michael@0 170 {
michael@0 171 }
michael@0 172
michael@0 173 nsresult
michael@0 174 HashStore::Reset()
michael@0 175 {
michael@0 176 LOG(("HashStore resetting"));
michael@0 177
michael@0 178 nsCOMPtr<nsIFile> storeFile;
michael@0 179 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 180 NS_ENSURE_SUCCESS(rv, rv);
michael@0 181
michael@0 182 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(STORE_SUFFIX));
michael@0 183 NS_ENSURE_SUCCESS(rv, rv);
michael@0 184
michael@0 185 rv = storeFile->Remove(false);
michael@0 186 NS_ENSURE_SUCCESS(rv, rv);
michael@0 187
michael@0 188 return NS_OK;
michael@0 189 }
michael@0 190
michael@0 191 nsresult
michael@0 192 HashStore::CheckChecksum(nsIFile* aStoreFile,
michael@0 193 uint32_t aFileSize)
michael@0 194 {
michael@0 195 // Check for file corruption by
michael@0 196 // comparing the stored checksum to actual checksum of data
michael@0 197 nsAutoCString hash;
michael@0 198 nsAutoCString compareHash;
michael@0 199 char *data;
michael@0 200 uint32_t read;
michael@0 201
michael@0 202 nsresult rv = CalculateChecksum(hash, aFileSize, true);
michael@0 203 NS_ENSURE_SUCCESS(rv, rv);
michael@0 204
michael@0 205 compareHash.GetMutableData(&data, hash.Length());
michael@0 206
michael@0 207 if (hash.Length() > aFileSize) {
michael@0 208 NS_WARNING("SafeBrowing file not long enough to store its hash");
michael@0 209 return NS_ERROR_FAILURE;
michael@0 210 }
michael@0 211 nsCOMPtr<nsISeekableStream> seekIn = do_QueryInterface(mInputStream);
michael@0 212 rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, aFileSize - hash.Length());
michael@0 213 NS_ENSURE_SUCCESS(rv, rv);
michael@0 214
michael@0 215 rv = mInputStream->Read(data, hash.Length(), &read);
michael@0 216 NS_ENSURE_SUCCESS(rv, rv);
michael@0 217 NS_ASSERTION(read == hash.Length(), "Could not read hash bytes");
michael@0 218
michael@0 219 if (!hash.Equals(compareHash)) {
michael@0 220 NS_WARNING("Safebrowing file failed checksum.");
michael@0 221 return NS_ERROR_FAILURE;
michael@0 222 }
michael@0 223
michael@0 224 return NS_OK;
michael@0 225 }
michael@0 226
michael@0 227 nsresult
michael@0 228 HashStore::Open()
michael@0 229 {
michael@0 230 nsCOMPtr<nsIFile> storeFile;
michael@0 231 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 232 NS_ENSURE_SUCCESS(rv, rv);
michael@0 233
michael@0 234 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
michael@0 235 NS_ENSURE_SUCCESS(rv, rv);
michael@0 236
michael@0 237 nsCOMPtr<nsIInputStream> origStream;
michael@0 238 rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile,
michael@0 239 PR_RDONLY | nsIFile::OS_READAHEAD);
michael@0 240
michael@0 241 if (rv == NS_ERROR_FILE_NOT_FOUND) {
michael@0 242 UpdateHeader();
michael@0 243 return NS_OK;
michael@0 244 } else {
michael@0 245 SUCCESS_OR_RESET(rv);
michael@0 246 }
michael@0 247
michael@0 248 int64_t fileSize;
michael@0 249 rv = storeFile->GetFileSize(&fileSize);
michael@0 250 NS_ENSURE_SUCCESS(rv, rv);
michael@0 251
michael@0 252 if (fileSize < 0 || fileSize > UINT32_MAX) {
michael@0 253 return NS_ERROR_FAILURE;
michael@0 254 }
michael@0 255
michael@0 256 uint32_t fileSize32 = static_cast<uint32_t>(fileSize);
michael@0 257
michael@0 258 rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream,
michael@0 259 fileSize32);
michael@0 260 NS_ENSURE_SUCCESS(rv, rv);
michael@0 261
michael@0 262 rv = CheckChecksum(storeFile, fileSize32);
michael@0 263 SUCCESS_OR_RESET(rv);
michael@0 264
michael@0 265 rv = ReadHeader();
michael@0 266 SUCCESS_OR_RESET(rv);
michael@0 267
michael@0 268 rv = SanityCheck();
michael@0 269 SUCCESS_OR_RESET(rv);
michael@0 270
michael@0 271 rv = ReadChunkNumbers();
michael@0 272 SUCCESS_OR_RESET(rv);
michael@0 273
michael@0 274 return NS_OK;
michael@0 275 }
michael@0 276
michael@0 277 nsresult
michael@0 278 HashStore::ReadHeader()
michael@0 279 {
michael@0 280 if (!mInputStream) {
michael@0 281 UpdateHeader();
michael@0 282 return NS_OK;
michael@0 283 }
michael@0 284
michael@0 285 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
michael@0 286 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 287 NS_ENSURE_SUCCESS(rv, rv);
michael@0 288
michael@0 289 void *buffer = &mHeader;
michael@0 290 rv = NS_ReadInputStreamToBuffer(mInputStream,
michael@0 291 &buffer,
michael@0 292 sizeof(Header));
michael@0 293 NS_ENSURE_SUCCESS(rv, rv);
michael@0 294
michael@0 295 return NS_OK;
michael@0 296 }
michael@0 297
michael@0 298 nsresult
michael@0 299 HashStore::SanityCheck()
michael@0 300 {
michael@0 301 if (mHeader.magic != STORE_MAGIC || mHeader.version != CURRENT_VERSION) {
michael@0 302 NS_WARNING("Unexpected header data in the store.");
michael@0 303 return NS_ERROR_FAILURE;
michael@0 304 }
michael@0 305
michael@0 306 return NS_OK;
michael@0 307 }
michael@0 308
michael@0 309 nsresult
michael@0 310 HashStore::CalculateChecksum(nsAutoCString& aChecksum,
michael@0 311 uint32_t aFileSize,
michael@0 312 bool aChecksumPresent)
michael@0 313 {
michael@0 314 aChecksum.Truncate();
michael@0 315
michael@0 316 // Reset mInputStream to start
michael@0 317 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
michael@0 318 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
michael@0 319
michael@0 320 nsCOMPtr<nsICryptoHash> hash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
michael@0 321 NS_ENSURE_SUCCESS(rv, rv);
michael@0 322
michael@0 323 // Size of MD5 hash in bytes
michael@0 324 const uint32_t CHECKSUM_SIZE = 16;
michael@0 325
michael@0 326 // MD5 is not a secure hash function, but since this is a filesystem integrity
michael@0 327 // check, this usage is ok.
michael@0 328 rv = hash->Init(nsICryptoHash::MD5);
michael@0 329 NS_ENSURE_SUCCESS(rv, rv);
michael@0 330
michael@0 331 if (!aChecksumPresent) {
michael@0 332 // Hash entire file
michael@0 333 rv = hash->UpdateFromStream(mInputStream, UINT32_MAX);
michael@0 334 } else {
michael@0 335 // Hash everything but last checksum bytes
michael@0 336 if (aFileSize < CHECKSUM_SIZE) {
michael@0 337 NS_WARNING("SafeBrowsing file isn't long enough to store its checksum");
michael@0 338 return NS_ERROR_FAILURE;
michael@0 339 }
michael@0 340 rv = hash->UpdateFromStream(mInputStream, aFileSize - CHECKSUM_SIZE);
michael@0 341 }
michael@0 342 NS_ENSURE_SUCCESS(rv, rv);
michael@0 343
michael@0 344 rv = hash->Finish(false, aChecksum);
michael@0 345 NS_ENSURE_SUCCESS(rv, rv);
michael@0 346
michael@0 347 return NS_OK;
michael@0 348 }
michael@0 349
michael@0 350 void
michael@0 351 HashStore::UpdateHeader()
michael@0 352 {
michael@0 353 mHeader.magic = STORE_MAGIC;
michael@0 354 mHeader.version = CURRENT_VERSION;
michael@0 355
michael@0 356 mHeader.numAddChunks = mAddChunks.Length();
michael@0 357 mHeader.numSubChunks = mSubChunks.Length();
michael@0 358 mHeader.numAddPrefixes = mAddPrefixes.Length();
michael@0 359 mHeader.numSubPrefixes = mSubPrefixes.Length();
michael@0 360 mHeader.numAddCompletes = mAddCompletes.Length();
michael@0 361 mHeader.numSubCompletes = mSubCompletes.Length();
michael@0 362 }
michael@0 363
michael@0 364 nsresult
michael@0 365 HashStore::ReadChunkNumbers()
michael@0 366 {
michael@0 367 NS_ENSURE_STATE(mInputStream);
michael@0 368
michael@0 369 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
michael@0 370 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET,
michael@0 371 sizeof(Header));
michael@0 372
michael@0 373 rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks);
michael@0 374 NS_ENSURE_SUCCESS(rv, rv);
michael@0 375 NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks, "Read the right amount of add chunks.");
michael@0 376
michael@0 377 rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks);
michael@0 378 NS_ENSURE_SUCCESS(rv, rv);
michael@0 379 NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks, "Read the right amount of sub chunks.");
michael@0 380
michael@0 381 return NS_OK;
michael@0 382 }
michael@0 383
michael@0 384 nsresult
michael@0 385 HashStore::ReadHashes()
michael@0 386 {
michael@0 387 if (!mInputStream) {
michael@0 388 // BeginUpdate has been called but Open hasn't initialized mInputStream,
michael@0 389 // because the existing HashStore is empty.
michael@0 390 return NS_OK;
michael@0 391 }
michael@0 392
michael@0 393 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mInputStream);
michael@0 394
michael@0 395 uint32_t offset = sizeof(Header);
michael@0 396 offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32_t);
michael@0 397 nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset);
michael@0 398
michael@0 399 rv = ReadAddPrefixes();
michael@0 400 NS_ENSURE_SUCCESS(rv, rv);
michael@0 401
michael@0 402 rv = ReadSubPrefixes();
michael@0 403 NS_ENSURE_SUCCESS(rv, rv);
michael@0 404
michael@0 405 rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes);
michael@0 406 NS_ENSURE_SUCCESS(rv, rv);
michael@0 407
michael@0 408 rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes);
michael@0 409 NS_ENSURE_SUCCESS(rv, rv);
michael@0 410
michael@0 411 return NS_OK;
michael@0 412 }
michael@0 413
michael@0 414 nsresult
michael@0 415 HashStore::BeginUpdate()
michael@0 416 {
michael@0 417 // Read the rest of the store in memory.
michael@0 418 nsresult rv = ReadHashes();
michael@0 419 SUCCESS_OR_RESET(rv);
michael@0 420
michael@0 421 // Close input stream, won't be needed any more and
michael@0 422 // we will rewrite ourselves.
michael@0 423 if (mInputStream) {
michael@0 424 rv = mInputStream->Close();
michael@0 425 NS_ENSURE_SUCCESS(rv, rv);
michael@0 426 }
michael@0 427
michael@0 428 mInUpdate = true;
michael@0 429
michael@0 430 return NS_OK;
michael@0 431 }
michael@0 432
michael@0 433 template<class T>
michael@0 434 static nsresult
michael@0 435 Merge(ChunkSet* aStoreChunks,
michael@0 436 FallibleTArray<T>* aStorePrefixes,
michael@0 437 ChunkSet& aUpdateChunks,
michael@0 438 FallibleTArray<T>& aUpdatePrefixes,
michael@0 439 bool aAllowMerging = false)
michael@0 440 {
michael@0 441 EntrySort(aUpdatePrefixes);
michael@0 442
michael@0 443 T* updateIter = aUpdatePrefixes.Elements();
michael@0 444 T* updateEnd = aUpdatePrefixes.Elements() + aUpdatePrefixes.Length();
michael@0 445
michael@0 446 T* storeIter = aStorePrefixes->Elements();
michael@0 447 T* storeEnd = aStorePrefixes->Elements() + aStorePrefixes->Length();
michael@0 448
michael@0 449 // use a separate array so we can keep the iterators valid
michael@0 450 // if the nsTArray grows
michael@0 451 nsTArray<T> adds;
michael@0 452
michael@0 453 for (; updateIter != updateEnd; updateIter++) {
michael@0 454 // skip this chunk if we already have it, unless we're
michael@0 455 // merging completions, in which case we'll always already
michael@0 456 // have the chunk from the original prefix
michael@0 457 if (aStoreChunks->Has(updateIter->Chunk()))
michael@0 458 if (!aAllowMerging)
michael@0 459 continue;
michael@0 460 // XXX: binary search for insertion point might be faster in common
michael@0 461 // case?
michael@0 462 while (storeIter < storeEnd && (storeIter->Compare(*updateIter) < 0)) {
michael@0 463 // skip forward to matching element (or not...)
michael@0 464 storeIter++;
michael@0 465 }
michael@0 466 // no match, add
michael@0 467 if (storeIter == storeEnd
michael@0 468 || storeIter->Compare(*updateIter) != 0) {
michael@0 469 if (!adds.AppendElement(*updateIter))
michael@0 470 return NS_ERROR_OUT_OF_MEMORY;
michael@0 471 }
michael@0 472 }
michael@0 473
michael@0 474 // Chunks can be empty, but we should still report we have them
michael@0 475 // to make the chunkranges continuous.
michael@0 476 aStoreChunks->Merge(aUpdateChunks);
michael@0 477
michael@0 478 aStorePrefixes->AppendElements(adds);
michael@0 479 EntrySort(*aStorePrefixes);
michael@0 480
michael@0 481 return NS_OK;
michael@0 482 }
michael@0 483
michael@0 484 nsresult
michael@0 485 HashStore::ApplyUpdate(TableUpdate &update)
michael@0 486 {
michael@0 487 nsresult rv = mAddExpirations.Merge(update.AddExpirations());
michael@0 488 NS_ENSURE_SUCCESS(rv, rv);
michael@0 489
michael@0 490 rv = mSubExpirations.Merge(update.SubExpirations());
michael@0 491 NS_ENSURE_SUCCESS(rv, rv);
michael@0 492
michael@0 493 rv = Expire();
michael@0 494 NS_ENSURE_SUCCESS(rv, rv);
michael@0 495
michael@0 496 rv = Merge(&mAddChunks, &mAddPrefixes,
michael@0 497 update.AddChunks(), update.AddPrefixes());
michael@0 498 NS_ENSURE_SUCCESS(rv, rv);
michael@0 499
michael@0 500 rv = Merge(&mAddChunks, &mAddCompletes,
michael@0 501 update.AddChunks(), update.AddCompletes(), true);
michael@0 502 NS_ENSURE_SUCCESS(rv, rv);
michael@0 503
michael@0 504 rv = Merge(&mSubChunks, &mSubPrefixes,
michael@0 505 update.SubChunks(), update.SubPrefixes());
michael@0 506 NS_ENSURE_SUCCESS(rv, rv);
michael@0 507
michael@0 508 rv = Merge(&mSubChunks, &mSubCompletes,
michael@0 509 update.SubChunks(), update.SubCompletes(), true);
michael@0 510 NS_ENSURE_SUCCESS(rv, rv);
michael@0 511
michael@0 512 return NS_OK;
michael@0 513 }
michael@0 514
michael@0 515 nsresult
michael@0 516 HashStore::Rebuild()
michael@0 517 {
michael@0 518 NS_ASSERTION(mInUpdate, "Must be in update to rebuild.");
michael@0 519
michael@0 520 nsresult rv = ProcessSubs();
michael@0 521 NS_ENSURE_SUCCESS(rv, rv);
michael@0 522
michael@0 523 UpdateHeader();
michael@0 524
michael@0 525 return NS_OK;
michael@0 526 }
michael@0 527
michael@0 528 void
michael@0 529 HashStore::ClearCompletes()
michael@0 530 {
michael@0 531 NS_ASSERTION(mInUpdate, "Must be in update to clear completes.");
michael@0 532
michael@0 533 mAddCompletes.Clear();
michael@0 534 mSubCompletes.Clear();
michael@0 535
michael@0 536 UpdateHeader();
michael@0 537 }
michael@0 538
michael@0 539 template<class T>
michael@0 540 static void
michael@0 541 ExpireEntries(FallibleTArray<T>* aEntries, ChunkSet& aExpirations)
michael@0 542 {
michael@0 543 T* addIter = aEntries->Elements();
michael@0 544 T* end = aEntries->Elements() + aEntries->Length();
michael@0 545
michael@0 546 for (T *iter = addIter; iter != end; iter++) {
michael@0 547 if (!aExpirations.Has(iter->Chunk())) {
michael@0 548 *addIter = *iter;
michael@0 549 addIter++;
michael@0 550 }
michael@0 551 }
michael@0 552
michael@0 553 aEntries->SetLength(addIter - aEntries->Elements());
michael@0 554 }
michael@0 555
michael@0 556 nsresult
michael@0 557 HashStore::Expire()
michael@0 558 {
michael@0 559 ExpireEntries(&mAddPrefixes, mAddExpirations);
michael@0 560 ExpireEntries(&mAddCompletes, mAddExpirations);
michael@0 561 ExpireEntries(&mSubPrefixes, mSubExpirations);
michael@0 562 ExpireEntries(&mSubCompletes, mSubExpirations);
michael@0 563
michael@0 564 mAddChunks.Remove(mAddExpirations);
michael@0 565 mSubChunks.Remove(mSubExpirations);
michael@0 566
michael@0 567 mAddExpirations.Clear();
michael@0 568 mSubExpirations.Clear();
michael@0 569
michael@0 570 return NS_OK;
michael@0 571 }
michael@0 572
michael@0 573 template<class T>
michael@0 574 nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray<T>& aIn)
michael@0 575 {
michael@0 576 uLongf insize = aIn.Length() * sizeof(T);
michael@0 577 uLongf outsize = compressBound(insize);
michael@0 578 FallibleTArray<char> outBuff;
michael@0 579 if (!outBuff.SetLength(outsize)) {
michael@0 580 return NS_ERROR_OUT_OF_MEMORY;
michael@0 581 }
michael@0 582
michael@0 583 int zerr = compress(reinterpret_cast<Bytef*>(outBuff.Elements()),
michael@0 584 &outsize,
michael@0 585 reinterpret_cast<const Bytef*>(aIn.Elements()),
michael@0 586 insize);
michael@0 587 if (zerr != Z_OK) {
michael@0 588 return NS_ERROR_FAILURE;
michael@0 589 }
michael@0 590 LOG(("DeflateWriteTArray: %d in %d out", insize, outsize));
michael@0 591
michael@0 592 outBuff.TruncateLength(outsize);
michael@0 593
michael@0 594 // Length of compressed data stream
michael@0 595 uint32_t dataLen = outBuff.Length();
michael@0 596 uint32_t written;
michael@0 597 nsresult rv = aStream->Write(reinterpret_cast<char*>(&dataLen), sizeof(dataLen), &written);
michael@0 598 NS_ENSURE_SUCCESS(rv, rv);
michael@0 599
michael@0 600 NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length");
michael@0 601
michael@0 602 // Store to stream
michael@0 603 rv = WriteTArray(aStream, outBuff);
michael@0 604 NS_ENSURE_SUCCESS(rv, rv);
michael@0 605
michael@0 606 return NS_OK;
michael@0 607 }
michael@0 608
michael@0 609 template<class T>
michael@0 610 nsresult InflateReadTArray(nsIInputStream* aStream, FallibleTArray<T>* aOut,
michael@0 611 uint32_t aExpectedSize)
michael@0 612 {
michael@0 613
michael@0 614 uint32_t inLen;
michael@0 615 uint32_t read;
michael@0 616 nsresult rv = aStream->Read(reinterpret_cast<char*>(&inLen), sizeof(inLen), &read);
michael@0 617 NS_ENSURE_SUCCESS(rv, rv);
michael@0 618
michael@0 619 NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length");
michael@0 620
michael@0 621 FallibleTArray<char> inBuff;
michael@0 622 if (!inBuff.SetLength(inLen)) {
michael@0 623 return NS_ERROR_OUT_OF_MEMORY;
michael@0 624 }
michael@0 625
michael@0 626 rv = ReadTArray(aStream, &inBuff, inLen);
michael@0 627 NS_ENSURE_SUCCESS(rv, rv);
michael@0 628
michael@0 629 uLongf insize = inLen;
michael@0 630 uLongf outsize = aExpectedSize * sizeof(T);
michael@0 631 if (!aOut->SetLength(aExpectedSize)) {
michael@0 632 return NS_ERROR_OUT_OF_MEMORY;
michael@0 633 }
michael@0 634
michael@0 635 int zerr = uncompress(reinterpret_cast<Bytef*>(aOut->Elements()),
michael@0 636 &outsize,
michael@0 637 reinterpret_cast<const Bytef*>(inBuff.Elements()),
michael@0 638 insize);
michael@0 639 if (zerr != Z_OK) {
michael@0 640 return NS_ERROR_FAILURE;
michael@0 641 }
michael@0 642 LOG(("InflateReadTArray: %d in %d out", insize, outsize));
michael@0 643
michael@0 644 NS_ASSERTION(outsize == aExpectedSize * sizeof(T), "Decompression size mismatch");
michael@0 645
michael@0 646 return NS_OK;
michael@0 647 }
michael@0 648
michael@0 649 static nsresult
michael@0 650 ByteSliceWrite(nsIOutputStream* aOut, nsTArray<uint32_t>& aData)
michael@0 651 {
michael@0 652 nsTArray<uint8_t> slice1;
michael@0 653 nsTArray<uint8_t> slice2;
michael@0 654 nsTArray<uint8_t> slice3;
michael@0 655 nsTArray<uint8_t> slice4;
michael@0 656 uint32_t count = aData.Length();
michael@0 657
michael@0 658 slice1.SetCapacity(count);
michael@0 659 slice2.SetCapacity(count);
michael@0 660 slice3.SetCapacity(count);
michael@0 661 slice4.SetCapacity(count);
michael@0 662
michael@0 663 for (uint32_t i = 0; i < count; i++) {
michael@0 664 slice1.AppendElement( aData[i] >> 24);
michael@0 665 slice2.AppendElement((aData[i] >> 16) & 0xFF);
michael@0 666 slice3.AppendElement((aData[i] >> 8) & 0xFF);
michael@0 667 slice4.AppendElement( aData[i] & 0xFF);
michael@0 668 }
michael@0 669
michael@0 670 nsresult rv = DeflateWriteTArray(aOut, slice1);
michael@0 671 NS_ENSURE_SUCCESS(rv, rv);
michael@0 672 rv = DeflateWriteTArray(aOut, slice2);
michael@0 673 NS_ENSURE_SUCCESS(rv, rv);
michael@0 674 rv = DeflateWriteTArray(aOut, slice3);
michael@0 675 NS_ENSURE_SUCCESS(rv, rv);
michael@0 676 // The LSB slice is generally uncompressible, don't bother
michael@0 677 // compressing it.
michael@0 678 rv = WriteTArray(aOut, slice4);
michael@0 679 NS_ENSURE_SUCCESS(rv, rv);
michael@0 680
michael@0 681 return NS_OK;
michael@0 682 }
michael@0 683
michael@0 684 static nsresult
michael@0 685 ByteSliceRead(nsIInputStream* aInStream, FallibleTArray<uint32_t>* aData, uint32_t count)
michael@0 686 {
michael@0 687 FallibleTArray<uint8_t> slice1;
michael@0 688 FallibleTArray<uint8_t> slice2;
michael@0 689 FallibleTArray<uint8_t> slice3;
michael@0 690 FallibleTArray<uint8_t> slice4;
michael@0 691
michael@0 692 nsresult rv = InflateReadTArray(aInStream, &slice1, count);
michael@0 693 NS_ENSURE_SUCCESS(rv, rv);
michael@0 694
michael@0 695 rv = InflateReadTArray(aInStream, &slice2, count);
michael@0 696 NS_ENSURE_SUCCESS(rv, rv);
michael@0 697
michael@0 698 rv = InflateReadTArray(aInStream, &slice3, count);
michael@0 699 NS_ENSURE_SUCCESS(rv, rv);
michael@0 700
michael@0 701 rv = ReadTArray(aInStream, &slice4, count);
michael@0 702 NS_ENSURE_SUCCESS(rv, rv);
michael@0 703
michael@0 704 if (!aData->SetCapacity(count)) {
michael@0 705 return NS_ERROR_OUT_OF_MEMORY;
michael@0 706 }
michael@0 707
michael@0 708 for (uint32_t i = 0; i < count; i++) {
michael@0 709 aData->AppendElement((slice1[i] << 24) | (slice2[i] << 16)
michael@0 710 | (slice3[i] << 8) | (slice4[i]));
michael@0 711 }
michael@0 712
michael@0 713 return NS_OK;
michael@0 714 }
michael@0 715
michael@0 716 nsresult
michael@0 717 HashStore::ReadAddPrefixes()
michael@0 718 {
michael@0 719 FallibleTArray<uint32_t> chunks;
michael@0 720 uint32_t count = mHeader.numAddPrefixes;
michael@0 721
michael@0 722 nsresult rv = ByteSliceRead(mInputStream, &chunks, count);
michael@0 723 NS_ENSURE_SUCCESS(rv, rv);
michael@0 724
michael@0 725 if (!mAddPrefixes.SetCapacity(count)) {
michael@0 726 return NS_ERROR_OUT_OF_MEMORY;
michael@0 727 }
michael@0 728 for (uint32_t i = 0; i < count; i++) {
michael@0 729 AddPrefix *add = mAddPrefixes.AppendElement();
michael@0 730 add->prefix.FromUint32(0);
michael@0 731 add->addChunk = chunks[i];
michael@0 732 }
michael@0 733
michael@0 734 return NS_OK;
michael@0 735 }
michael@0 736
michael@0 737 nsresult
michael@0 738 HashStore::ReadSubPrefixes()
michael@0 739 {
michael@0 740 FallibleTArray<uint32_t> addchunks;
michael@0 741 FallibleTArray<uint32_t> subchunks;
michael@0 742 FallibleTArray<uint32_t> prefixes;
michael@0 743 uint32_t count = mHeader.numSubPrefixes;
michael@0 744
michael@0 745 nsresult rv = ByteSliceRead(mInputStream, &addchunks, count);
michael@0 746 NS_ENSURE_SUCCESS(rv, rv);
michael@0 747
michael@0 748 rv = ByteSliceRead(mInputStream, &subchunks, count);
michael@0 749 NS_ENSURE_SUCCESS(rv, rv);
michael@0 750
michael@0 751 rv = ByteSliceRead(mInputStream, &prefixes, count);
michael@0 752 NS_ENSURE_SUCCESS(rv, rv);
michael@0 753
michael@0 754 if (!mSubPrefixes.SetCapacity(count)) {
michael@0 755 return NS_ERROR_OUT_OF_MEMORY;
michael@0 756 }
michael@0 757 for (uint32_t i = 0; i < count; i++) {
michael@0 758 SubPrefix *sub = mSubPrefixes.AppendElement();
michael@0 759 sub->addChunk = addchunks[i];
michael@0 760 sub->prefix.FromUint32(prefixes[i]);
michael@0 761 sub->subChunk = subchunks[i];
michael@0 762 }
michael@0 763
michael@0 764 return NS_OK;
michael@0 765 }
michael@0 766
michael@0 767 // Split up PrefixArray back into the constituents
michael@0 768 nsresult
michael@0 769 HashStore::WriteAddPrefixes(nsIOutputStream* aOut)
michael@0 770 {
michael@0 771 nsTArray<uint32_t> chunks;
michael@0 772 uint32_t count = mAddPrefixes.Length();
michael@0 773 chunks.SetCapacity(count);
michael@0 774
michael@0 775 for (uint32_t i = 0; i < count; i++) {
michael@0 776 chunks.AppendElement(mAddPrefixes[i].Chunk());
michael@0 777 }
michael@0 778
michael@0 779 nsresult rv = ByteSliceWrite(aOut, chunks);
michael@0 780 NS_ENSURE_SUCCESS(rv, rv);
michael@0 781
michael@0 782 return NS_OK;
michael@0 783 }
michael@0 784
michael@0 785 nsresult
michael@0 786 HashStore::WriteSubPrefixes(nsIOutputStream* aOut)
michael@0 787 {
michael@0 788 nsTArray<uint32_t> addchunks;
michael@0 789 nsTArray<uint32_t> subchunks;
michael@0 790 nsTArray<uint32_t> prefixes;
michael@0 791 uint32_t count = mSubPrefixes.Length();
michael@0 792 addchunks.SetCapacity(count);
michael@0 793 subchunks.SetCapacity(count);
michael@0 794 prefixes.SetCapacity(count);
michael@0 795
michael@0 796 for (uint32_t i = 0; i < count; i++) {
michael@0 797 addchunks.AppendElement(mSubPrefixes[i].AddChunk());
michael@0 798 prefixes.AppendElement(mSubPrefixes[i].PrefixHash().ToUint32());
michael@0 799 subchunks.AppendElement(mSubPrefixes[i].Chunk());
michael@0 800 }
michael@0 801
michael@0 802 nsresult rv = ByteSliceWrite(aOut, addchunks);
michael@0 803 NS_ENSURE_SUCCESS(rv, rv);
michael@0 804
michael@0 805 rv = ByteSliceWrite(aOut, subchunks);
michael@0 806 NS_ENSURE_SUCCESS(rv, rv);
michael@0 807
michael@0 808 rv = ByteSliceWrite(aOut, prefixes);
michael@0 809 NS_ENSURE_SUCCESS(rv, rv);
michael@0 810
michael@0 811 return NS_OK;
michael@0 812 }
michael@0 813
michael@0 814 nsresult
michael@0 815 HashStore::WriteFile()
michael@0 816 {
michael@0 817 NS_ASSERTION(mInUpdate, "Must be in update to write database.");
michael@0 818
michael@0 819 nsCOMPtr<nsIFile> storeFile;
michael@0 820 nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile));
michael@0 821 NS_ENSURE_SUCCESS(rv, rv);
michael@0 822 rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore"));
michael@0 823 NS_ENSURE_SUCCESS(rv, rv);
michael@0 824
michael@0 825 nsCOMPtr<nsIOutputStream> out;
michael@0 826 rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile,
michael@0 827 PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE);
michael@0 828 NS_ENSURE_SUCCESS(rv, rv);
michael@0 829
michael@0 830 uint32_t written;
michael@0 831 rv = out->Write(reinterpret_cast<char*>(&mHeader), sizeof(mHeader), &written);
michael@0 832 NS_ENSURE_SUCCESS(rv, rv);
michael@0 833
michael@0 834 // Write chunk numbers.
michael@0 835 rv = mAddChunks.Write(out);
michael@0 836 NS_ENSURE_SUCCESS(rv, rv);
michael@0 837
michael@0 838 rv = mSubChunks.Write(out);
michael@0 839 NS_ENSURE_SUCCESS(rv, rv);
michael@0 840
michael@0 841 // Write hashes.
michael@0 842 rv = WriteAddPrefixes(out);
michael@0 843 NS_ENSURE_SUCCESS(rv, rv);
michael@0 844
michael@0 845 rv = WriteSubPrefixes(out);
michael@0 846 NS_ENSURE_SUCCESS(rv, rv);
michael@0 847
michael@0 848 rv = WriteTArray(out, mAddCompletes);
michael@0 849 NS_ENSURE_SUCCESS(rv, rv);
michael@0 850
michael@0 851 rv = WriteTArray(out, mSubCompletes);
michael@0 852 NS_ENSURE_SUCCESS(rv, rv);
michael@0 853
michael@0 854 nsCOMPtr<nsISafeOutputStream> safeOut = do_QueryInterface(out, &rv);
michael@0 855 NS_ENSURE_SUCCESS(rv, rv);
michael@0 856
michael@0 857 rv = safeOut->Finish();
michael@0 858 NS_ENSURE_SUCCESS(rv, rv);
michael@0 859
michael@0 860 return NS_OK;
michael@0 861 }
michael@0 862
michael@0 863 template <class T>
michael@0 864 static void
michael@0 865 Erase(FallibleTArray<T>* array, T* iterStart, T* iterEnd)
michael@0 866 {
michael@0 867 uint32_t start = iterStart - array->Elements();
michael@0 868 uint32_t count = iterEnd - iterStart;
michael@0 869
michael@0 870 if (count > 0) {
michael@0 871 array->RemoveElementsAt(start, count);
michael@0 872 }
michael@0 873 }
michael@0 874
michael@0 875 // Find items matching between |subs| and |adds|, and remove them,
michael@0 876 // recording the item from |adds| in |adds_removed|. To minimize
michael@0 877 // copies, the inputs are processing in parallel, so |subs| and |adds|
michael@0 878 // should be compatibly ordered (either by SBAddPrefixLess or
michael@0 879 // SBAddPrefixHashLess).
michael@0 880 //
michael@0 881 // |predAS| provides add < sub, |predSA| provides sub < add, for the
michael@0 882 // tightest compare appropriate (see calls in SBProcessSubs).
michael@0 883 template<class TSub, class TAdd>
michael@0 884 static void
michael@0 885 KnockoutSubs(FallibleTArray<TSub>* aSubs, FallibleTArray<TAdd>* aAdds)
michael@0 886 {
michael@0 887 // Keep a pair of output iterators for writing kept items. Due to
michael@0 888 // deletions, these may lag the main iterators. Using erase() on
michael@0 889 // individual items would result in O(N^2) copies. Using a list
michael@0 890 // would work around that, at double or triple the memory cost.
michael@0 891 TAdd* addOut = aAdds->Elements();
michael@0 892 TAdd* addIter = aAdds->Elements();
michael@0 893
michael@0 894 TSub* subOut = aSubs->Elements();
michael@0 895 TSub* subIter = aSubs->Elements();
michael@0 896
michael@0 897 TAdd* addEnd = addIter + aAdds->Length();
michael@0 898 TSub* subEnd = subIter + aSubs->Length();
michael@0 899
michael@0 900 while (addIter != addEnd && subIter != subEnd) {
michael@0 901 // additer compare, so it compares on add chunk
michael@0 902 int32_t cmp = addIter->Compare(*subIter);
michael@0 903 if (cmp > 0) {
michael@0 904 // If |*sub_iter| < |*add_iter|, retain the sub.
michael@0 905 *subOut = *subIter;
michael@0 906 ++subOut;
michael@0 907 ++subIter;
michael@0 908 } else if (cmp < 0) {
michael@0 909 // If |*add_iter| < |*sub_iter|, retain the add.
michael@0 910 *addOut = *addIter;
michael@0 911 ++addOut;
michael@0 912 ++addIter;
michael@0 913 } else {
michael@0 914 // Drop equal items
michael@0 915 ++addIter;
michael@0 916 ++subIter;
michael@0 917 }
michael@0 918 }
michael@0 919
michael@0 920 Erase(aAdds, addOut, addIter);
michael@0 921 Erase(aSubs, subOut, subIter);
michael@0 922 }
michael@0 923
michael@0 924 // Remove items in |removes| from |fullHashes|. |fullHashes| and
michael@0 925 // |removes| should be ordered by SBAddPrefix component.
michael@0 926 template <class T>
michael@0 927 static void
michael@0 928 RemoveMatchingPrefixes(const SubPrefixArray& aSubs, FallibleTArray<T>* aFullHashes)
michael@0 929 {
michael@0 930 // Where to store kept items.
michael@0 931 T* out = aFullHashes->Elements();
michael@0 932 T* hashIter = out;
michael@0 933 T* hashEnd = aFullHashes->Elements() + aFullHashes->Length();
michael@0 934
michael@0 935 SubPrefix const * removeIter = aSubs.Elements();
michael@0 936 SubPrefix const * removeEnd = aSubs.Elements() + aSubs.Length();
michael@0 937
michael@0 938 while (hashIter != hashEnd && removeIter != removeEnd) {
michael@0 939 int32_t cmp = removeIter->CompareAlt(*hashIter);
michael@0 940 if (cmp > 0) {
michael@0 941 // Keep items less than |*removeIter|.
michael@0 942 *out = *hashIter;
michael@0 943 ++out;
michael@0 944 ++hashIter;
michael@0 945 } else if (cmp < 0) {
michael@0 946 // No hit for |*removeIter|, bump it forward.
michael@0 947 ++removeIter;
michael@0 948 } else {
michael@0 949 // Drop equal items, there may be multiple hits.
michael@0 950 do {
michael@0 951 ++hashIter;
michael@0 952 } while (hashIter != hashEnd &&
michael@0 953 !(removeIter->CompareAlt(*hashIter) < 0));
michael@0 954 ++removeIter;
michael@0 955 }
michael@0 956 }
michael@0 957 Erase(aFullHashes, out, hashIter);
michael@0 958 }
michael@0 959
michael@0 960 static void
michael@0 961 RemoveDeadSubPrefixes(SubPrefixArray& aSubs, ChunkSet& aAddChunks)
michael@0 962 {
michael@0 963 SubPrefix * subIter = aSubs.Elements();
michael@0 964 SubPrefix * subEnd = aSubs.Elements() + aSubs.Length();
michael@0 965
michael@0 966 for (SubPrefix * iter = subIter; iter != subEnd; iter++) {
michael@0 967 bool hasChunk = aAddChunks.Has(iter->AddChunk());
michael@0 968 // Keep the subprefix if the chunk it refers to is one
michael@0 969 // we haven't seen it yet.
michael@0 970 if (!hasChunk) {
michael@0 971 *subIter = *iter;
michael@0 972 subIter++;
michael@0 973 }
michael@0 974 }
michael@0 975
michael@0 976 LOG(("Removed %u dead SubPrefix entries.", subEnd - subIter));
michael@0 977 aSubs.SetLength(subIter - aSubs.Elements());
michael@0 978 }
michael@0 979
michael@0 980 #ifdef DEBUG
michael@0 981 template <class T>
michael@0 982 static void EnsureSorted(FallibleTArray<T>* aArray)
michael@0 983 {
michael@0 984 T* start = aArray->Elements();
michael@0 985 T* end = aArray->Elements() + aArray->Length();
michael@0 986 T* iter = start;
michael@0 987 T* previous = start;
michael@0 988
michael@0 989 while (iter != end) {
michael@0 990 previous = iter;
michael@0 991 ++iter;
michael@0 992 if (iter != end) {
michael@0 993 MOZ_ASSERT(iter->Compare(*previous) >= 0);
michael@0 994 }
michael@0 995 }
michael@0 996
michael@0 997 return;
michael@0 998 }
michael@0 999 #endif
michael@0 1000
michael@0 1001 nsresult
michael@0 1002 HashStore::ProcessSubs()
michael@0 1003 {
michael@0 1004 #ifdef DEBUG
michael@0 1005 EnsureSorted(&mAddPrefixes);
michael@0 1006 EnsureSorted(&mSubPrefixes);
michael@0 1007 EnsureSorted(&mAddCompletes);
michael@0 1008 EnsureSorted(&mSubCompletes);
michael@0 1009 LOG(("All databases seem to have a consistent sort order."));
michael@0 1010 #endif
michael@0 1011
michael@0 1012 RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes);
michael@0 1013 RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes);
michael@0 1014
michael@0 1015 // Remove any remaining subbed prefixes from both addprefixes
michael@0 1016 // and addcompletes.
michael@0 1017 KnockoutSubs(&mSubPrefixes, &mAddPrefixes);
michael@0 1018 KnockoutSubs(&mSubCompletes, &mAddCompletes);
michael@0 1019
michael@0 1020 // Remove any remaining subprefixes referring to addchunks that
michael@0 1021 // we have (and hence have been processed above).
michael@0 1022 RemoveDeadSubPrefixes(mSubPrefixes, mAddChunks);
michael@0 1023
michael@0 1024 #ifdef DEBUG
michael@0 1025 EnsureSorted(&mAddPrefixes);
michael@0 1026 EnsureSorted(&mSubPrefixes);
michael@0 1027 EnsureSorted(&mAddCompletes);
michael@0 1028 EnsureSorted(&mSubCompletes);
michael@0 1029 LOG(("All databases seem to have a consistent sort order."));
michael@0 1030 #endif
michael@0 1031
michael@0 1032 return NS_OK;
michael@0 1033 }
michael@0 1034
michael@0 1035 nsresult
michael@0 1036 HashStore::AugmentAdds(const nsTArray<uint32_t>& aPrefixes)
michael@0 1037 {
michael@0 1038 uint32_t cnt = aPrefixes.Length();
michael@0 1039 if (cnt != mAddPrefixes.Length()) {
michael@0 1040 LOG(("Amount of prefixes in cache not consistent with store (%d vs %d)",
michael@0 1041 aPrefixes.Length(), mAddPrefixes.Length()));
michael@0 1042 return NS_ERROR_FAILURE;
michael@0 1043 }
michael@0 1044 for (uint32_t i = 0; i < cnt; i++) {
michael@0 1045 mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]);
michael@0 1046 }
michael@0 1047 return NS_OK;
michael@0 1048 }
michael@0 1049
michael@0 1050 }
michael@0 1051 }

mercurial