Refactoring I.

- Revamp whole DB to Display flow
- Filter Pipeline, arbitrary filtering and sorting
- Binary tree arrays for faster lookup & manipulation
- DB: introducing custom functions
- DB scheme: split req into heap & cache
- cache written by GlassVPN only
- heap written by Main App only
- Introducing DB separation: DBCore, DBCommon, DBAppOnly
- Introducing DB data sources: TestDataSource, GroupedDomainDataSource, RecordingsDB, DomainFilter
- Background sync: Move entries from cache to heap and notify all observers
- GlassVPN: Binary tree filter lookup
- GlassVPN: Reusing prepared statement
This commit is contained in:
relikd
2020-06-02 21:45:08 +02:00
parent 10b43a0f67
commit b17fb3c354
36 changed files with 2214 additions and 1482 deletions

View File

@@ -16,102 +16,6 @@ struct QLog {
}
}
extension Collection {
subscript(ifExist i: Index?) -> Iterator.Element? {
guard let i = i else { return nil }
return indices.contains(i) ? self[i] : nil
}
}
var listOfSLDs: [String : [String : Bool]] = {
let path = Bundle.main.url(forResource: "third-level", withExtension: "txt")
let content = try! String(contentsOf: path!)
var res: [String : [String : Bool]] = [:]
content.enumerateLines { line, _ in
let dom = line.split(separator: ".")
let tld = String(dom.first!)
let sld = String(dom.last!)
if res[tld] == nil { res[tld] = [:] }
res[tld]![sld] = true
}
return res
}()
extension String {
/// Check if string is equal to `domain` or ends with `.domain`
func isSubdomain(of domain: String) -> Bool { self == domain || self.hasSuffix("." + domain) }
/// Split string into top level domain part and host part
func splitDomainAndHost() -> (domain: String, host: String?) {
let lastChr = last?.asciiValue ?? 0
guard lastChr > UInt8(ascii: "9") || lastChr < UInt8(ascii: "0") else { // IP address
return (domain: "# IP connection", host: self)
}
var parts = components(separatedBy: ".")
guard let tld = parts.popLast(), let sld = parts.popLast() else {
return (domain: self, host: nil) // no subdomains, just plain SLD
}
var ending = sld + "." + tld
if listOfSLDs[tld]?[sld] ?? false, let rld = parts.popLast() {
ending = rld + "." + ending
}
return (domain: ending, host: parts.joined(separator: "."))
}
/// Returns `true` if String matches list of known second level domains (e.g., `co.uk`).
func isKnownSLD() -> Bool {
let parts = components(separatedBy: ".")
return parts.count == 2 && listOfSLDs[parts.last!]?[parts.first!] ?? false
}
}
extension Timer {
@discardableResult static func repeating(_ interval: TimeInterval, call selector: Selector, on target: Any, userInfo: Any? = nil) -> Timer {
Timer.scheduledTimer(timeInterval: interval, target: target, selector: selector,
userInfo: userInfo, repeats: true)
}
}
extension DateFormatter {
convenience init(withFormat: String) {
self.init()
dateFormat = withFormat
}
func with(format: String) -> Self {
dateFormat = format
return self
}
func string(from ts: Timestamp) -> String {
string(from: Date.init(timeIntervalSince1970: Double(ts)))
}
}
struct TimeFormat {
static func from(_ duration: Timestamp) -> String {
String(format: "%02d:%02d", duration / 60, duration % 60)
}
static func from(_ duration: TimeInterval, millis: Bool = false) -> String {
let t = Int(duration)
if millis {
let mil = Int(duration * 1000) % 1000
return String(format: "%02d:%02d.%03d", t / 60, t % 60, mil)
}
return String(format: "%02d:%02d", t / 60, t % 60)
}
static func since(_ date: Date, millis: Bool = false) -> String {
from(Date().timeIntervalSince(date), millis: millis)
}
/// Formatted duration string, e.g., `20 min` or `7 days`
/// - Parameters:
/// - minutes: Duration in minutes
/// - style: Default: `.short`
static func short(minutes: Int, style: DateComponentsFormatter.UnitsStyle = .short) -> String? {
let dcf = DateComponentsFormatter()
dcf.maximumUnitCount = 1
dcf.allowedUnits = [.day, .hour, .minute]
dcf.unitsStyle = style
return dcf.string(from: DateComponents(minute: minutes))
}
}
extension UIColor {
static var sysBg: UIColor { get { if #available(iOS 13.0, *) { return .systemBackground } else { return .white } }}
static var sysFg: UIColor { get { if #available(iOS 13.0, *) { return .label } else { return .black } }}