//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Url Classifier code * * The Initial Developer of the Original Code is * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Gian-Carlo Pascutto * Mehdi Mulani * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsUrlClassifierPrefixSet.h" #include "nsIUrlClassifierPrefixSet.h" #include "nsToolkitCompsCID.h" #include "nsTArray.h" #include "nsThreadUtils.h" #include "mozilla/Mutex.h" #include "prlog.h" using namespace mozilla; // NSPR_LOG_MODULES=UrlClassifierPrefixSet:5 #if defined(PR_LOGGING) static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull; #define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4) #else #define LOG(args) #define LOG_ENABLED() (PR_FALSE) #endif NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet); nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet() : mPrefixTreeLock("mPrefixTreeLock"), mTreeIsReady(mPrefixTreeLock, "mTreeIsReady"), mHasPrefixes(PR_FALSE) { #if defined(PR_LOGGING) if (!gUrlClassifierPrefixSetLog) gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet"); #endif LOG(("Instantiating PrefixSet")); } NS_IMETHODIMP nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength) { { MutexAutoLock lock(mPrefixTreeLock); if (mHasPrefixes) { LOG(("Clearing PrefixSet")); mDeltas.Clear(); mIndexPrefixes.Clear(); mIndexStarts.Clear(); mHasPrefixes = PR_FALSE; } } if (aLength > 0) { // Ensure they are sorted before adding nsTArray prefixes; prefixes.AppendElements(aArray, aLength); prefixes.Sort(); AddPrefixes(prefixes.Elements(), prefixes.Length()); } return NS_OK; } NS_IMETHODIMP nsUrlClassifierPrefixSet::AddPrefixes(const PRUint32 * prefixes, PRUint32 aLength) { if (aLength == 0) { return NS_OK; } MutexAutoLock lock(mPrefixTreeLock); mIndexPrefixes.AppendElement(prefixes[0]); mIndexStarts.AppendElement(mDeltas.Length()); PRUint32 numOfDeltas = 0; PRUint32 currentItem = prefixes[0]; for (PRUint32 i = 1; i < aLength; i++) { if ((numOfDeltas >= DELTAS_LIMIT) || (prefixes[i] - currentItem >= MAX_INDEX_DIFF)) { mIndexStarts.AppendElement(mDeltas.Length()); mIndexPrefixes.AppendElement(prefixes[i]); numOfDeltas = 0; } else { PRUint16 delta = prefixes[i] - currentItem; mDeltas.AppendElement(delta); numOfDeltas++; } currentItem = prefixes[i]; } mIndexPrefixes.Compact(); mIndexStarts.Compact(); mDeltas.Compact(); LOG(("Total number of indices: %d", mIndexPrefixes.Length())); LOG(("Total number of deltas: %d", mDeltas.Length())); mHasPrefixes = PR_TRUE; mTreeIsReady.NotifyAll(); return NS_OK; } PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start, PRUint32 end, PRUint32 target) { while (start != end && end >= start) { int i = start + ((end - start) >> 1); int value = mIndexPrefixes[i]; if (value < target) { start = i + 1; } else if (value > target) { end = i - 1; } else { return i; } } return end; } NS_IMETHODIMP nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, PRBool * aFound) { *aFound = PR_FALSE; if (!mHasPrefixes) { return NS_OK; } PRUint32 target = aPrefix; // We want to do a "Price is Right" binary search, that is, we want to find // the index of the value either equal to the target or the closest value // that is less than the target. // if (target < mIndexPrefixes[0]) { return NS_OK; } // |binsearch| does not necessarily return the correct index (when the // target is not found) but rather it returns an index at least one away // from the correct index. // Because of this, we need to check if the target lies before the beginning // of the indices. int i = BinSearch(0, mIndexPrefixes.Length() - 1, target); if (mIndexPrefixes[i] > target && i > 0) { i--; } // Now search through the deltas for the target. PRUint32 diff = target - mIndexPrefixes[i]; PRUint32 deltaIndex = mIndexStarts[i]; PRUint32 end = (i + 1 < mIndexStarts.Length()) ? mIndexStarts[i+1] : mDeltas.Length(); while (diff > 0 && deltaIndex < end) { diff -= mDeltas[deltaIndex]; deltaIndex++; } if (diff == 0) { *aFound = PR_TRUE; } return NS_OK; } NS_IMETHODIMP nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize) { MutexAutoLock lock(mPrefixTreeLock); *aSize = sizeof(PRBool); if (mHasPrefixes) { *aSize += sizeof(PRUint16) * mDeltas.Length(); *aSize += sizeof(PRUint32) * mIndexPrefixes.Length(); *aSize += sizeof(PRUint32) * mIndexStarts.Length(); } return NS_OK; } NS_IMETHODIMP nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRBool * aReady, PRBool * aFound) { MutexAutoLock lock(mPrefixTreeLock); // check whether we are opportunistically probing or should wait if (*aReady) { // we should block until we are ready while (!mHasPrefixes) { LOG(("Tree is empty, probe must wait")); mTreeIsReady.Wait(); } } else { // opportunistic probe -> check if set is loaded if (mHasPrefixes) { *aReady = PR_TRUE; } else { return NS_OK; } } nsresult rv = Contains(aPrefix, aFound); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }