openreplay/tracker/tracker-ios/Sources/ORTracker/Extensions/Data.swift
Delirium c0da34c528
feat(ios): add Ios source (#1640)
* feat: add ios and rn source

* fix(ios): remove testing keys

* fix(tracker): change default path
2023-11-10 10:32:55 +01:00

230 lines
7.3 KiB
Swift

import UIKit
import CommonCrypto
protocol NSObjectCoding: NSCoding, NSObject {}
extension NSObjectCoding {
static func from(data: Data, offset: inout Int) throws -> Self {
let valueData = try data.readData(offset: &offset)
guard let result = try NSKeyedUnarchiver.unarchivedObject(ofClass: self, from: valueData)
else { throw NSError(domain: "ErrorDomain", code: 0, userInfo: [NSLocalizedDescriptionKey: "Error reading NSCoding"]) }
return result
}
}
extension Data {
mutating func appendString(_ string: String) {
if let data = string.data(using: .utf8) {
append(data)
}
}
func sha256() -> String {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash)
}
return Data(hash).hexEncodedString()
}
func hexEncodedString() -> String {
return map { String(format: "%02hhx", $0) }.joined()
}
func subdata(start: Int, length: Int) -> Data? {
let start = startIndex.advanced(by: start)
let end = start.advanced(by: length)
guard start >= 0, end <= count else { return nil }
return subdata(in: start..<end)
}
}
extension Data {
func readPrimary<T>(offset: inout Int) throws -> T {
if T.self == CGFloat.self {
return CGFloat(try readPrimary(offset: &offset) as Double) as! T
}
if T.self == UInt64.self {
return try readUint(offset: &offset) as! T
}
if T.self == Int64.self {
return try readInt(offset: &offset) as! T
}
if T.self == Bool.self {
return try readBoolean(offset: &offset) as! T
}
let valueSize = MemoryLayout<T>.size
guard let data = subdata(start: offset, length: valueSize) else { throw "Error reading primary value" }
let result = data.withUnsafeBytes {
$0.load(as: T.self)
}
offset += data.count
return result
}
func readData(offset: inout Int) throws -> Data {
let length = try readUint(offset: &offset)
guard let data = subdata(start: offset, length: Int(length)),
length == data.count else { throw "Error reading data" }
offset += Int(length)
return data
}
func readString(offset: inout Int) throws -> String {
let data = try readData(offset: &offset)
guard let result = String(data: data, encoding: .utf8)
else { throw "Error reading string" }
return result
}
private func readByte(offset: inout Int) throws -> UInt8 {
guard offset < count else { throw "Error reading byte" }
let b = self[offset]
offset += 1
return b
}
private func readUint(offset: inout Int) throws -> UInt64 {
var x: UInt64 = 0
var s: Int = 0
var i: Int = 0
while true {
let b = try readByte(offset: &offset)
if b < 0x80 {
if i > 9 || i == 9 && b > 1 {
throw "Invalid UInt"
}
return x | UInt64(b)<<s
}
x |= UInt64(b&0x7f) << s
s += 7
i += 1
}
}
private func readInt(offset: inout Int) throws -> Int64 {
let ux = try readUint(offset: &offset)
var x = Int64(ux >> 1)
if ux&1 != 0 {
x = ~x
}
return x
}
private func readBoolean(offset: inout Int) throws -> Bool {
return try readByte(offset: &offset) == 1
}
}
extension Data {
init(value: Any?) {
self.init()
if let value = value {
writeValue(value: value)
}
}
init(values: Any...) {
self.init()
values.forEach { writeValue(value: $0) }
}
mutating func writeValues(values: Any...) {
values.forEach { writeValue(value: $0) }
}
mutating func writeValue(value: Any) {
let oldLength = count
switch value {
case is NSNull: break
case let parsed as Data: writeData(parsed, sizePrefix: true)
case let parsed as Int64: writeInt(parsed)
case let parsed as UInt64: writeUint(parsed)
case let parsed as Int: writePrimary(parsed)
case let parsed as UInt8: writePrimary(parsed)
case let parsed as UInt16: writePrimary(parsed)
case let parsed as UInt32: writePrimary(parsed)
case let parsed as Float: writePrimary(parsed)
case let parsed as CGFloat: writePrimary(Double(parsed))
case let parsed as Double: writePrimary(parsed)
case let parsed as Bool: writeBoolean(parsed)
case let parsed as UIEdgeInsets:
writeValues(values: parsed.top, parsed.bottom, parsed.left, parsed.right)
case let parsed as CGRect:
writeValues(values: parsed.origin.x, parsed.origin.y, parsed.size.width, parsed.size.height)
case let parsed as CGPoint: writeValues(values: parsed.x, parsed.y)
case let parsed as CGSize: writeValues(values: parsed.width, parsed.height)
case let parsed as UIColor:
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
parsed.getRed(&red, green: &green, blue: &blue, alpha: &alpha)
writeValues(values: red, green, blue, alpha)
case let parsed as String: writeString(parsed)
case let parsed as UIFont: writeNSCoding(parsed.fontDescriptor)
case let parsed as NSAttributedString: writeNSCoding(parsed)
default: break
}
if CFGetTypeID(value as CFTypeRef) == CGColor.typeID {
writeValue(value: UIColor(cgColor: value as! CGColor))
}
let length = count - oldLength
if length == 0 && !(value is NSNull) {
print("Nothing was written for \(String(describing: type(of: value))):\(value) ")
}
}
private mutating func writePrimary<T>(_ value: T) {
writeData(Swift.withUnsafeBytes(of: value) { Data($0) }, sizePrefix: false)
}
private mutating func writeString(_ string: String) {
let stringData = string.data(using: .utf8, allowLossyConversion: true) ?? Data()
writeData(stringData, sizePrefix: true)
}
private mutating func writeNSCoding(_ coding: NSCoding) {
do {
let valueData = try NSKeyedArchiver.archivedData(withRootObject: coding, requiringSecureCoding: true)
writeData(valueData, sizePrefix: true)
} catch {
print("Unexpected error: \(error).")
}
}
private mutating func writeData(_ data: Data, sizePrefix: Bool) {
if sizePrefix {
writeValue(value: UInt64(data.count))
}
append(data)
}
private mutating func writeUint(_ input: UInt64) {
var v = input
while v >= 0x80 {
append(UInt8(v.littleEndian & 0x7F) | 0x80) // v.littleEndian ?
v >>= 7
}
append(UInt8(v))
}
private mutating func writeInt(_ v: Int64) {
var uv = UInt64(v) << 1
if v < 0 {
uv = ~uv
}
writeUint(uv)
}
private mutating func writeBoolean(_ v: Bool) {
append(v ? 1 : 0)
}
}
extension Encodable {
func toJSONData() -> Data? { try? JSONEncoder().encode(self) }
}
extension String: Error {}