// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. import Foundation import Sodium 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)) } } static func generateMockData() { // Don't re-generate the mock data if it already exists var existingMockDataThread: TSContactThread? Storage.read { transaction in existingMockDataThread = TSContactThread.getWithContactSessionID("MockDatabaseThread", transaction: transaction) } guard existingMockDataThread == nil else { 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 = 100 let closedGroupThreadCount: Int = 0 let openGroupThreadCount: Int = 0 let maxMessagesPerThread: Int = 50 let dmRandomSeed: Int = 1111 let cgRandomSeed: Int = 2222 let ogRandomSeed: Int = 3333 // FIXME: Make sure this data doesn't go off device somehow? Storage.shared.write { anyTransaction in guard let transaction: YapDatabaseReadWriteTransaction = anyTransaction as? YapDatabaseReadWriteTransaction else { return } // First create the thread used to indicate that the mock data has been generated _ = TSContactThread.getOrCreateThread(withContactSessionID: "MockDatabaseThread", transaction: transaction) // Multiple spaces to make it look more like words let stringContent: [String] = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ".map { String($0) } let timestampNow: TimeInterval = Date().timeIntervalSince1970 let userSessionId: String = getUserHexEncodedPublicKey() // MARK: - -- DM Thread var dmThreadRandomGenerator: ARC4RandomNumberGenerator = ARC4RandomNumberGenerator(seed: dmRandomSeed) (0..