Created
December 13, 2018 10:47
-
-
Save akshaynexus/4100b152e93b72f2de6de434335c780f to your computer and use it in GitHub Desktop.
New swift extensions.swift from ravenwallet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Extensions.swift | |
// RavenWallet | |
// | |
// Created by Samuel Sutch on 1/30/16. | |
// Copyright (c) 2018 Ravenwallet Team | |
// | |
// Permission is hereby granted, free of charge, to any person obtaining a copy | |
// of this software and associated documentation files (the "Software"), to deal | |
// in the Software without restriction, including without limitation the rights | |
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
// copies of the Software, and to permit persons to whom the Software is | |
// furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in | |
// all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
// THE SOFTWARE. | |
import Foundation | |
import BRCore | |
import libbz2 | |
import UIKit | |
public extension String { | |
static func buildQueryString(_ options: [String: [String]]?, includeQ: Bool = false) -> String { | |
var s = "" | |
if let options = options , options.count > 0 { | |
s = includeQ ? "?" : "" | |
var i = 0 | |
for (k, vals) in options { | |
for v in vals { | |
if i != 0 { | |
s += "&" | |
} | |
i += 1 | |
s += "\(k.urlEscapedString)=\(v.urlEscapedString)" | |
} | |
} | |
} | |
return s | |
} | |
static var urlQuoteCharacterSet: CharacterSet { | |
if let cset = (NSMutableCharacterSet.urlQueryAllowed as NSCharacterSet).mutableCopy() as? NSMutableCharacterSet { | |
cset.removeCharacters(in: "?=&") | |
return cset as CharacterSet | |
} | |
return NSMutableCharacterSet.urlQueryAllowed as CharacterSet | |
} | |
func md5() -> String { | |
guard let data = self.data(using: .utf8) else { | |
assert(false, "couldnt encode string as utf8 data") | |
return "" | |
} | |
var result = Data(count: 128/8) | |
let resultCount = result.count | |
return result.withUnsafeMutableBytes { (resultBytes: UnsafeMutablePointer<CUnsignedChar>) -> String in | |
data.withUnsafeBytes { (dataBytes) -> Void in | |
BRMD5(resultBytes, dataBytes, data.count) | |
} | |
var hash = String() | |
for i in 0..<resultCount { | |
hash = hash.appendingFormat("%02x", resultBytes[i]) | |
} | |
return hash | |
} | |
} | |
func base58DecodedData() -> Data { | |
let len = BRBase58Decode(nil, 0, self) | |
var data = Data(count: len) | |
_ = data.withUnsafeMutableBytes({ BRBase58Decode($0, len, self) }) | |
return data | |
} | |
var urlEscapedString: String { | |
return addingPercentEncoding(withAllowedCharacters: String.urlQuoteCharacterSet) ?? "" | |
} | |
func parseQueryString() -> [String: [String]] { | |
var ret = [String: [String]]() | |
var strippedString = self | |
if String(self[..<self.index(self.startIndex, offsetBy: 1)]) == "?" { | |
strippedString = String(self[self.index(self.startIndex, offsetBy: 1)...]) | |
} | |
strippedString = strippedString.replacingOccurrences(of: "+", with: " ") | |
strippedString = strippedString.removingPercentEncoding! | |
for s in strippedString.components(separatedBy: "&") { | |
let kp = s.components(separatedBy: "=") | |
if kp.count == 2 { | |
if var k = ret[kp[0]] { | |
k.append(kp[1]) | |
} else { | |
ret[kp[0]] = [kp[1]] | |
} | |
} | |
} | |
return ret | |
} | |
var cStr: UnsafePointer<CChar>? { | |
return (self as NSString).utf8String | |
} | |
} | |
extension UserDefaults { | |
var deviceID: String { | |
if let s = string(forKey: "BR_DEVICE_ID") { | |
return s | |
} | |
let s = CFUUIDCreateString(nil, CFUUIDCreate(nil)) as String | |
setValue(s, forKey: "BR_DEVICE_ID") | |
print("new device id \(s)") | |
return s | |
} | |
} | |
let VAR_INT16_HEADER: UInt64 = 0xfd | |
let VAR_INT32_HEADER: UInt64 = 0xfe | |
let VAR_INT64_HEADER: UInt64 = 0xff | |
extension NSMutableData { | |
func appendVarInt(i: UInt64) { | |
if (i < VAR_INT16_HEADER) { | |
var payload = UInt8(i) | |
append(&payload, length: MemoryLayout<UInt8>.size) | |
} | |
else if (Int32(i) <= UINT16_MAX) { | |
var header = UInt8(VAR_INT16_HEADER) | |
var payload = CFSwapInt16HostToLittle(UInt16(i)) | |
append(&header, length: MemoryLayout<UInt8>.size) | |
append(&payload, length: MemoryLayout<UInt16>.size) | |
} | |
else if (UInt32(i) <= UINT32_MAX) { | |
var header = UInt8(VAR_INT32_HEADER) | |
var payload = CFSwapInt32HostToLittle(UInt32(i)) | |
append(&header, length: MemoryLayout<UInt8>.size) | |
append(&payload, length: MemoryLayout<UInt32>.size) | |
} | |
else { | |
var header = UInt8(VAR_INT64_HEADER) | |
var payload = CFSwapInt64HostToLittle(i) | |
append(&header, length: MemoryLayout<UInt8>.size) | |
append(&payload, length: MemoryLayout<UInt64>.size) | |
} | |
} | |
} | |
var BZCompressionBufferSize: UInt32 = 1024 | |
var BZDefaultBlockSize: Int32 = 7 | |
var BZDefaultWorkFactor: Int32 = 100 | |
private struct AssociatedKeys { | |
static var hexString = "hexString" | |
} | |
public extension Data { | |
var hexString: String { | |
if let string = getCachedHexString() { | |
return string | |
} else { | |
let string = reduce("") {$0 + String(format: "%02x", $1)} | |
setHexString(string: string) | |
return string | |
} | |
} | |
private func setHexString(string: String) { | |
objc_setAssociatedObject(self, &AssociatedKeys.hexString, string, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) | |
} | |
private func getCachedHexString() -> String? { | |
return objc_getAssociatedObject(self, &AssociatedKeys.hexString) as? String | |
} | |
var bzCompressedData: Data? { | |
get { | |
guard !self.isEmpty else { | |
return self | |
} | |
var compressed = Data() | |
var stream = bz_stream() | |
var mself = self | |
var success = true | |
mself.withUnsafeMutableBytes { (selfBuff: UnsafeMutablePointer<Int8>) -> Void in | |
let outBuff = UnsafeMutablePointer<Int8>.allocate(capacity: Int(BZCompressionBufferSize)) | |
defer { outBuff.deallocate() } | |
stream.next_in = selfBuff | |
stream.avail_in = UInt32(self.count) | |
stream.next_out = outBuff | |
stream.avail_out = BZCompressionBufferSize | |
var bzret = BZ2_bzCompressInit(&stream, BZDefaultBlockSize, 0, BZDefaultWorkFactor) | |
guard bzret == BZ_OK else { | |
print("failed compression init") | |
success = false | |
return | |
} | |
repeat { | |
bzret = BZ2_bzCompress(&stream, stream.avail_in > 0 ? BZ_RUN : BZ_FINISH) | |
guard bzret >= BZ_OK else { | |
print("failed compress") | |
success = false | |
return | |
} | |
let bpp = UnsafeBufferPointer(start: outBuff, count: (Int(BZCompressionBufferSize) - Int(stream.avail_out))) | |
compressed.append(bpp) | |
stream.next_out = outBuff | |
stream.avail_out = BZCompressionBufferSize | |
} while bzret != BZ_STREAM_END | |
} | |
BZ2_bzCompressEnd(&stream) | |
guard success else { return nil } | |
return compressed | |
} | |
} | |
init?(bzCompressedData data: Data) { | |
guard !data.isEmpty else { | |
return nil | |
} | |
var stream = bz_stream() | |
var decompressed = Data() | |
var myDat = data | |
var success = true | |
myDat.withUnsafeMutableBytes { (datBuff: UnsafeMutablePointer<Int8>) -> Void in | |
let outBuff = UnsafeMutablePointer<Int8>.allocate(capacity: Int(BZCompressionBufferSize)) | |
defer { outBuff.deallocate() } | |
stream.next_in = datBuff | |
stream.avail_in = UInt32(data.count) | |
stream.next_out = outBuff | |
stream.avail_out = BZCompressionBufferSize | |
var bzret = BZ2_bzDecompressInit(&stream, 0, 0) | |
guard bzret == BZ_OK else { | |
print("failed decompress init") | |
success = false | |
return | |
} | |
repeat { | |
bzret = BZ2_bzDecompress(&stream) | |
guard bzret >= BZ_OK else { | |
print("failed decompress") | |
success = false | |
return | |
} | |
let bpp = UnsafeBufferPointer(start: outBuff, count: (Int(BZCompressionBufferSize) - Int(stream.avail_out))) | |
decompressed.append(bpp) | |
stream.next_out = outBuff | |
stream.avail_out = BZCompressionBufferSize | |
} while bzret != BZ_STREAM_END | |
} | |
BZ2_bzDecompressEnd(&stream) | |
guard success else { return nil } | |
self.init(decompressed) | |
} | |
var base58: String { | |
return self.withUnsafeBytes { (selfBytes: UnsafePointer<UInt8>) -> String in | |
let len = BRBase58Encode(nil, 0, selfBytes, self.count) | |
var data = Data(count: len) | |
return data.withUnsafeMutableBytes { (b: UnsafeMutablePointer<Int8>) in | |
BRBase58Encode(b, len, selfBytes, self.count) | |
return String(cString: b) | |
} | |
} | |
} | |
var sha1: Data { | |
var data = Data(count: 20) | |
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in | |
self.withUnsafeBytes({ (selfBytes: UnsafePointer<UInt8>) in | |
BRSHA1(bytes, selfBytes, self.count) | |
}) | |
} | |
return data | |
} | |
var sha256: Data { | |
var data = Data(count: 32) | |
data.withUnsafeMutableBytes { (bytes: UnsafeMutablePointer<UInt8>) in | |
self.withUnsafeBytes({ (selfBytes: UnsafePointer<UInt8>) in | |
BRSHA256(bytes, selfBytes, self.count) | |
}) | |
} | |
return data | |
} | |
var sha256_2: Data { | |
return self.sha256.sha256 | |
} | |
var uInt256: UInt256 { | |
return self.withUnsafeBytes { (ptr: UnsafePointer<UInt256>) -> UInt256 in | |
return ptr.pointee | |
} | |
} | |
public func uInt8(atOffset offset: UInt) -> UInt8 { | |
let offt = Int(offset) | |
let size = MemoryLayout<UInt8>.size | |
if self.count < offt + size { return 0 } | |
return self.subdata(in: offt..<(offt+size)).withUnsafeBytes { (ptr: UnsafePointer<UInt8>) -> UInt8 in | |
return ptr.pointee | |
} | |
} | |
public func uInt32(atOffset offset: UInt) -> UInt32 { | |
let offt = Int(offset) | |
let size = MemoryLayout<UInt32>.size | |
if self.count < offt + size { return 0 } | |
return self.subdata(in: offt..<(offt+size)).withUnsafeBytes { (ptr: UnsafePointer<UInt32>) -> UInt32 in | |
return CFSwapInt32LittleToHost(ptr.pointee) | |
} | |
} | |
public func uInt64(atOffset offset: UInt) -> UInt64 { | |
let offt = Int(offset) | |
let size = MemoryLayout<UInt64>.size | |
if self.count < offt + size { return 0 } | |
return self.subdata(in: offt..<(offt+size)).withUnsafeBytes { (ptr: UnsafePointer<UInt64>) -> UInt64 in | |
return CFSwapInt64LittleToHost(ptr.pointee) | |
} | |
} | |
public func compactSign(key: BRKey) -> Data { | |
return self.withUnsafeBytes({ (selfBytes: UnsafePointer<UInt8>) -> Data in | |
var data = Data(count: 65) | |
var k = key | |
_ = data.withUnsafeMutableBytes({ BRKeyCompactSign(&k, $0, 65, self.uInt256) }) | |
return data | |
}) | |
} | |
fileprivate func genNonce() -> [UInt8] { | |
var tv = timeval() | |
gettimeofday(&tv, nil) | |
var t = UInt64(tv.tv_usec) * 1_000_000 + UInt64(tv.tv_usec) | |
let p = [UInt8](repeating: 0, count: 4) | |
return Data(bytes: &t, count: MemoryLayout<UInt64>.size).withUnsafeBytes { (dat: UnsafePointer<UInt8>) -> [UInt8] in | |
let buf = UnsafeBufferPointer(start: dat, count: MemoryLayout<UInt64>.size) | |
return p + Array(buf) | |
} | |
} | |
public func chacha20Poly1305AEADEncrypt(key: BRKey) -> Data { | |
let data = [UInt8](self) | |
let inData = UnsafePointer<UInt8>(data) | |
let nonce = genNonce() | |
var null = CChar(0) | |
var sk = key.secret | |
return withUnsafePointer(to: &sk) { | |
let outSize = BRChacha20Poly1305AEADEncrypt(nil, 0, $0, nonce, inData, data.count, &null, 0) | |
var outData = [UInt8](repeating: 0, count: outSize) | |
BRChacha20Poly1305AEADEncrypt(&outData, outSize, $0, nonce, inData, data.count, &null, 0) | |
return Data(nonce + outData) | |
} | |
} | |
public func chacha20Poly1305AEADDecrypt(key: BRKey) throws -> Data { | |
let data = [UInt8](self) | |
guard data.count > 12 else { throw BRReplicatedKVStoreError.malformedData } | |
let nonce = Array(data[data.startIndex...data.startIndex.advanced(by: 12)]) | |
let inData = Array(data[data.startIndex.advanced(by: 12)...(data.endIndex-1)]) | |
var null = CChar(0) | |
var sk = key.secret | |
return withUnsafePointer(to: &sk) { | |
let outSize = BRChacha20Poly1305AEADDecrypt(nil, 0, $0, nonce, inData, inData.count, &null, 0) | |
var outData = [UInt8](repeating: 0, count: outSize) | |
BRChacha20Poly1305AEADDecrypt(&outData, outSize, $0, nonce, inData, inData.count, &null, 0) | |
return Data(outData) | |
} | |
} | |
var masterPubKey: BRMasterPubKey? { | |
guard self.count >= (4 + 32 + 33) else { return nil } | |
var mpk = BRMasterPubKey() | |
mpk.fingerPrint = self.subdata(in: 0..<4).withUnsafeBytes { $0.pointee } | |
mpk.chainCode = self.subdata(in: 4..<(4 + 32)).withUnsafeBytes { $0.pointee } | |
mpk.pubKey = self.subdata(in: (4 + 32)..<(4 + 32 + 33)).withUnsafeBytes { $0.pointee } | |
return mpk | |
} | |
init(masterPubKey mpk: BRMasterPubKey) { | |
var data = [mpk.fingerPrint].withUnsafeBufferPointer { Data(buffer: $0) } | |
[mpk.chainCode].withUnsafeBufferPointer { data.append($0) } | |
[mpk.pubKey].withUnsafeBufferPointer { data.append($0) } | |
self.init(data) | |
} | |
var urlEncodedObject: [String: [String]]? { | |
guard let str = String(data: self, encoding: .utf8) else { | |
return nil | |
} | |
return str.parseQueryString() | |
} | |
} | |
public extension Date { | |
static func withMsTimestamp(_ ms: UInt64) -> Date { | |
return Date(timeIntervalSince1970: Double(ms) / 1000.0) | |
} | |
func msTimestamp() -> UInt64 { | |
return UInt64((self.timeIntervalSince1970 < 0 ? 0 : self.timeIntervalSince1970) * 1000.0) | |
} | |
// this is lifted from: https://github.com/Fykec/NSDate-RFC1123/blob/master/NSDate%2BRFC1123.swift | |
// Copyright © 2015 Foster Yin. All rights reserved. | |
fileprivate static func cachedThreadLocalObjectWithKey<T: AnyObject>(_ key: String, create: () -> T) -> T { | |
let threadDictionary = Thread.current.threadDictionary | |
if let cachedObject = threadDictionary[key] as! T? { | |
return cachedObject | |
} | |
else { | |
let newObject = create() | |
threadDictionary[key] = newObject | |
return newObject | |
} | |
} | |
fileprivate static func RFC1123DateFormatter() -> DateFormatter { | |
return cachedThreadLocalObjectWithKey("RFC1123DateFormatter") { | |
let locale = Locale(identifier: "en_US") | |
let timeZone = TimeZone(identifier: "GMT") | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = locale //need locale for some iOS 9 verision, will not select correct default locale | |
dateFormatter.timeZone = timeZone | |
dateFormatter.dateFormat = "EEE, dd MMM yyyy HH:mm:ss z" | |
return dateFormatter | |
} | |
} | |
fileprivate static func RFC850DateFormatter() -> DateFormatter { | |
return cachedThreadLocalObjectWithKey("RFC850DateFormatter") { | |
let locale = Locale(identifier: "en_US") | |
let timeZone = TimeZone(identifier: "GMT") | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = locale //need locale for some iOS 9 verision, will not select correct default locale | |
dateFormatter.timeZone = timeZone | |
dateFormatter.dateFormat = "EEEE, dd-MMM-yy HH:mm:ss z" | |
return dateFormatter | |
} | |
} | |
fileprivate static func asctimeDateFormatter() -> DateFormatter { | |
return cachedThreadLocalObjectWithKey("asctimeDateFormatter") { | |
let locale = Locale(identifier: "en_US") | |
let timeZone = TimeZone(identifier: "GMT") | |
let dateFormatter = DateFormatter() | |
dateFormatter.locale = locale //need locale for some iOS 9 verision, will not select correct default locale | |
dateFormatter.timeZone = timeZone | |
dateFormatter.dateFormat = "EEE MMM d HH:mm:ss yyyy" | |
return dateFormatter | |
} | |
} | |
static func fromRFC1123(_ dateString: String) -> Date? { | |
var date: Date? | |
// RFC1123 | |
date = Date.RFC1123DateFormatter().date(from: dateString) | |
if date != nil { | |
return date | |
} | |
// RFC850 | |
date = Date.RFC850DateFormatter().date(from: dateString) | |
if date != nil { | |
return date | |
} | |
// asctime-date | |
date = Date.asctimeDateFormatter().date(from: dateString) | |
if date != nil { | |
return date | |
} | |
return nil | |
} | |
func RFC1123String() -> String? { | |
return Date.RFC1123DateFormatter().string(from: self) | |
} | |
} | |
public extension BRKey { | |
public var publicKey: Data { | |
var k = self | |
let len = BRKeyPubKey(&k, nil, 0) | |
var data = Data(count: len) | |
BRKeyPubKey(&k, data.withUnsafeMutableBytes({ (d: UnsafeMutablePointer<UInt8>) -> UnsafeMutablePointer<UInt8> in d }), len) | |
return data | |
} | |
} | |
extension UIImage { | |
/// Represents a scaling mode | |
enum ScalingMode { | |
case aspectFill | |
case aspectFit | |
/// Calculates the aspect ratio between two sizes | |
/// | |
/// - parameters: | |
/// - size: the first size used to calculate the ratio | |
/// - otherSize: the second size used to calculate the ratio | |
/// | |
/// - return: the aspect ratio between the two sizes | |
func aspectRatio(between size: CGSize, and otherSize: CGSize) -> CGFloat { | |
let aspectWidth = size.width/otherSize.width | |
let aspectHeight = size.height/otherSize.height | |
switch self { | |
case .aspectFill: | |
return max(aspectWidth, aspectHeight) | |
case .aspectFit: | |
return min(aspectWidth, aspectHeight) | |
} | |
} | |
} | |
/// Scales an image to fit within a bounds with a size governed by the passed size. Also keeps the aspect ratio. | |
/// | |
/// - parameters: | |
/// - newSize: the size of the bounds the image must fit within. | |
/// - scalingMode: the desired scaling mode | |
/// | |
/// - returns: a new scaled image. | |
func scaled(to newSize: CGSize, scalingMode: UIImage.ScalingMode = .aspectFill) -> UIImage { | |
let aspectRatio = scalingMode.aspectRatio(between: newSize, and: size) | |
/* Build the rectangle representing the area to be drawn */ | |
var scaledImageRect = CGRect.zero | |
scaledImageRect.size.width = size.width * aspectRatio | |
scaledImageRect.size.height = size.height * aspectRatio | |
scaledImageRect.origin.x = (newSize.width - size.width * aspectRatio) / 2.0 | |
scaledImageRect.origin.y = (newSize.height - size.height * aspectRatio) / 2.0 | |
/* Draw and retrieve the scaled image */ | |
UIGraphicsBeginImageContext(newSize) | |
draw(in: scaledImageRect) | |
let scaledImage = UIGraphicsGetImageFromCurrentImageContext() | |
UIGraphicsEndImageContext() | |
return scaledImage! | |
} | |
} | |
extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any { | |
var flattened: [Key: String] { | |
var ret = [Key: String]() | |
for (k, v) in self { | |
if let v = v as? [String] { | |
if v.count > 0 { | |
ret[k] = v[0] | |
} | |
} | |
} | |
return ret | |
} | |
var jsonString: String { | |
guard let json = try? JSONSerialization.data(withJSONObject: self, options: []) else { | |
return "null" | |
} | |
guard let jstring = String(data: json, encoding: .utf8) else { | |
return "null" | |
} | |
return jstring | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment