From 66bd3c78b9bbe3d7d41eeef003d70b19279facba Mon Sep 17 00:00:00 2001 From: Justin Mazzocchi <2831158+jzzocc@users.noreply.github.com> Date: Thu, 15 Oct 2020 03:03:39 -0700 Subject: [PATCH] Refactoring --- .../CodableBloomFilter/BloomFilter.swift | 15 ++---- .../CodableBloomFilterTests.swift | 20 ++------ .../Services/InstanceURLService.swift | 46 +++++++++++++++---- .../Utilities/UserDefaultsClient.swift | 22 +++------ 4 files changed, 53 insertions(+), 50 deletions(-) diff --git a/CodableBloomFilter/Sources/CodableBloomFilter/BloomFilter.swift b/CodableBloomFilter/Sources/CodableBloomFilter/BloomFilter.swift index 1497c4d..9313f59 100644 --- a/CodableBloomFilter/Sources/CodableBloomFilter/BloomFilter.swift +++ b/CodableBloomFilter/Sources/CodableBloomFilter/BloomFilter.swift @@ -2,14 +2,6 @@ import Foundation -// https://en.wikipedia.org/wiki/Bloom_filter -// https://khanlou.com/2018/09/bloom-filters/ -// This implementation uses deterministic hashing functions so it can conform to Codable - -enum BloomFilterError: Error { - case noHashesProvided -} - public struct BloomFilter: Codable { enum CodingKeys: String, CodingKey { case hashes @@ -20,12 +12,11 @@ public struct BloomFilter: Codable { private var bits: BitArray - public init(hashes: Set, byteCount: Int) throws { - try self.init(hashes: hashes, data: Data(repeating: 0, count: byteCount)) + public init(hashes: Set, byteCount: Int) { + self.init(hashes: hashes, data: Data(repeating: 0, count: byteCount)) } - public init(hashes: Set, data: Data) throws { - guard !hashes.isEmpty else { throw BloomFilterError.noHashesProvided } + public init(hashes: Set, data: Data) { // Sort the hashes for consistent decoding output self.hashes = Array(hashes.sorted { $0.rawValue < $1.rawValue }) bits = BitArray(data: data) diff --git a/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift b/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift index e7af542..2052140 100644 --- a/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift +++ b/CodableBloomFilter/Tests/CodableBloomFilterTests/CodableBloomFilterTests.swift @@ -12,18 +12,8 @@ final class CodableBloomFilterTests: XCTestCase { XCTAssertEqual(Hash.fnv1a32.apply("hash"), 3469047761) } - func noHashesProvided() throws { - XCTAssertThrowsError(try BloomFilter(hashes: [], byteCount: 8)) { - guard case BloomFilterError.noHashesProvided = $0 else { - XCTFail("Expected no hashers provided error") - - return - } - } - } - func testContains() throws { - var sut = try BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) + var sut = BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) sut.insert("lol") sut.insert("ok") @@ -35,7 +25,7 @@ final class CodableBloomFilterTests: XCTestCase { } func testData() throws { - var sut = try BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) + var sut = BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) sut.insert("lol") sut.insert("ok") @@ -44,7 +34,7 @@ final class CodableBloomFilterTests: XCTestCase { } func testFromData() throws { - let sut = try BloomFilter(hashes: [.sdbm32, .djb232], data: Data([0, 16, 0, 0, 0, 2, 0, 144])) + let sut = BloomFilter(hashes: [.sdbm32, .djb232], data: Data([0, 16, 0, 0, 0, 2, 0, 144])) XCTAssert(sut.contains("lol")) XCTAssert(sut.contains("ok")) @@ -53,7 +43,7 @@ final class CodableBloomFilterTests: XCTestCase { } func testCoding() throws { - var sut = try BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) + var sut = BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) let expectedData = Data(#"{"data":"ABAAAAACAJA=","hashes":["djb232","sdbm32"]}"#.utf8) sut.insert("lol") @@ -88,7 +78,7 @@ final class CodableBloomFilterTests: XCTestCase { } func testDataEncodingStrategy() throws { - var sut = try BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) + var sut = BloomFilter(hashes: [.sdbm32, .djb232], byteCount: 8) let expectedData = Data(#"{"data":"0010000000020090","hashes":["djb232","sdbm32"]}"#.utf8) sut.insert("lol") diff --git a/ServiceLayer/Sources/ServiceLayer/Services/InstanceURLService.swift b/ServiceLayer/Sources/ServiceLayer/Services/InstanceURLService.swift index 366a2c4..1cd25eb 100644 --- a/ServiceLayer/Sources/ServiceLayer/Services/InstanceURLService.swift +++ b/ServiceLayer/Sources/ServiceLayer/Services/InstanceURLService.swift @@ -61,7 +61,7 @@ public extension InstanceURLService { func updateFilter() -> AnyPublisher { httpClient.request(UpdatedFilterTarget()) - .handleEvents(receiveOutput: { userDefaultsClient.updatedInstanceFilter = $0 }) + .handleEvents(receiveOutput: { userDefaultsClient.updateInstanceFilter($0) }) .ignoreOutput() .eraseToAnyPublisher() } @@ -81,13 +81,43 @@ private struct UpdatedFilterTarget: DecodableTarget { private extension InstanceURLService { static let httpsPrefix = "https://" static let shortestPossibleURLLength = 4 - // swiftlint:disable line_length - static let defaultFilterData = #"{"hashes":["djb232","djb2a32","fnv132","fnv1a32","sdbm32"],"data":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAIAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAgAAAAAQAAAAAABAAACAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAABAAAEAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAIAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAIAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAIAAAQAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAQAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAADAAAAAAAAAAAAA=="}"# - .data(using: .utf8)! - // swiftlint:enable line_length - // swiftlint:disable force_try - static let defaultFilter = try! JSONDecoder().decode(BloomFilter.self, from: defaultFilterData) - // swiftlint:enable force_try + static let defaultFilter = BloomFilter( + hashes: [.djb232, .djb2a32, .sdbm32, .fnv132, .fnv1a32], + data: Data([ + 0, 0, 0, 16, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 2, 8, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, + 128, 0, 0, 32, 0, 128, 0, 0, 0, 4, 16, 4, 32, 0, 0, 16, 16, 4, 32, 0, 0, 128, 0, 16, 0, 0, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 4, 0, 0, 3, 2, 0, 0, 0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, 64, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, 64, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 96, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 8, 0, 1, 0, 8, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 160, + 0, 0, 0, 8, 64, 0, 1, 32, 0, 0, 1, 0, 0, 0, 0, 64, 8, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 32, 0, 0, 0, 0, 0, 130, 65, 0, 4, 0, 0, 0, 0, + 0, 0, 0, 8, 0, 0, 0, 0, 128, 65, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 64, 0, 128, 0, 0, 0, 16, + 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 2, 128, 0, 1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 48, 0, 0, 0, 2, 0, 0, 0, 0, 48, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 16, 0, 0, 0, 16, 0, 16, 0, 2, 64, + 0, 0, 0, 128, 0, 0, 0, 64, 16, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 8, 0, 0, 0, 4, 8, 0, 64, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 66, + 0, 64, 0, 16, 8, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 10, 0, + 0, 4, 0, 0, 0, 1, 24, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 128, 4, 64, 0, 128, 0, 0, 0, + 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 8, 0, 8, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 16, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 129, 8, 0, 8, 0, 0, 0, 8, 0, 0, + 0, 2, 0, 0, 128, 8, 36, 32, 0, 64, 0, 0, 0, 4, 0, 32, 0, 0, 0, 0, 0, 16, 2, 0, 0, 0, 2, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 16, 0, 0, 0, 136, 128, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 64, 0, 64, 0, 0, 0, 16, 0, 0, 0, 8, 0, 0, 0, 0, 16, 0, + 0, 0, 0, 32, 0, 4, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, + 0, 0, 0, 8, 0, 4, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 160, 0, 0, 0, 4, 0, 0, 16, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 64, + 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 16, 0, 0, 0, 4, 0, 1, 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, 128, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 4, 0, 64, 0, 1, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 12, 16, 0, 72, 0, 0, 0, 0, 0, 0 + ])) + var filter: BloomFilter { userDefaultsClient.updatedInstanceFilter ?? Self.defaultFilter } diff --git a/ServiceLayer/Sources/ServiceLayer/Utilities/UserDefaultsClient.swift b/ServiceLayer/Sources/ServiceLayer/Utilities/UserDefaultsClient.swift index a94f48f..24e1aeb 100644 --- a/ServiceLayer/Sources/ServiceLayer/Utilities/UserDefaultsClient.swift +++ b/ServiceLayer/Sources/ServiceLayer/Utilities/UserDefaultsClient.swift @@ -3,7 +3,7 @@ import CodableBloomFilter import Foundation -final class UserDefaultsClient { +struct UserDefaultsClient { private let userDefaults: UserDefaults init(userDefaults: UserDefaults) { @@ -13,23 +13,15 @@ final class UserDefaultsClient { extension UserDefaultsClient { var updatedInstanceFilter: BloomFilter? { - get { - guard let data = self[.updatedFilter] as Data? else { - return nil - } - - return try? JSONDecoder().decode(BloomFilter.self, from: data) + guard let data = self[.updatedFilter] as Data? else { + return nil } - set { - var data: Data? + return try? JSONDecoder().decode(BloomFilter.self, from: data) + } - if let newValue = newValue { - data = try? JSONEncoder().encode(newValue) - } - - self[.updatedFilter] = data - } + func updateInstanceFilter( _ filter: BloomFilter) { + userDefaults.set(try? JSONEncoder().encode(filter), forKey: Item.updatedFilter.rawValue) } }