// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import GRDB import Curve25519Kit import SessionMessagingKit enum MockDataGenerator { // Note: This was taken from TensorFlow's Random (https://github.com/apple/swift/blob/bc8f9e61d333b8f7a625f74d48ef0b554726e349/stdlib/public/TensorFlow/Random.swift) // the complex approach is needed due to an issue with Swift's randomElement(using:) // generation (see https://stackoverflow.com/a/64897775 for more info) struct ARC4RandomNumberGenerator: RandomNumberGenerator { var state: [UInt8] = Array(0...255) var iPos: UInt8 = 0 var jPos: UInt8 = 0 init(seed: T) { self.init( seed: (0..<(UInt64.bitWidth / UInt64.bitWidth)).map { index in UInt8(truncatingIfNeeded: seed >> (UInt8.bitWidth * index)) } ) } init(seed: [UInt8]) { precondition(seed.count > 0, "Length of seed must be positive") precondition(seed.count <= 256, "Length of seed must be at most 256") // Note: Have to use a for loop instead of a 'forEach' otherwise // it doesn't work properly (not sure why...) var j: UInt8 = 0 for i: UInt8 in 0...255 { j &+= S(i) &+ seed[Int(i) % seed.count] swapAt(i, j) } } /// Produce the next random UInt64 from the stream, and advance the internal state mutating func next() -> UInt64 { // Note: Have to use a for loop instead of a 'forEach' otherwise // it doesn't work properly (not sure why...) var result: UInt64 = 0 for _ in 0.. UInt8 { return state[Int(index)] } /// Helper to swap elements of the state private mutating func swapAt(_ i: UInt8, _ j: UInt8) { state.swapAt(Int(i), Int(j)) } /// Generates the next byte in the keystream. private mutating func nextByte() -> UInt8 { iPos &+= 1 jPos &+= S(iPos) swapAt(iPos, jPos) return S(S(iPos) &+ S(jPos)) } } // MARK: - Generation static var printProgress: Bool = true static var hasStartedGenerationThisRun: Bool = false static func generateMockData(_ db: Database) { // Don't re-generate the mock data if it already exists guard !hasStartedGenerationThisRun && !(try! SessionThread.exists(db, id: "MockDatabaseThread")) else { hasStartedGenerationThisRun = true return } /// The mock data generation is quite slow, there are 3 parts which take a decent amount of time (deleting the account afterwards will /// also take a long time): /// Generating the threads & content - ~3s per 100 /// Writing to the database - ~10s per 1000 /// Updating the UI - ~10s per 1000 let dmThreadCount: Int = 1000 let closedGroupThreadCount: Int = 50 let openGroupThreadCount: Int = 20 let messageRangePerThread: [ClosedRange] = [(0...500)] let dmRandomSeed: Int = 1111 let cgRandomSeed: Int = 2222 let ogRandomSeed: Int = 3333 let chunkSize: Int = 1000 // Chunk up the thread writing to prevent memory issues let stringContent: [String] = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ".map { String($0) } let wordContent: [String] = ["alias", "consequatur", "aut", "perferendis", "sit", "voluptatem", "accusantium", "doloremque", "aperiam", "eaque", "ipsa", "quae", "ab", "illo", "inventore", "veritatis", "et", "quasi", "architecto", "beatae", "vitae", "dicta", "sunt", "explicabo", "aspernatur", "aut", "odit", "aut", "fugit", "sed", "quia", "consequuntur", "magni", "dolores", "eos", "qui", "ratione", "voluptatem", "sequi", "nesciunt", "neque", "dolorem", "ipsum", "quia", "dolor", "sit", "amet", "consectetur", "adipisci", "velit", "sed", "quia", "non", "numquam", "eius", "modi", "tempora", "incidunt", "ut", "labore", "et", "dolore", "magnam", "aliquam", "quaerat", "voluptatem", "ut", "enim", "ad", "minima", "veniam", "quis", "nostrum", "exercitationem", "ullam", "corporis", "nemo", "enim", "ipsam", "voluptatem", "quia", "voluptas", "sit", "suscipit", "laboriosam", "nisi", "ut", "aliquid", "ex", "ea", "commodi", "consequatur", "quis", "autem", "vel", "eum", "iure", "reprehenderit", "qui", "in", "ea", "voluptate", "velit", "esse", "quam", "nihil", "molestiae", "et", "iusto", "odio", "dignissimos", "ducimus", "qui", "blanditiis", "praesentium", "laudantium", "totam", "rem", "voluptatum", "deleniti", "atque", "corrupti", "quos", "dolores", "et", "quas", "molestias", "excepturi", "sint", "occaecati", "cupiditate", "non", "provident", "sed", "ut", "perspiciatis", "unde", "omnis", "iste", "natus", "error", "similique", "sunt", "in", "culpa", "qui", "officia", "deserunt", "mollitia", "animi", "id", "est", "laborum", "et", "dolorum", "fuga", "et", "harum", "quidem", "rerum", "facilis", "est", "et", "expedita", "distinctio", "nam", "libero", "tempore", "cum", "soluta", "nobis", "est", "eligendi", "optio", "cumque", "nihil", "impedit", "quo", "porro", "quisquam", "est", "qui", "minus", "id", "quod", "maxime", "placeat", "facere", "possimus", "omnis", "voluptas", "assumenda", "est", "omnis", "dolor", "repellendus", "temporibus", "autem", "quibusdam", "et", "aut", "consequatur", "vel", "illum", "qui", "dolorem", "eum", "fugiat", "quo", "voluptas", "nulla", "pariatur", "at", "vero", "eos", "et", "accusamus", "officiis", "debitis", "aut", "rerum", "necessitatibus", "saepe", "eveniet", "ut", "et", "voluptates", "repudiandae", "sint", "et", "molestiae", "non", "recusandae", "itaque", "earum", "rerum", "hic", "tenetur", "a", "sapiente", "delectus", "ut", "aut", "reiciendis", "voluptatibus", "maiores", "doloribus", "asperiores", "repellat"] let timestampNow: TimeInterval = Date().timeIntervalSince1970 let userSessionId: String = getUserHexEncodedPublicKey(db) let logProgress: (String, String) -> () = { title, event in guard printProgress else { return } print("[MockDataGenerator] (\(Date().timeIntervalSince1970)) \(title) - \(event)") } hasStartedGenerationThisRun = true // FIXME: Make sure this data doesn't go off device somehow? logProgress("", "Start") // First create the thread used to indicate that the mock data has been generated _ = try? SessionThread .fetchOrCreate(db, id: "MockDatabaseThread", variant: .contact, shouldBeVisible: false) // MARK: - -- DM Thread var dmThreadRandomGenerator: ARC4RandomNumberGenerator = ARC4RandomNumberGenerator(seed: dmRandomSeed) var dmThreadIndex: Int = 0 logProgress("DM Threads", "Start Generating \(dmThreadCount) threads") while dmThreadIndex < dmThreadCount { let remainingThreads: Int = (dmThreadCount - dmThreadIndex) (0..