import Foundation
import Shared
private let _singleton = AdBlocker()

class AdBlocker {

  private init() {
  }

  class var singleton: AdBlocker {
    return _singleton
  }

  func shouldBlock(request: NSURLRequest) -> Bool {
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let profile = appDelegate.getProfile(UIApplication.sharedApplication())
    if !(profile.prefs.boolForKey(AdBlockSetting.prefKey) ?? AdBlockSetting.defaultValue) {
      return false
    }

    guard let url = request.URL,
      domain = request.mainDocumentURL?.host else {
        return false
    }

    if let host = url.host where host.startsWith(domain) {
      return false
    }

    return AdBlockCppFilter.singleton().checkWithCppABPFilter(url.absoluteString, mainDocumentUrl: domain)
  }
}
import Foundation
import Shared
private let _singleton = AdBlocker()

class AdBlocker {
  // Store the last 500 URLs checked
  // Store 10 x 50 URLs in the array called timeOrderedCacheChunks. This is a FIFO array,
  // Throw out a 50 URL chunk when the array is full
  var fifoOfCachedUrlChunks: [NSMutableDictionary] = []
  let maxChunks = 10
  let maxUrlsPerChunk = 50

  private init() {
  }

  class var singleton: AdBlocker {
    return _singleton
  }

  func shouldBlock(request: NSURLRequest) -> Bool {
    // TODO: there shouldn't be a cost to checking unchanged prefs, please confirm this
    let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
    let profile = appDelegate.getProfile(UIApplication.sharedApplication())
    if !(profile.prefs.boolForKey(AdBlockSetting.prefKey) ?? AdBlockSetting.defaultValue) {
      return false
    }

    guard let url = request.URL,
      domain = request.mainDocumentURL?.host else {
        return false
    }

    if let host = url.host where host.startsWith(domain) {
      return false
    }

    // A cache entry is like: timeOrderedCacheChunks[0]["www.microsoft.com_http://some.url"] = true/false for blocking
    let key = "\(domain)_\(url.absoluteString)"

    for urls in fifoOfCachedUrlChunks {
      if let urlIsBlocked = urls[key] {
        return urlIsBlocked as! Bool
      }
    }

    let isBlocked = AdBlockCppFilter.singleton().checkWithCppABPFilter(url.absoluteString, mainDocumentUrl: domain)

    if fifoOfCachedUrlChunks.count > maxChunks {
      fifoOfCachedUrlChunks.removeFirst()
    }

    if fifoOfCachedUrlChunks.last == nil || fifoOfCachedUrlChunks.last?.count > maxUrlsPerChunk {
      fifoOfCachedUrlChunks.append(NSMutableDictionary())
    }

    if let cacheChunkUrlAndDomain = fifoOfCachedUrlChunks.last {
      cacheChunkUrlAndDomain[key] = isBlocked
    }

    return isBlocked
  }
}
Implement a FIFO queue for url checking, which stores the last 500 domain+urls and there block result (true/false for blocking). Implmented as a simple 10 element array of 50 URLs. A priority queue doesn't seem valuable here. My method requires 10 hash lookups per URL check, a PQ of 500 elements has log2(500) = 8.5 plus the overhead of releasing memory for every single URL (the overhead of memory cleanup on every URL check is horrible compared to a single amortized memory cleanup every 50 urls)
Implement a FIFO queue for url checking, which stores the last 500 domain+urls and there block result (true/false for blocking).
Implmented as a simple 10 element array of 50 URLs. A priority queue doesn't seem valuable here.
My method requires 10 hash lookups per URL check, a PQ of 500 elements has log2(500) = 8.5 plus the overhead of releasing memory for every single URL
(the overhead of memory cleanup on every URL check is horrible compared to a single amortized memory cleanup every 50 urls)
