├── .gitignore ├── .travis.yml ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── SwiftFoundation │ ├── Base64.swift │ ├── ComparisonResult.swift │ ├── ContiguousBytes.swift │ ├── Data.swift │ ├── DataProtocol.swift │ ├── Date.swift │ ├── Extensions │ ├── Hexadecimal.swift │ └── Integer.swift │ ├── POSIXError.swift │ ├── POSIXTime.swift │ ├── Thread.swift │ ├── URL.swift │ └── UUID.swift ├── SwiftFoundation.xcodeproj ├── SwiftFoundationTests_Info.plist ├── SwiftFoundation_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── xcuserdata │ │ └── coleman.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcshareddata │ └── xcschemes │ └── SwiftFoundation-Package.xcscheme └── Tests ├── LinuxMain.swift └── SwiftFoundationTests ├── DataTests.swift ├── POSIXTimeTests.swift └── UUIDTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | SwiftFoundation/SwiftFoundation.xcodeproj/project.xcworkspace/xcuserdata/* 2 | SwiftFoundation.xcworkspace/xcuserdata/* 3 | SwiftFoundationAppleBridge/SwiftFoundationAppleBridge.xcodeproj/xcuserdata/* 4 | SwiftFoundation/SwiftFoundation.xcodeproj/xcuserdata/* 5 | Carthage/* 6 | SwiftFoundation/SwiftFoundationTests/1.json 7 | .build 8 | 9 | Packages/* 10 | 11 | SwiftFoundation/build 12 | 13 | Xcode/SwiftFoundation.xcodeproj/xcuserdata 14 | 15 | Xcode/SwiftFoundation.xcodeproj/project.xcworkspace/xcuserdata 16 | 17 | .DS_Store 18 | 19 | .swiftpm 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: generic 2 | matrix: 3 | include: 4 | # Test Ubuntu Linux 16.04 / Swift 5.2 5 | - os: linux 6 | dist: xenial 7 | sudo: required 8 | env: 9 | - SWIFT_VERSION=swift-5.2.2-RELEASE 10 | - SWIFT_URL=https://swift.org/builds/swift-5.2.2-release/ubuntu1604/swift-5.2.2-RELEASE/swift-5.2.2-RELEASE-ubuntu16.04.tar.gz 11 | install: 12 | - export PATH=$(pwd)/tests/$SWIFT_VERSION-ubuntu16.04/usr/bin:"${PATH}" 13 | # Test Xcode 11.4 / Swift 5.2.2 14 | - os: osx 15 | osx_image: xcode11.4 16 | addons: 17 | apt: 18 | packages: 19 | - clang 20 | - pkg-config 21 | script: 22 | # Setup Linux environment 23 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get update ; fi 24 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install clang ; fi 25 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then SWIFT_DIR=tests ; fi 26 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then mkdir $SWIFT_DIR ; fi 27 | - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then curl $SWIFT_URL -s | tar xz -C $SWIFT_DIR &> /dev/null ; fi 28 | # Run Unit Tests 29 | - swift test 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Alsey Coleman Miller 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | import PackageDescription 3 | 4 | let libraryType: PackageDescription.Product.Library.LibraryType 5 | #if os(Linux) 6 | libraryType = .dynamic 7 | #else 8 | libraryType = .static 9 | #endif 10 | 11 | let package = Package( 12 | name: "SwiftFoundation", 13 | products: [ 14 | .library( 15 | name: "SwiftFoundation", 16 | type: libraryType, 17 | targets: ["SwiftFoundation"] 18 | ) 19 | ], 20 | targets: [ 21 | .target(name: "SwiftFoundation"), 22 | .testTarget(name: "SwiftFoundationTests", dependencies: ["SwiftFoundation"]) 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SwiftFoundation # 2 | [![Swift](https://img.shields.io/badge/swift-5.0-orange.svg?style=flat)](https://developer.apple.com/swift/) 3 | [![Platforms](https://img.shields.io/badge/platform-osx%20%7C%20ios%20%7C%20watchos%20%7C%20tvos%20%7C%20linux%20%7C%20wasm-lightgrey.svg)](https://developer.apple.com/swift/) 4 | [![Release](https://img.shields.io/github/release/pureswift/swiftfoundation.svg)](https://github.com/PureSwift/SwiftFoundation/releases) 5 | [![License](https://img.shields.io/badge/license-MIT-71787A.svg)](https://tldrlegal.com/license/mit-license) 6 | [![Build Status](https://travis-ci.org/PureSwift/SwiftFoundation.svg?branch=develop)](https://travis-ci.org/PureSwift/SwiftFoundation) 7 | 8 | Cross-Platform, Protocol-Oriented Programming base library to complement the Swift Standard Library. 9 | 10 | ## Goals 11 | 12 | - Provide a cross-platform *interface* that mimics Apple's Foundation framework. 13 | - Provide a POSIX-based *implementation* for maximum portability. 14 | - *Rewrite* Foundation with Protocol-Oriented Programming principals. 15 | - Long-term Pure Swift *replacement* for the Cocoa frameworks. 16 | 17 | ## Problems with [Apple's Foundation](https://github.com/apple/swift-corelibs-foundation) 18 | 19 | - **Objective-C** - Apple's Foundation is an old API designed for Objective-C. While it works great (on Apple's platforms) and has a nice API for Objective-C programming, when imported into Swift, you can see the shortcomings of its 20+ year old API. 20 | - **Unimplemented** - The [open source version](https://github.com/apple/swift-corelibs-foundation) of Apple's Foundation is severly lacking implementation. Most methods are marked with ```NSUnimplemented()```. Only a small subset of Foundation based on CoreFoundation is implemented (e.g. ```NSArray```, ```NSString```, ```NSDictionary```). Basic Functionality like JSON, Base64, and even HTTP requests are not implemented. 21 | - **Portability** - Since Apple's Foundation is backed by CoreFoundation, the only supported platforms are currently Linux, Darwin, and (potentially) Windows. Supporting other platforms (e.g. ARM Linux, BSD, SunOS) would require changes to the CoreFoundation codebase, written in C, which is not good for a long term Swift base library. We want all of our code to be understood by any Swift programmer. 22 | - **Protocol Oriented Programming** - Perhaps the biggest reason to use this library, is to break free from the old *Object-Oriented Programming* paradigms. Swift structures and protocols free you from pointers and memory management, along with bugs related to multithreaded environments. Creating structs for basic types like ```Date``` and ```UUID``` allows you to use ```let``` and ```var``` correctly. Structs also bring huge performance improvements since the compiler can perform more optimizations and doesn't have to create all the metadata needed for the Swift class runtime. 23 | 24 | ## Targeted Platforms 25 | 26 | - Darwin (macOS, iOS, watchOS, tvOS) 27 | - Linux (Ubuntu x86_64, [Debian Armv7](https://github.com/uraimo/buildSwiftOnARM)) 28 | - [WebAssembly](https://swiftwasm.org) (wasm32) 29 | 30 | ## Implemented 31 | To see what parts of Foundation are implemented, just look at the unit tests. Completed functionality will be fully unit tested. Note that there is some functionality that is written as a protocol only, that will not be included on this list. 32 | 33 | - [x] Base64 34 | - [x] Data 35 | - [x] Date 36 | - [ ] FileManager 37 | - [ ] JSON 38 | - [ ] RegularExpression (POSIX, not ICU) 39 | - [x] Thread 40 | - [x] URL 41 | - [x] UUID 42 | 43 | # License 44 | 45 | This program is free software; you can redistribute it and/or modify it under the terms of the MIT License. 46 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/Base64.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Base64.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 6/28/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | public extension SwiftFoundation.Data { 10 | 11 | struct Base64EncodingOptions : OptionSet { 12 | public let rawValue : UInt 13 | public init(rawValue: UInt) { self.rawValue = rawValue } 14 | 15 | public static let encoding64CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 0)) 16 | public static let encoding76CharacterLineLength = Base64EncodingOptions(rawValue: UInt(1 << 1)) 17 | public static let encodingEndLineWithCarriageReturn = Base64EncodingOptions(rawValue: UInt(1 << 4)) 18 | public static let encodingEndLineWithLineFeed = Base64EncodingOptions(rawValue: UInt(1 << 5)) 19 | } 20 | 21 | struct Base64DecodingOptions : OptionSet { 22 | public let rawValue : UInt 23 | public init(rawValue: UInt) { self.rawValue = rawValue } 24 | 25 | public static let ignoreUnknownCharacters = Base64DecodingOptions(rawValue: UInt(1 << 0)) 26 | } 27 | 28 | /* Create an NSData from a Base-64 encoded NSString using the given options. By default, returns nil when the input is not recognized as valid Base-64. 29 | */ 30 | init?(base64Encoded base64String: String, options: Base64DecodingOptions = []) { 31 | guard let decodedBytes = Data.base64DecodeBytes(base64String.utf8, options: options) else { 32 | return nil 33 | } 34 | self.init(storage: .buffer(decodedBytes)) 35 | } 36 | 37 | /* Create a Base-64 encoded String from the receiver's contents using the given options. 38 | */ 39 | func base64EncodedString(_ options: Base64EncodingOptions = []) -> String { 40 | 41 | let encodedBytes = Data.base64EncodeBytes(self, options: options) 42 | return String(encodedBytes.lazy.map { Character(UnicodeScalar($0)) }) 43 | } 44 | 45 | /* Create an Data from a Base-64, UTF-8 encoded NSData. By default, returns nil when the input is not recognized as valid Base-64. 46 | */ 47 | init?(base64Encoded base64Data: Data, options: Base64DecodingOptions = []) { 48 | guard let decodedBytes = Data.base64DecodeBytes(base64Data, options: options) else { 49 | return nil 50 | } 51 | self.init(storage: .buffer(decodedBytes)) 52 | } 53 | 54 | /* Create a Base-64, UTF-8 encoded Data from the receiver's contents using the given options. 55 | */ 56 | func base64EncodedData(_ options: Base64EncodingOptions = []) -> Data { 57 | let encodedBytes = Data.base64EncodeBytes(self, options: options) 58 | return Data(storage: .buffer(encodedBytes)) 59 | } 60 | 61 | /** 62 | The ranges of ASCII characters that are used to encode data in Base64. 63 | */ 64 | private static let base64ByteMappings: [Range] = [ 65 | 65 ..< 91, // A-Z 66 | 97 ..< 123, // a-z 67 | 48 ..< 58, // 0-9 68 | 43 ..< 44, // + 69 | 47 ..< 48, // / 70 | ] 71 | /** 72 | Padding character used when the number of bytes to encode is not divisible by 3 73 | */ 74 | private static let base64Padding : UInt8 = 61 // = 75 | 76 | /** 77 | This method takes a byte with a character from Base64-encoded string 78 | and gets the binary value that the character corresponds to. 79 | 80 | - parameter byte: The byte with the Base64 character. 81 | - returns: Base64DecodedByte value containing the result (Valid , Invalid, Padding) 82 | */ 83 | private enum Base64DecodedByte { 84 | case valid(UInt8) 85 | case invalid 86 | case padding 87 | } 88 | 89 | private static func base64DecodeByte(_ byte: UInt8) -> Base64DecodedByte { 90 | guard byte != base64Padding else {return .padding} 91 | var decodedStart: UInt8 = 0 92 | for range in base64ByteMappings { 93 | if range.contains(byte) { 94 | let result = decodedStart + (byte - range.lowerBound) 95 | return .valid(result) 96 | } 97 | decodedStart += range.upperBound - range.lowerBound 98 | } 99 | return .invalid 100 | } 101 | 102 | /** 103 | This method takes six bits of binary data and encodes it as a character 104 | in Base64. 105 | 106 | The value in the byte must be less than 64, because a Base64 character 107 | can only represent 6 bits. 108 | 109 | - parameter byte: The byte to encode 110 | - returns: The ASCII value for the encoded character. 111 | */ 112 | private static func base64EncodeByte(_ byte: UInt8) -> UInt8 { 113 | assert(byte < 64) 114 | var decodedStart: UInt8 = 0 115 | for range in base64ByteMappings { 116 | let decodedRange = decodedStart ..< decodedStart + (range.upperBound - range.lowerBound) 117 | if decodedRange.contains(byte) { 118 | return range.lowerBound + (byte - decodedStart) 119 | } 120 | decodedStart += range.upperBound - range.lowerBound 121 | } 122 | return 0 123 | } 124 | 125 | 126 | /** 127 | This method decodes Base64-encoded data. 128 | 129 | If the input contains any bytes that are not valid Base64 characters, 130 | this will return nil. 131 | 132 | - parameter bytes: The Base64 bytes 133 | - parameter options: Options for handling invalid input 134 | - returns: The decoded bytes. 135 | */ 136 | private static func base64DecodeBytes(_ bytes: C, options: Base64DecodingOptions = []) -> Data.Buffer? where C: Collection, C.Element == UInt8 { 137 | var decodedBytes = Data.Buffer() 138 | decodedBytes.reserveCapacity((bytes.count/3)*2) 139 | 140 | var currentByte : UInt8 = 0 141 | var validCharacterCount = 0 142 | var paddingCount = 0 143 | var index = 0 144 | 145 | 146 | for base64Char in bytes { 147 | 148 | let value : UInt8 149 | 150 | switch base64DecodeByte(base64Char) { 151 | case .valid(let v): 152 | value = v 153 | validCharacterCount += 1 154 | case .invalid: 155 | if options.contains(.ignoreUnknownCharacters) { 156 | continue 157 | } else { 158 | return nil 159 | } 160 | case .padding: 161 | paddingCount += 1 162 | continue 163 | } 164 | 165 | //padding found in the middle of the sequence is invalid 166 | if paddingCount > 0 { 167 | return nil 168 | } 169 | 170 | switch index%4 { 171 | case 0: 172 | currentByte = (value << 2) 173 | case 1: 174 | currentByte |= (value >> 4) 175 | decodedBytes.append(currentByte) 176 | currentByte = (value << 4) 177 | case 2: 178 | currentByte |= (value >> 2) 179 | decodedBytes.append(currentByte) 180 | currentByte = (value << 6) 181 | case 3: 182 | currentByte |= value 183 | decodedBytes.append(currentByte) 184 | default: 185 | fatalError() 186 | } 187 | 188 | index += 1 189 | } 190 | 191 | guard (validCharacterCount + paddingCount)%4 == 0 else { 192 | //invalid character count 193 | return nil 194 | } 195 | return decodedBytes 196 | } 197 | 198 | 199 | /** 200 | This method encodes data in Base64. 201 | 202 | - parameter bytes: The bytes you want to encode 203 | - parameter options: Options for formatting the result 204 | - returns: The Base64-encoding for those bytes. 205 | */ 206 | private static func base64EncodeBytes(_ bytes: C, options: Base64EncodingOptions = []) -> Data.Buffer where C: Collection, C.Element == UInt8 { 207 | var result = Data.Buffer() 208 | result.reserveCapacity((bytes.count/3)*4) 209 | 210 | let lineOptions : (lineLength : Int, separator : Data.Buffer)? = { 211 | let lineLength: Int 212 | 213 | if options.contains(.encoding64CharacterLineLength) { lineLength = 64 } 214 | else if options.contains(.encoding76CharacterLineLength) { lineLength = 76 } 215 | else { 216 | return nil 217 | } 218 | 219 | var separator = Data.Buffer() 220 | if options.contains(.encodingEndLineWithCarriageReturn) { separator.append(13) } 221 | if options.contains(.encodingEndLineWithLineFeed) { separator.append(10) } 222 | 223 | //if the kind of line ending to insert is not specified, the default line ending is Carriage Return + Line Feed. 224 | if separator.count == 0 {separator = [13,10]} 225 | 226 | return (lineLength,separator) 227 | }() 228 | 229 | var currentLineCount = 0 230 | let appendByteToResult : (UInt8) -> () = { 231 | result.append($0) 232 | currentLineCount += 1 233 | if let options = lineOptions, currentLineCount == options.lineLength { 234 | result.append(contentsOf: options.separator) 235 | currentLineCount = 0 236 | } 237 | } 238 | 239 | var currentByte : UInt8 = 0 240 | 241 | for (index,value) in bytes.enumerated() { 242 | switch index%3 { 243 | case 0: 244 | currentByte = (value >> 2) 245 | appendByteToResult(Data.base64EncodeByte(currentByte)) 246 | currentByte = ((value << 6) >> 2) 247 | case 1: 248 | currentByte |= (value >> 4) 249 | appendByteToResult(Data.base64EncodeByte(currentByte)) 250 | currentByte = ((value << 4) >> 2) 251 | case 2: 252 | currentByte |= (value >> 6) 253 | appendByteToResult(Data.base64EncodeByte(currentByte)) 254 | currentByte = ((value << 2) >> 2) 255 | appendByteToResult(Data.base64EncodeByte(currentByte)) 256 | default: 257 | fatalError() 258 | } 259 | } 260 | //add padding 261 | switch bytes.count%3 { 262 | case 0: break //no padding needed 263 | case 1: 264 | appendByteToResult(Data.base64EncodeByte(currentByte)) 265 | appendByteToResult(self.base64Padding) 266 | appendByteToResult(self.base64Padding) 267 | case 2: 268 | appendByteToResult(Data.base64EncodeByte(currentByte)) 269 | appendByteToResult(self.base64Padding) 270 | default: 271 | fatalError() 272 | } 273 | return result 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/ComparisonResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ComparisonResult.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 6/30/16. 6 | // Copyright © 2016 PureSwift. All rights reserved. 7 | // 8 | 9 | public enum ComparisonResult: Int { 10 | 11 | case orderedAscending = -1 12 | case orderedSame 13 | case orderedDescending 14 | } 15 | 16 | // MARK: - Implementation 17 | 18 | public extension Comparable { 19 | 20 | /// Compares the reciever with another and returns their order. 21 | func compare(_ other: Self) -> ComparisonResult { 22 | if self < other { 23 | return .orderedAscending 24 | } 25 | if self > other { 26 | return .orderedDescending 27 | } 28 | return .orderedSame 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/ContiguousBytes.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2018 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | //===--- ContiguousBytes --------------------------------------------------===// 14 | 15 | /// Indicates that the conforming type is a contiguous collection of raw bytes 16 | /// whose underlying storage is directly accessible by withUnsafeBytes. 17 | public protocol ContiguousBytes { 18 | /// Calls the given closure with the contents of underlying storage. 19 | /// 20 | /// - note: Calling `withUnsafeBytes` multiple times does not guarantee that 21 | /// the same buffer pointer will be passed in every time. 22 | /// - warning: The buffer argument to the body should not be stored or used 23 | /// outside of the lifetime of the call to the closure. 24 | func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R 25 | } 26 | 27 | //===--- Collection Conformances ------------------------------------------===// 28 | 29 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 30 | extension Array : ContiguousBytes where Element == UInt8 { } 31 | 32 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 33 | extension ArraySlice : ContiguousBytes where Element == UInt8 { } 34 | 35 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 36 | extension ContiguousArray : ContiguousBytes where Element == UInt8 { } 37 | 38 | //===--- Pointer Conformances ---------------------------------------------===// 39 | 40 | extension UnsafeRawBufferPointer : ContiguousBytes { 41 | @inlinable 42 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 43 | return try body(self) 44 | } 45 | } 46 | 47 | extension UnsafeMutableRawBufferPointer : ContiguousBytes { 48 | @inlinable 49 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 50 | return try body(UnsafeRawBufferPointer(self)) 51 | } 52 | } 53 | 54 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 55 | extension UnsafeBufferPointer : ContiguousBytes where Element == UInt8 { 56 | @inlinable 57 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 58 | return try body(UnsafeRawBufferPointer(self)) 59 | } 60 | } 61 | 62 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 63 | extension UnsafeMutableBufferPointer : ContiguousBytes where Element == UInt8 { 64 | @inlinable 65 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 66 | return try body(UnsafeRawBufferPointer(self)) 67 | } 68 | } 69 | 70 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 71 | extension EmptyCollection : ContiguousBytes where Element == UInt8 { 72 | @inlinable 73 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 74 | return try body(UnsafeRawBufferPointer(start: nil, count: 0)) 75 | } 76 | } 77 | 78 | // FIXME: When possible, expand conformance to `where Element : Trivial`. 79 | extension CollectionOfOne : ContiguousBytes where Element == UInt8 { 80 | @inlinable 81 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R { 82 | let element = self.first! 83 | return try Swift.withUnsafeBytes(of: element) { 84 | return try body($0) 85 | } 86 | } 87 | } 88 | 89 | //===--- Conditional Conformances -----------------------------------------===// 90 | 91 | extension Slice : ContiguousBytes where Base : ContiguousBytes { 92 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { 93 | let offset = base.distance(from: base.startIndex, to: self.startIndex) 94 | return try base.withUnsafeBytes { ptr in 95 | let slicePtr = ptr.baseAddress?.advanced(by: offset) 96 | let sliceBuffer = UnsafeRawBufferPointer(start: slicePtr, count: self.count) 97 | return try body(sliceBuffer) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 6/28/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | #if canImport(Darwin) 10 | import Darwin.C 11 | #elseif canImport(Glibc) 12 | import Glibc 13 | #endif 14 | 15 | /// Encapsulates data. 16 | @frozen 17 | public struct Data: RandomAccessCollection, MutableCollection, RangeReplaceableCollection, MutableDataProtocol, ContiguousBytes { 18 | 19 | // MARK: - Properties 20 | 21 | @usableFromInline 22 | internal private(set) var storage: Storage 23 | 24 | // MARK: - Initialization 25 | 26 | @inlinable 27 | internal init(storage: Storage) { 28 | self.storage = storage 29 | } 30 | 31 | /// Initialize a `Data` with copied memory content. 32 | /// 33 | /// - parameter bytes: A pointer to the memory. It will be copied. 34 | /// - parameter count: The number of bytes to copy. 35 | @inlinable // This is @inlinable as a trivial initializer. 36 | public init(bytes: UnsafeRawPointer, count: Int) { 37 | self.storage = .init(UnsafeRawBufferPointer(start: bytes, count: count)) 38 | } 39 | 40 | /// Initialize a `Data` with copied memory content. 41 | /// 42 | /// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`. 43 | @inlinable // This is @inlinable as a trivial, generic initializer. 44 | public init(buffer: UnsafeBufferPointer) { 45 | self.storage = .init(UnsafeRawBufferPointer(buffer)) 46 | } 47 | 48 | /// Initialize a `Data` with copied memory content. 49 | /// 50 | /// - parameter buffer: A buffer pointer to copy. The size is calculated from `SourceType` and `buffer.count`. 51 | @inlinable // This is @inlinable as a trivial, generic initializer. 52 | public init(buffer: UnsafeMutableBufferPointer) { 53 | self.storage = .init(UnsafeRawBufferPointer(buffer)) 54 | } 55 | 56 | /// Initialize a `Data` with a repeating byte pattern 57 | /// 58 | /// - parameter repeatedValue: A byte to initialize the pattern 59 | /// - parameter count: The number of bytes the data initially contains initialized to the repeatedValue 60 | @inlinable // This is @inlinable as a convenience initializer. 61 | public init(repeating repeatedValue: UInt8, count: Int) { 62 | self.init(count: count) 63 | if count > 0 { 64 | self.withUnsafeMutableBytes { (buffer: UnsafeMutableRawBufferPointer) -> Void in 65 | memset(buffer.baseAddress!, Int32(repeatedValue), buffer.count) 66 | } 67 | } 68 | } 69 | 70 | /// Initialize a `Data` with the specified size. 71 | /// 72 | /// This initializer doesn't necessarily allocate the requested memory right away. `Data` allocates additional memory as needed, so `capacity` simply establishes the initial capacity. When it does allocate the initial memory, though, it allocates the specified amount. 73 | /// 74 | /// This method sets the `count` of the data to 0. 75 | /// 76 | /// If the capacity specified in `capacity` is greater than four memory pages in size, this may round the amount of requested memory up to the nearest full page. 77 | /// 78 | /// - parameter capacity: The size of the data. 79 | @inlinable // This is @inlinable as a trivial initializer. 80 | public init(capacity: Int) { 81 | self.storage = .init(capacity: capacity) 82 | } 83 | 84 | /// Initialize a `Data` with the specified count of zeroed bytes. 85 | /// 86 | /// - parameter count: The number of bytes the data initially contains. 87 | @inlinable // This is @inlinable as a trivial initializer. 88 | public init(count: Int) { 89 | self.storage = .init(count: count) 90 | } 91 | 92 | /// Initialize an empty `Data`. 93 | @inlinable // This is @inlinable as a trivial initializer. 94 | public init() { 95 | self.init(storage: .empty) 96 | } 97 | 98 | // slightly faster paths for common sequences 99 | @inlinable // This is @inlinable as an important generic funnel point, despite being a non-trivial initializer. 100 | public init(_ elements: S) where S.Element == UInt8 { 101 | // If the sequence is already contiguous, access the underlying raw memory directly. 102 | if let contiguous = elements as? ContiguousBytes { 103 | self.storage = contiguous.withUnsafeBytes { return .init($0) } 104 | return 105 | } 106 | 107 | // The sequence might still be able to provide direct access to typed memory. 108 | // NOTE: It's safe to do this because we're already guarding on S's element as `UInt8`. This would not be safe on arbitrary sequences. 109 | let storage = elements.withContiguousStorageIfAvailable { Storage(UnsafeRawBufferPointer($0)) } 110 | 111 | if let storage = storage { 112 | self.storage = storage 113 | } else { 114 | // slow path 115 | self.storage = .buffer(.init(elements)) 116 | } 117 | } 118 | 119 | // MARK: - Properties and Functions 120 | 121 | @inlinable // This is @inlinable as trivially forwarding. 122 | public mutating func reserveCapacity(_ minimumCapacity: Int) { 123 | storage.reserveCapacity(minimumCapacity) 124 | } 125 | 126 | /// The number of bytes in the data. 127 | @inlinable // This is @inlinable as trivially forwarding. 128 | public var count: Int { 129 | get { return storage.count } 130 | /* 131 | set { 132 | precondition(newValue >= 0, "count must not be negative") 133 | storage.count = newValue 134 | }*/ 135 | } 136 | 137 | @inlinable // This is @inlinable as trivially computable. 138 | public var regions: CollectionOfOne { 139 | return CollectionOfOne(self) 140 | } 141 | 142 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 143 | public func withUnsafeBytes(_ body: (UnsafeRawBufferPointer) throws -> ResultType) rethrows -> ResultType { 144 | return try storage.withUnsafeBytes(body) 145 | } 146 | 147 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 148 | public mutating func withUnsafeMutableBytes(_ body: (UnsafeMutableRawBufferPointer) throws -> ResultType) rethrows -> ResultType { 149 | return try storage.withUnsafeMutableBytes(body) 150 | } 151 | 152 | // MARK: - 153 | // MARK: Copy Bytes 154 | 155 | /// Copy the contents of the data to a pointer. 156 | /// 157 | /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. 158 | /// - parameter count: The number of bytes to copy. 159 | /// - warning: This method does not verify that the contents at pointer have enough space to hold `count` bytes. 160 | @inlinable // This is @inlinable as trivially forwarding. 161 | public func copyBytes(to pointer: UnsafeMutablePointer, count: Int) { 162 | precondition(count >= 0, "count of bytes to copy must not be negative") 163 | if count == 0 { return } 164 | _copyBytesHelper(to: UnsafeMutableRawPointer(pointer), from: startIndex..<(startIndex + count)) 165 | } 166 | 167 | @inlinable // This is @inlinable as trivially forwarding. 168 | internal func _copyBytesHelper(to pointer: UnsafeMutableRawPointer, from range: Range) { 169 | if range.isEmpty { return } 170 | storage.copyBytes(to: pointer, from: range) 171 | } 172 | 173 | /// Copy a subset of the contents of the data to a pointer. 174 | /// 175 | /// - parameter pointer: A pointer to the buffer you wish to copy the bytes into. 176 | /// - parameter range: The range in the `Data` to copy. 177 | /// - warning: This method does not verify that the contents at pointer have enough space to hold the required number of bytes. 178 | @inlinable // This is @inlinable as trivially forwarding. 179 | public func copyBytes(to pointer: UnsafeMutablePointer, from range: Range) { 180 | _copyBytesHelper(to: pointer, from: range) 181 | } 182 | 183 | // Copy the contents of the data into a buffer. 184 | /// 185 | /// This function copies the bytes in `range` from the data into the buffer. If the count of the `range` is greater than `MemoryLayout.stride * buffer.count` then the first N bytes will be copied into the buffer. 186 | /// - precondition: The range must be within the bounds of the data. Otherwise `fatalError` is called. 187 | /// - parameter buffer: A buffer to copy the data into. 188 | /// - parameter range: A range in the data to copy into the buffer. If the range is empty, this function will return 0 without copying anything. If the range is nil, as much data as will fit into `buffer` is copied. 189 | /// - returns: Number of bytes copied into the destination buffer. 190 | @inlinable // This is @inlinable as generic and reasonably small. 191 | public func copyBytes(to buffer: UnsafeMutableBufferPointer, from range: Range? = nil) -> Int { 192 | let cnt = count 193 | guard cnt > 0 else { return 0 } 194 | 195 | let copyRange : Range 196 | if let r = range { 197 | guard !r.isEmpty else { return 0 } 198 | copyRange = r.lowerBound..<(r.lowerBound + Swift.min(buffer.count * MemoryLayout.stride, r.upperBound - r.lowerBound)) 199 | } else { 200 | copyRange = 0...stride, cnt) 201 | } 202 | 203 | guard !copyRange.isEmpty else { return 0 } 204 | 205 | _copyBytesHelper(to: buffer.baseAddress!, from: copyRange) 206 | return copyRange.upperBound - copyRange.lowerBound 207 | } 208 | 209 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 210 | internal mutating func _append(_ buffer : UnsafeBufferPointer) { 211 | if buffer.isEmpty { return } 212 | storage.append(contentsOf: UnsafeRawBufferPointer(buffer)) 213 | } 214 | 215 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 216 | public mutating func append(_ bytes: UnsafePointer, count: Int) { 217 | if count == 0 { return } 218 | _append(UnsafeBufferPointer(start: bytes, count: count)) 219 | } 220 | 221 | public mutating func append(_ other: Data) { 222 | guard other.count > 0 else { return } 223 | other.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) in 224 | storage.append(contentsOf: buffer) 225 | } 226 | } 227 | 228 | /// Append a buffer of bytes to the data. 229 | /// 230 | /// - parameter buffer: The buffer of bytes to append. The size is calculated from `SourceType` and `buffer.count`. 231 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 232 | public mutating func append(_ buffer : UnsafeBufferPointer) { 233 | _append(buffer) 234 | } 235 | 236 | @inlinable // This is @inlinable as trivially forwarding. 237 | public mutating func append(contentsOf bytes: [UInt8]) { 238 | bytes.withUnsafeBufferPointer { (buffer: UnsafeBufferPointer) -> Void in 239 | _append(buffer) 240 | } 241 | } 242 | 243 | @inlinable // This is @inlinable as an important generic funnel point, despite being non-trivial. 244 | public mutating func append(contentsOf elements: S) where S.Element == UInt8 { 245 | // If the sequence is already contiguous, access the underlying raw memory directly. 246 | if let contiguous = elements as? ContiguousBytes { 247 | contiguous.withUnsafeBytes { 248 | storage.append(contentsOf: $0) 249 | } 250 | return 251 | } 252 | 253 | // The sequence might still be able to provide direct access to typed memory. 254 | // NOTE: It's safe to do this because we're already guarding on S's element as `UInt8`. This would not be safe on arbitrary sequences. 255 | var appended = false 256 | elements.withContiguousStorageIfAvailable { 257 | storage.append(contentsOf: UnsafeRawBufferPointer($0)) 258 | appended = true 259 | } 260 | 261 | guard !appended else { return } 262 | 263 | // inefficient path, forget inline storage 264 | var buffer: Data.Buffer 265 | switch storage { 266 | case .empty: 267 | buffer = .init() 268 | case let .inline(inline): 269 | buffer = .init(inline) 270 | case let .buffer(bufffer): 271 | buffer = bufffer 272 | } 273 | buffer.append(contentsOf: elements) 274 | self.storage = .buffer(buffer) 275 | } 276 | 277 | /// Return a new copy of the data in a specified range. 278 | /// 279 | /// - parameter range: The range to copy. 280 | public func subdata(in range: Range) -> Data { 281 | if isEmpty || range.upperBound - range.lowerBound == 0 { 282 | return Data() 283 | } 284 | let slice = self[range] 285 | 286 | return slice.withUnsafeBytes { (buffer: UnsafeRawBufferPointer) -> Data in 287 | return Data(bytes: buffer.baseAddress!, count: buffer.count) 288 | } 289 | } 290 | 291 | public func advanced(by amount: Int) -> Data { 292 | let length = count - amount 293 | precondition(length > 0) 294 | return withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> Data in 295 | return Data(bytes: ptr.baseAddress!.advanced(by: amount), count: length) 296 | } 297 | } 298 | 299 | // MARK: - 300 | // MARK: Index and Subscript 301 | 302 | /// Sets or returns the byte at the specified index. 303 | @inlinable // This is @inlinable as trivially forwarding. 304 | public subscript(index: Index) -> UInt8 { 305 | get { 306 | return storage[index] 307 | } 308 | set(newValue) { 309 | storage[index] = newValue 310 | } 311 | } 312 | 313 | @inlinable // This is @inlinable as trivially forwarding. 314 | public subscript(bounds: Range) -> Data { 315 | get { 316 | return storage[bounds] 317 | } 318 | set { 319 | replaceSubrange(bounds, with: newValue) 320 | } 321 | } 322 | 323 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 324 | public subscript(_ rangeExpression: R) -> Data 325 | where R.Bound: FixedWidthInteger { 326 | get { 327 | let lower = R.Bound(startIndex) 328 | let upper = R.Bound(endIndex) 329 | let range = rangeExpression.relative(to: lower.. = start.. = start.. Index { 367 | return i - 1 368 | } 369 | 370 | @inlinable // This is @inlinable as trivially computable. 371 | public func index(after i: Index) -> Index { 372 | return i + 1 373 | } 374 | 375 | @inlinable // This is @inlinable as trivially computable. 376 | public var indices: Range { 377 | get { 378 | return startIndex.. Bool { 390 | let length1 = d1.count 391 | if length1 != d2.count { 392 | return false 393 | } 394 | if length1 > 0 { 395 | return d1.withUnsafeBytes { (b1: UnsafeRawBufferPointer) in 396 | return d2.withUnsafeBytes { (b2: UnsafeRawBufferPointer) in 397 | return memcmp(b1.baseAddress!, b2.baseAddress!, b2.count) == 0 398 | } 399 | } 400 | } 401 | return true 402 | } 403 | } 404 | 405 | // MARK: - Hashable 406 | 407 | extension Data: Hashable { 408 | 409 | /// The hash value for the data. 410 | @inline(never) // This is not inlinable as emission into clients could cause cross-module inconsistencies if they are not all recompiled together. 411 | public func hash(into hasher: inout Hasher) { 412 | storage.hash(into: &hasher) 413 | } 414 | } 415 | 416 | // MARK: - Sequence 417 | 418 | extension Data: Sequence { 419 | 420 | /// An iterator over the contents of the data. 421 | /// 422 | /// The iterator will increment byte-by-byte. 423 | @inlinable // This is @inlinable as trivially computable. 424 | public func makeIterator() -> IndexingIterator { 425 | return IndexingIterator(_elements: self) 426 | } 427 | } 428 | 429 | // MARK: - CustomStringConvertible 430 | 431 | extension Data: CustomStringConvertible { 432 | 433 | /// A human-readable description for the data. 434 | public var description: String { 435 | return "\(self.count) bytes" 436 | } 437 | } 438 | 439 | // MARK: - CustomDebugStringConvertible 440 | 441 | extension Data: CustomDebugStringConvertible { 442 | 443 | /// A human-readable debug description for the data. 444 | public var debugDescription: String { 445 | return self.description 446 | } 447 | } 448 | 449 | // MARK: - CustomReflectable 450 | 451 | extension Data: CustomReflectable { 452 | 453 | public var customMirror: Mirror { 454 | let nBytes = self.count 455 | var children: [(label: String?, value: Any)] = [] 456 | children.append((label: "count", value: nBytes)) 457 | 458 | self.withUnsafeBytes { (bytes : UnsafeRawBufferPointer) in 459 | children.append((label: "pointer", value: bytes.baseAddress!)) 460 | } 461 | 462 | // Minimal size data is output as an array 463 | if nBytes < 64 { 464 | children.append((label: "bytes", value: Array(self[startIndex.. 512 | typealias Element = UInt8 513 | } 514 | 515 | // MARK: - Buffer 516 | 517 | internal extension Data { 518 | 519 | @usableFromInline 520 | typealias Buffer = ContiguousArray 521 | } 522 | 523 | internal extension Data.Buffer { 524 | 525 | @usableFromInline 526 | init(_ inline: Data.Inline) { 527 | self.init() 528 | self.reserveCapacity(inline.count) 529 | /// FIXME: Improve 530 | //self.append(contentsOf: inline) 531 | self = inline.withUnsafeBytes { Data.Buffer($0) } 532 | } 533 | 534 | @inlinable 535 | var range: Range { 536 | return startIndex ..< endIndex 537 | } 538 | } 539 | 540 | // MARK: - Storage 541 | 542 | internal extension Data { 543 | 544 | @usableFromInline 545 | enum Storage { 546 | 547 | case empty 548 | case inline(Data.Inline) 549 | case buffer(Buffer) 550 | } 551 | } 552 | 553 | internal extension Data.Storage { 554 | 555 | @inlinable // This is @inlinable as a trivial initializer. 556 | init(_ buffer: UnsafeRawBufferPointer) { 557 | if buffer.count == 0 { 558 | self = .empty 559 | } else if Data.Inline.canStore(count: buffer.count) { 560 | self = .inline(Data.Inline(buffer)) 561 | } else { 562 | self = .buffer(Data.Buffer(buffer)) 563 | } 564 | } 565 | 566 | @inlinable // This is @inlinable as a trivial initializer. 567 | init(capacity: Int) { 568 | if capacity == 0 { 569 | self = .empty 570 | } else if Data.Inline.canStore(count: capacity) { 571 | self = .inline(Data.Inline()) 572 | } else { 573 | var buffer = Data.Buffer() 574 | buffer.reserveCapacity(capacity) 575 | self = .buffer(buffer) 576 | } 577 | } 578 | 579 | @inlinable // This is @inlinable as a trivial initializer. 580 | init(count: Int) { 581 | if count == 0 { 582 | self = .empty 583 | } else if Data.Inline.canStore(count: count) { 584 | self = .inline(Data.Inline(count: count)) 585 | } else { 586 | self = .buffer(Data.Buffer(repeating: 0x00, count: count)) 587 | } 588 | } 589 | 590 | @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. 591 | mutating func reserveCapacity(_ minimumCapacity: Int) { 592 | guard minimumCapacity > 0 else { return } 593 | switch self { 594 | case .empty: 595 | if Data.Inline.canStore(count: minimumCapacity) { 596 | self = .inline(Data.Inline()) 597 | } else { 598 | var buffer = Data.Buffer() 599 | buffer.reserveCapacity(minimumCapacity) 600 | self = .buffer(buffer) 601 | } 602 | case .inline(let inline): 603 | guard minimumCapacity > inline.capacity else { return } 604 | // we know we are going to be heap promoted 605 | var buffer = Data.Buffer() 606 | buffer.reserveCapacity(minimumCapacity) 607 | self = .buffer(buffer) 608 | case .buffer(var buffer): 609 | guard minimumCapacity > buffer.capacity else { return } 610 | self = .empty // make sure ARC has the buffer uniquely referenced 611 | buffer.reserveCapacity(minimumCapacity) 612 | self = .buffer(buffer) 613 | } 614 | } 615 | 616 | @inlinable // This is @inlinable as reasonably small. 617 | var count: Int { 618 | get { 619 | switch self { 620 | case .empty: return 0 621 | case .inline(let inline): return inline.count 622 | case .buffer(let buffer): return buffer.count 623 | } 624 | }/* 625 | set { 626 | // HACK: The definition of this inline function takes an inout reference to self, giving the optimizer a unique referencing guarantee. 627 | // This allows us to avoid excessive retain-release traffic around modifying enum values, and inlining the function then avoids the additional frame. 628 | @inline(__always) 629 | func apply(_ representation: inout Data.Storage, _ newValue: Int) -> Data.Storage? { 630 | switch representation { 631 | case .empty: 632 | if newValue == 0 { 633 | return nil 634 | } else if Data.Inline.canStore(count: newValue) { 635 | return .inline(Data.Inline(count: newValue)) 636 | } else { 637 | return .buffer(Data.Buffer(repeating: 0x00, count: newValue)) 638 | } 639 | case .inline(var inline): 640 | if newValue == 0 { 641 | return .empty 642 | } else if Data.Inline.canStore(count: newValue) { 643 | guard inline.count != newValue else { return nil } 644 | inline.count = newValue 645 | return .inline(inline) 646 | } else { 647 | var buffer = Data.Buffer(inline) 648 | buffer.count = newValue 649 | return .buffer(buffer) 650 | } 651 | case .buffer(var buffer): 652 | if newValue == 0 && buffer.startIndex == 0 { 653 | return .empty 654 | } else if buffer.startIndex == 0 && Data.Inline.canStore(count: newValue) { 655 | return .inline(buffer.withUnsafeBytes { Data.Inline($0) }) 656 | } else { 657 | guard buffer.count != newValue else { return nil} 658 | representation = .empty 659 | buffer.count = newValue 660 | return .buffer(buffer) 661 | } 662 | } 663 | } 664 | 665 | if let rep = apply(&self, newValue) { 666 | self = rep 667 | } 668 | }*/ 669 | } 670 | 671 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 672 | func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { 673 | switch self { 674 | case .empty: 675 | let empty = Data.Inline() 676 | return try empty.withUnsafeBytes(apply) 677 | case .inline(let inline): 678 | return try inline.withUnsafeBytes(apply) 679 | case .buffer(let buffer): 680 | return try buffer.withUnsafeBytes(apply) 681 | } 682 | } 683 | 684 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 685 | mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { 686 | switch self { 687 | case .empty: 688 | var empty = Data.Inline() 689 | return try empty.withUnsafeMutableBytes(apply) 690 | case .inline(var inline): 691 | defer { self = .inline(inline) } 692 | return try inline.withUnsafeMutableBytes(apply) 693 | case .buffer(var buffer): 694 | self = .empty 695 | defer { self = .buffer(buffer) } 696 | return try buffer.withUnsafeMutableBytes(apply) 697 | } 698 | } 699 | 700 | @inlinable // This is @inlinable as reasonably small. 701 | mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { 702 | switch self { 703 | case .empty: 704 | self = Data.Storage(buffer) 705 | case .inline(var inline): 706 | if Data.Inline.canStore(count: inline.count + buffer.count) { 707 | inline.append(contentsOf: buffer) 708 | self = .inline(inline) 709 | } else { 710 | var newBuffer = Data.Buffer(inline) 711 | newBuffer.append(contentsOf: buffer) 712 | self = .buffer(newBuffer) 713 | } 714 | case .buffer(var buffer): 715 | self = .empty 716 | defer { self = .buffer(buffer) } 717 | buffer.append(contentsOf: buffer) 718 | } 719 | } 720 | 721 | @inlinable // This is @inlinable as trivially forwarding. 722 | subscript(index: Data.Index) -> UInt8 { 723 | get { 724 | switch self { 725 | case .empty: preconditionFailure("index \(index) out of range of 0") 726 | case .inline(let inline): return inline[index] 727 | case .buffer(let buffer): return buffer[index] 728 | } 729 | } 730 | set { 731 | switch self { 732 | case .empty: preconditionFailure("index \(index) out of range of 0") 733 | case .inline(var inline): 734 | inline[index] = newValue 735 | self = .inline(inline) 736 | case .buffer(var buffer): 737 | self = .empty // for ARC, to keep unique reference count 738 | buffer[index] = newValue 739 | self = .buffer(buffer) 740 | } 741 | } 742 | } 743 | 744 | @inlinable // This is @inlinable as reasonably small. 745 | subscript(bounds: Range) -> Data { 746 | get { 747 | switch self { 748 | case .empty: 749 | precondition(bounds.lowerBound == 0 && (bounds.upperBound - bounds.lowerBound) == 0, "Range \(bounds) out of bounds 0..<0") 750 | return Data() 751 | case .inline(let inline): 752 | precondition(bounds.upperBound <= inline.count, "Range \(bounds) out of bounds 0..<\(inline.count)") 753 | if bounds.lowerBound == 0 { 754 | var newInline = inline 755 | newInline.count = bounds.upperBound 756 | return Data(storage: .inline(newInline)) 757 | } else { 758 | var newInline = Data.Inline() 759 | inline.withUnsafeBytes { newInline.append(contentsOf: UnsafeRawBufferPointer(rebasing: $0[bounds])) } 760 | return Data(storage: .inline(newInline)) 761 | } 762 | case .buffer(let buffer): 763 | precondition(buffer.startIndex <= bounds.lowerBound, "Range \(bounds) out of bounds \(buffer.range)") 764 | precondition(bounds.lowerBound <= buffer.endIndex, "Range \(bounds) out of bounds \(buffer.range)") 765 | precondition(buffer.startIndex <= bounds.upperBound, "Range \(bounds) out of bounds \(buffer.range)") 766 | precondition(bounds.upperBound <= buffer.endIndex, "Range \(bounds) out of bounds \(buffer.range)") 767 | if bounds.lowerBound == 0 && bounds.upperBound == 0 { 768 | return Data() 769 | } else if Data.Inline.canStore(count: bounds.count) { 770 | var newInline = Data.Inline() 771 | buffer.withUnsafeBytes { newInline.append(contentsOf: UnsafeRawBufferPointer(rebasing: $0[bounds])) } 772 | return Data(storage: .inline(newInline)) 773 | } else { 774 | var newBuffer = Data.Buffer() 775 | newBuffer.reserveCapacity(bounds.count) 776 | newBuffer.append(contentsOf: buffer[bounds]) 777 | return Data(storage: .buffer(newBuffer)) 778 | } 779 | } 780 | } 781 | } 782 | 783 | @inlinable // This is @inlinable as trivially forwarding. 784 | var startIndex: Int { 785 | switch self { 786 | case .empty: return 0 787 | case .inline: return 0 788 | case .buffer(let buffer): return buffer.startIndex 789 | } 790 | } 791 | 792 | @inlinable // This is @inlinable as trivially forwarding. 793 | var endIndex: Int { 794 | switch self { 795 | case .empty: return 0 796 | case .inline(let inline): return inline.count 797 | case .buffer(let buffer): return buffer.endIndex 798 | } 799 | } 800 | 801 | @inlinable // This is @inlinable as trivially forwarding. 802 | func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { 803 | switch self { 804 | case .empty: 805 | precondition(range.lowerBound == 0 && range.upperBound == 0, "Range \(range) out of bounds 0..<0") 806 | return 807 | case .inline(let inline): 808 | inline.copyBytes(to: pointer, from: range) 809 | case .buffer(let buffer): 810 | buffer.withUnsafeBytes { 811 | let cnt = Swift.min($0.count, range.upperBound - range.lowerBound) 812 | guard cnt > 0 else { return } 813 | pointer.copyMemory(from: $0.baseAddress!.advanced(by: range.lowerBound), byteCount: cnt) 814 | } 815 | } 816 | } 817 | 818 | @inline(__always) // This should always be inlined into Data.hash(into:). 819 | func hash(into hasher: inout Hasher) { 820 | switch self { 821 | case .empty: 822 | hasher.combine(0) 823 | case .inline(let inline): 824 | inline.hash(into: &hasher) 825 | case .buffer(let buffer): 826 | buffer.hash(into: &hasher) 827 | } 828 | } 829 | } 830 | 831 | // MARK: - Inline 832 | 833 | internal extension Data { 834 | 835 | // A small inline buffer of bytes suitable for stack-allocation of small data. 836 | // Inlinability strategy: everything here should be inlined for direct operation on the stack wherever possible. 837 | @usableFromInline 838 | @frozen 839 | struct Inline { 840 | #if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) 841 | @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, 842 | UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) //len //enum 843 | @usableFromInline var bytes: Buffer 844 | #elseif arch(i386) || arch(arm) || arch(wasm32) 845 | @usableFromInline typealias Buffer = (UInt8, UInt8, UInt8, UInt8, 846 | UInt8, UInt8) //len //enum 847 | @usableFromInline var bytes: Buffer 848 | #else 849 | #error("This architecture isn't known. Add it to the 32-bit or 64-bit line.") 850 | #endif 851 | @usableFromInline var length: UInt8 852 | 853 | @inlinable // This is @inlinable as trivially computable. 854 | static func canStore(count: Int) -> Bool { 855 | return count <= MemoryLayout.size 856 | } 857 | 858 | @inlinable // This is @inlinable as a convenience initializer. 859 | init(_ srcBuffer: UnsafeRawBufferPointer) { 860 | self.init(count: srcBuffer.count) 861 | if srcBuffer.count > 0 { 862 | Swift.withUnsafeMutableBytes(of: &bytes) { dstBuffer in 863 | dstBuffer.baseAddress?.copyMemory(from: srcBuffer.baseAddress!, byteCount: srcBuffer.count) 864 | } 865 | } 866 | } 867 | 868 | @inlinable // This is @inlinable as a trivial initializer. 869 | init(count: Int = 0) { 870 | assert(count <= MemoryLayout.size) 871 | #if arch(x86_64) || arch(arm64) || arch(s390x) || arch(powerpc64) || arch(powerpc64le) 872 | bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) 873 | #elseif arch(i386) || arch(arm) || arch(wasm32) 874 | bytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0)) 875 | #else 876 | #error("This architecture isn't known. Add it to the 32-bit or 64-bit line.") 877 | #endif 878 | length = UInt8(count) 879 | } 880 | 881 | @inlinable // This is @inlinable as trivially computable. 882 | var capacity: Int { 883 | return MemoryLayout.size 884 | } 885 | 886 | @inlinable // This is @inlinable as trivially computable. 887 | var count: Int { 888 | get { 889 | return Int(length) 890 | } 891 | set(newValue) { 892 | assert(newValue <= MemoryLayout.size) 893 | length = UInt8(newValue) 894 | } 895 | } 896 | 897 | @inlinable // This is @inlinable as trivially computable. 898 | var startIndex: Int { 899 | return 0 900 | } 901 | 902 | @inlinable // This is @inlinable as trivially computable. 903 | var endIndex: Int { 904 | return count 905 | } 906 | 907 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 908 | func withUnsafeBytes(_ apply: (UnsafeRawBufferPointer) throws -> Result) rethrows -> Result { 909 | let count = Int(length) 910 | return try Swift.withUnsafeBytes(of: bytes) { (rawBuffer) throws -> Result in 911 | return try apply(UnsafeRawBufferPointer(start: rawBuffer.baseAddress, count: count)) 912 | } 913 | } 914 | 915 | @inlinable // This is @inlinable as a generic, trivially forwarding function. 916 | mutating func withUnsafeMutableBytes(_ apply: (UnsafeMutableRawBufferPointer) throws -> Result) rethrows -> Result { 917 | let count = Int(length) 918 | return try Swift.withUnsafeMutableBytes(of: &bytes) { (rawBuffer) throws -> Result in 919 | return try apply(UnsafeMutableRawBufferPointer(start: rawBuffer.baseAddress, count: count)) 920 | } 921 | } 922 | 923 | @inlinable // This is @inlinable as tribially computable. 924 | mutating func append(byte: UInt8) { 925 | let count = self.count 926 | assert(count + 1 <= MemoryLayout.size) 927 | Swift.withUnsafeMutableBytes(of: &bytes) { $0[count] = byte } 928 | self.length += 1 929 | } 930 | 931 | @inlinable // This is @inlinable as trivially computable. 932 | mutating func append(contentsOf buffer: UnsafeRawBufferPointer) { 933 | guard buffer.count > 0 else { return } 934 | assert(count + buffer.count <= MemoryLayout.size) 935 | let cnt = count 936 | _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in 937 | rawBuffer.baseAddress?.advanced(by: cnt).copyMemory(from: buffer.baseAddress!, byteCount: buffer.count) 938 | } 939 | 940 | length += UInt8(buffer.count) 941 | } 942 | 943 | @inlinable // This is @inlinable as trivially computable. 944 | subscript(index: Index) -> UInt8 { 945 | get { 946 | assert(index <= MemoryLayout.size) 947 | precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") 948 | return Swift.withUnsafeBytes(of: bytes) { rawBuffer -> UInt8 in 949 | return rawBuffer[index] 950 | } 951 | } 952 | set(newValue) { 953 | assert(index <= MemoryLayout.size) 954 | precondition(index < length, "index \(index) is out of bounds of 0..<\(length)") 955 | Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in 956 | rawBuffer[index] = newValue 957 | } 958 | } 959 | } 960 | 961 | @inlinable // This is @inlinable as trivially computable. 962 | mutating func resetBytes(in range: Range) { 963 | assert(range.lowerBound <= MemoryLayout.size) 964 | assert(range.upperBound <= MemoryLayout.size) 965 | precondition(range.lowerBound <= length, "index \(range.lowerBound) is out of bounds of 0..<\(length)") 966 | if count < range.upperBound { 967 | count = range.upperBound 968 | } 969 | 970 | let _ = Swift.withUnsafeMutableBytes(of: &bytes) { rawBuffer in 971 | memset(rawBuffer.baseAddress!.advanced(by: range.lowerBound), 0, range.upperBound - range.lowerBound) 972 | } 973 | } 974 | 975 | @usableFromInline // This is not @inlinable as it is a non-trivial, non-generic function. 976 | mutating func replaceSubrange(_ subrange: Range, with replacementBytes: UnsafeRawPointer?, count replacementLength: Int) { 977 | assert(subrange.lowerBound <= MemoryLayout.size) 978 | assert(subrange.upperBound <= MemoryLayout.size) 979 | assert(count - (subrange.upperBound - subrange.lowerBound) + replacementLength <= MemoryLayout.size) 980 | precondition(subrange.lowerBound <= length, "index \(subrange.lowerBound) is out of bounds of 0..<\(length)") 981 | precondition(subrange.upperBound <= length, "index \(subrange.upperBound) is out of bounds of 0..<\(length)") 982 | let currentLength = count 983 | let resultingLength = currentLength - (subrange.upperBound - subrange.lowerBound) + replacementLength 984 | let shift = resultingLength - currentLength 985 | Swift.withUnsafeMutableBytes(of: &bytes) { mutableBytes in 986 | /* shift the trailing bytes */ 987 | let start = subrange.lowerBound 988 | let length = subrange.upperBound - subrange.lowerBound 989 | if shift != 0 { 990 | memmove(mutableBytes.baseAddress!.advanced(by: start + replacementLength), mutableBytes.baseAddress!.advanced(by: start + length), currentLength - start - length) 991 | } 992 | if replacementLength != 0 { 993 | memmove(mutableBytes.baseAddress!.advanced(by: start), replacementBytes!, replacementLength) 994 | } 995 | } 996 | count = resultingLength 997 | } 998 | 999 | @inlinable // This is @inlinable as trivially computable. 1000 | func copyBytes(to pointer: UnsafeMutableRawPointer, from range: Range) { 1001 | precondition(startIndex <= range.lowerBound, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") 1002 | precondition(range.lowerBound <= endIndex, "index \(range.lowerBound) is out of bounds of \(startIndex)..<\(endIndex)") 1003 | precondition(startIndex <= range.upperBound, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") 1004 | precondition(range.upperBound <= endIndex, "index \(range.upperBound) is out of bounds of \(startIndex)..<\(endIndex)") 1005 | 1006 | Swift.withUnsafeBytes(of: bytes) { 1007 | let cnt = Swift.min($0.count, range.upperBound - range.lowerBound) 1008 | guard cnt > 0 else { return } 1009 | pointer.copyMemory(from: $0.baseAddress!.advanced(by: range.lowerBound), byteCount: cnt) 1010 | } 1011 | } 1012 | 1013 | @inline(__always) // This should always be inlined into _Representation.hash(into:). 1014 | func hash(into hasher: inout Hasher) { 1015 | // **NOTE**: this uses `count` (an Int) and NOT `length` (a UInt8) 1016 | // Despite having the same value, they hash differently. InlineSlice and LargeSlice both use `count` (an Int); if you combine the same bytes but with `length` over `count`, you can get a different hash. 1017 | // 1018 | // This affects slices, which are InlineSlice and not Data.Inline: 1019 | // 1020 | // let d = Data([0xFF, 0xFF]) // Data.Inline 1021 | // let s = Data([0, 0xFF, 0xFF]).dropFirst() // InlineSlice 1022 | // assert(s == d) 1023 | // assert(s.hashValue == d.hashValue) 1024 | hasher.combine(count) 1025 | 1026 | Swift.withUnsafeBytes(of: bytes) { 1027 | // We have access to the full byte buffer here, but not all of it is meaningfully used (bytes past self.length may be garbage). 1028 | let bytes = UnsafeRawBufferPointer(start: $0.baseAddress, count: self.count) 1029 | hasher.combine(bytes: bytes) 1030 | } 1031 | } 1032 | } 1033 | } 1034 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/DataProtocol.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2018 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | //===--- DataProtocol -----------------------------------------------------===// 14 | 15 | #if canImport(Darwin) 16 | import Darwin.C 17 | #elseif canImport(Glibc) 18 | import Glibc 19 | #endif 20 | 21 | public protocol DataProtocol : RandomAccessCollection where Element == UInt8, SubSequence : DataProtocol { 22 | // FIXME: Remove in favor of opaque type on `regions`. 23 | associatedtype Regions: BidirectionalCollection where Regions.Element : DataProtocol & ContiguousBytes, Regions.Element.SubSequence : ContiguousBytes 24 | 25 | /// A `BidirectionalCollection` of `DataProtocol` elements which compose a 26 | /// discontiguous buffer of memory. Each region is a contiguous buffer of 27 | /// bytes. 28 | /// 29 | /// The sum of the lengths of the associated regions must equal `self.count` 30 | /// (such that iterating `regions` and iterating `self` produces the same 31 | /// sequence of indices in the same number of index advancements). 32 | var regions: Regions { get } 33 | 34 | /// Returns the first found range of the given data buffer. 35 | /// 36 | /// A default implementation is given in terms of `self.regions`. 37 | func firstRange(of: D, in: R) -> Range? where R.Bound == Index 38 | 39 | /// Returns the last found range of the given data buffer. 40 | /// 41 | /// A default implementation is given in terms of `self.regions`. 42 | func lastRange(of: D, in: R) -> Range? where R.Bound == Index 43 | 44 | /// Copies `count` bytes from the start of the buffer to the destination 45 | /// buffer. 46 | /// 47 | /// A default implementation is given in terms of `copyBytes(to:from:)`. 48 | @discardableResult 49 | func copyBytes(to: UnsafeMutableRawBufferPointer, count: Int) -> Int 50 | 51 | /// Copies `count` bytes from the start of the buffer to the destination 52 | /// buffer. 53 | /// 54 | /// A default implementation is given in terms of `copyBytes(to:from:)`. 55 | @discardableResult 56 | func copyBytes(to: UnsafeMutableBufferPointer, count: Int) -> Int 57 | 58 | /// Copies the bytes from the given range to the destination buffer. 59 | /// 60 | /// A default implementation is given in terms of `self.regions`. 61 | @discardableResult 62 | func copyBytes(to: UnsafeMutableRawBufferPointer, from: R) -> Int where R.Bound == Index 63 | 64 | /// Copies the bytes from the given range to the destination buffer. 65 | /// 66 | /// A default implementation is given in terms of `self.regions`. 67 | @discardableResult 68 | func copyBytes(to: UnsafeMutableBufferPointer, from: R) -> Int where R.Bound == Index 69 | } 70 | 71 | //===--- MutableDataProtocol ----------------------------------------------===// 72 | 73 | public protocol MutableDataProtocol : DataProtocol, MutableCollection, RangeReplaceableCollection { 74 | /// Replaces the contents of the buffer at the given range with zeroes. 75 | /// 76 | /// A default implementation is given in terms of 77 | /// `replaceSubrange(_:with:)`. 78 | mutating func resetBytes(in range: R) where R.Bound == Index 79 | } 80 | 81 | //===--- DataProtocol Extensions ------------------------------------------===// 82 | 83 | extension DataProtocol { 84 | public func firstRange(of data: D) -> Range? { 85 | return self.firstRange(of: data, in: self.startIndex ..< self.endIndex) 86 | } 87 | 88 | public func lastRange(of data: D) -> Range? { 89 | return self.lastRange(of: data, in: self.startIndex ..< self.endIndex) 90 | } 91 | 92 | @discardableResult 93 | public func copyBytes(to ptr: UnsafeMutableRawBufferPointer) -> Int { 94 | return copyBytes(to: ptr, from: self.startIndex ..< self.endIndex) 95 | } 96 | 97 | @discardableResult 98 | public func copyBytes(to ptr: UnsafeMutableBufferPointer) -> Int { 99 | return copyBytes(to: ptr, from: self.startIndex ..< self.endIndex) 100 | } 101 | 102 | @discardableResult 103 | public func copyBytes(to ptr: UnsafeMutableRawBufferPointer, count: Int) -> Int { 104 | return copyBytes(to: ptr, from: self.startIndex ..< self.index(self.startIndex, offsetBy: count)) 105 | } 106 | 107 | @discardableResult 108 | public func copyBytes(to ptr: UnsafeMutableBufferPointer, count: Int) -> Int { 109 | return copyBytes(to: ptr, from: self.startIndex ..< self.index(self.startIndex, offsetBy: count)) 110 | } 111 | 112 | @discardableResult 113 | public func copyBytes(to ptr: UnsafeMutableRawBufferPointer, from range: R) -> Int where R.Bound == Index { 114 | precondition(ptr.baseAddress != nil) 115 | 116 | let concreteRange = range.relative(to: self) 117 | let slice = self[concreteRange] 118 | 119 | // The type isn't contiguous, so we need to copy one region at a time. 120 | var offset = 0 121 | let rangeCount = distance(from: concreteRange.lowerBound, to: concreteRange.upperBound) 122 | var amountToCopy = Swift.min(ptr.count, rangeCount) 123 | for region in slice.regions { 124 | guard amountToCopy > 0 else { 125 | break 126 | } 127 | 128 | region.withUnsafeBytes { buffer in 129 | let offsetPtr = UnsafeMutableRawBufferPointer(rebasing: ptr[offset...]) 130 | let buf = UnsafeRawBufferPointer(start: buffer.baseAddress, count: Swift.min(buffer.count, amountToCopy)) 131 | offsetPtr.copyMemory(from: buf) 132 | offset += buf.count 133 | amountToCopy -= buf.count 134 | } 135 | } 136 | 137 | return offset 138 | } 139 | 140 | @discardableResult 141 | public func copyBytes(to ptr: UnsafeMutableBufferPointer, from range: R) -> Int where R.Bound == Index { 142 | return self.copyBytes(to: UnsafeMutableRawBufferPointer(start: ptr.baseAddress, count: ptr.count * MemoryLayout.stride), from: range) 143 | } 144 | 145 | public func firstRange(of data: D, in range: R) -> Range? where R.Bound == Index { 146 | let r = range.relative(to: self) 147 | let rangeCount = distance(from: r.lowerBound, to: r.upperBound) 148 | if rangeCount < data.count { 149 | return nil 150 | } 151 | var haystackIndex = r.lowerBound 152 | let haystackEnd = index(r.upperBound, offsetBy: -data.count) 153 | while haystackIndex < haystackEnd { 154 | var compareIndex = haystackIndex 155 | var needleIndex = data.startIndex 156 | let needleEnd = data.endIndex 157 | var matched = true 158 | while compareIndex < haystackEnd && needleIndex < needleEnd { 159 | if self[compareIndex] != data[needleIndex] { 160 | matched = false 161 | break 162 | } 163 | needleIndex = data.index(after: needleIndex) 164 | compareIndex = index(after: compareIndex) 165 | } 166 | if matched { 167 | return haystackIndex..(of data: D, in range: R) -> Range? where R.Bound == Index { 175 | let r = range.relative(to: self) 176 | let rangeCount = distance(from: r.lowerBound, to: r.upperBound) 177 | if rangeCount < data.count { 178 | return nil 179 | } 180 | var haystackIndex = r.upperBound 181 | let haystackStart = index(r.lowerBound, offsetBy: data.count) 182 | while haystackIndex > haystackStart { 183 | var compareIndex = haystackIndex 184 | var needleIndex = data.endIndex 185 | let needleStart = data.startIndex 186 | var matched = true 187 | while compareIndex > haystackStart && needleIndex > needleStart { 188 | if self[compareIndex] != data[needleIndex] { 189 | matched = false 190 | break 191 | } 192 | needleIndex = data.index(before: needleIndex) 193 | compareIndex = index(before: compareIndex) 194 | } 195 | if matched { 196 | return compareIndex..(to ptr: UnsafeMutableBufferPointer, from range: R) where R.Bound == Index { 206 | precondition(ptr.baseAddress != nil) 207 | 208 | let concreteRange = range.relative(to: self) 209 | withUnsafeBytes { fullBuffer in 210 | let adv = distance(from: startIndex, to: concreteRange.lowerBound) 211 | let delta = distance(from: concreteRange.lowerBound, to: concreteRange.upperBound) 212 | memcpy(ptr.baseAddress!, fullBuffer.baseAddress!.advanced(by: adv), delta) 213 | } 214 | } 215 | } 216 | 217 | //===--- MutableDataProtocol Extensions -----------------------------------===// 218 | 219 | extension MutableDataProtocol { 220 | public mutating func resetBytes(in range: R) where R.Bound == Index { 221 | let r = range.relative(to: self) 222 | let count = distance(from: r.lowerBound, to: r.upperBound) 223 | replaceSubrange(r, with: repeatElement(UInt8(0), count: count)) 224 | } 225 | } 226 | 227 | //===--- DataProtocol Conditional Conformances ----------------------------===// 228 | 229 | extension Slice : DataProtocol where Base : DataProtocol { 230 | public typealias Regions = [Base.Regions.Element.SubSequence] 231 | 232 | public var regions: [Base.Regions.Element.SubSequence] { 233 | let sliceLowerBound = startIndex 234 | let sliceUpperBound = endIndex 235 | var regionUpperBound = base.startIndex 236 | 237 | return base.regions.compactMap { (region) -> Base.Regions.Element.SubSequence? in 238 | let regionLowerBound = regionUpperBound 239 | regionUpperBound = base.index(regionUpperBound, offsetBy: region.count) 240 | 241 | /* 242 | [------ Region ------] 243 | [--- Slice ---] => 244 | 245 | OR 246 | 247 | [------ Region ------] 248 | <= [--- Slice ---] 249 | */ 250 | if sliceLowerBound >= regionLowerBound && sliceUpperBound <= regionUpperBound { 251 | let regionRelativeSliceLowerBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceLowerBound)) 252 | let regionRelativeSliceUpperBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceUpperBound)) 253 | return region[regionRelativeSliceLowerBound.. 258 | [------ Slice ------] 259 | 260 | OR 261 | 262 | <= [--- Region ---] 263 | [------ Slice ------] 264 | */ 265 | if regionLowerBound >= sliceLowerBound && regionUpperBound <= sliceUpperBound { 266 | return region[region.startIndex..= regionLowerBound && sliceLowerBound <= regionUpperBound { 274 | let regionRelativeSliceLowerBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceLowerBound)) 275 | return region[regionRelativeSliceLowerBound..= sliceLowerBound && regionLowerBound <= sliceUpperBound { 283 | let regionRelativeSliceUpperBound = region.index(region.startIndex, offsetBy: base.distance(from: regionLowerBound, to: sliceUpperBound)) 284 | return region[region.startIndex.. SwiftFoundation.TimeInterval { 113 | return timeIntervalSinceReferenceDate - date.timeIntervalSinceReferenceDate 114 | } 115 | 116 | /// Return a new `Date` by adding a `TimeInterval` to this `Date`. 117 | /// 118 | /// - parameter timeInterval: The value to add, in seconds. 119 | /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. 120 | public func addingTimeInterval(_ timeInterval: TimeInterval) -> Date { 121 | return self + timeInterval 122 | } 123 | 124 | /// Add a `TimeInterval` to this `Date`. 125 | /// 126 | /// - parameter timeInterval: The value to add, in seconds. 127 | /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. 128 | public mutating func addTimeInterval(_ timeInterval: TimeInterval) { 129 | self += timeInterval 130 | } 131 | } 132 | 133 | // MARK: - CustomStringConvertible 134 | 135 | extension SwiftFoundation.Date: CustomStringConvertible { 136 | 137 | public var description: String { 138 | // TODO: Custom date printing 139 | return timeIntervalSinceReferenceDate.description 140 | } 141 | } 142 | 143 | // MARK: - CustomDebugStringConvertible 144 | 145 | extension SwiftFoundation.Date: CustomDebugStringConvertible { 146 | 147 | public var debugDescription: String { 148 | return description 149 | } 150 | } 151 | 152 | // MARK: - Comparable 153 | 154 | extension SwiftFoundation.Date: Comparable { 155 | 156 | public static func < (lhs: SwiftFoundation.Date, rhs: SwiftFoundation.Date) -> Bool { 157 | return lhs.timeIntervalSinceReferenceDate < rhs.timeIntervalSinceReferenceDate 158 | } 159 | 160 | public static func > (lhs: SwiftFoundation.Date, rhs: SwiftFoundation.Date) -> Bool { 161 | return lhs.timeIntervalSinceReferenceDate > rhs.timeIntervalSinceReferenceDate 162 | } 163 | } 164 | 165 | // MARK: - Operators 166 | 167 | public extension SwiftFoundation.Date { 168 | 169 | /// Returns a `Date` with a specified amount of time added to it. 170 | static func +(lhs: Date, rhs: TimeInterval) -> Date { 171 | return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate + rhs) 172 | } 173 | 174 | /// Returns a `Date` with a specified amount of time subtracted from it. 175 | static func -(lhs: Date, rhs: TimeInterval) -> Date { 176 | return Date(timeIntervalSinceReferenceDate: lhs.timeIntervalSinceReferenceDate - rhs) 177 | } 178 | 179 | /// Add a `TimeInterval` to a `Date`. 180 | /// 181 | /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. 182 | static func +=(lhs: inout Date, rhs: TimeInterval) { 183 | lhs = lhs + rhs 184 | } 185 | 186 | /// Subtract a `TimeInterval` from a `Date`. 187 | /// 188 | /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. 189 | static func -=(lhs: inout Date, rhs: TimeInterval) { 190 | lhs = lhs - rhs 191 | } 192 | 193 | typealias Stride = TimeInterval 194 | 195 | /// Returns the `TimeInterval` between this `Date` and another given date. 196 | /// 197 | /// - returns: The interval between the receiver and the another parameter. If the receiver is earlier than `other`, the return value is negative. 198 | func distance(to other: Date) -> TimeInterval { 199 | return other.timeIntervalSince(self) 200 | } 201 | 202 | /// Creates a new date value by adding a `TimeInterval` to this `Date`. 203 | /// 204 | /// - warning: This only adjusts an absolute value. If you wish to add calendrical concepts like hours, days, months then you must use a `Calendar`. That will take into account complexities like daylight saving time, months with different numbers of days, and more. 205 | func advanced(by n: TimeInterval) -> Date { 206 | return self.addingTimeInterval(n) 207 | } 208 | } 209 | 210 | // MARK: - Codable 211 | 212 | extension Date: Codable { 213 | public init(from decoder: Decoder) throws { 214 | let container = try decoder.singleValueContainer() 215 | let timestamp = try container.decode(Double.self) 216 | self.init(timeIntervalSinceReferenceDate: timestamp) 217 | } 218 | 219 | public func encode(to encoder: Encoder) throws { 220 | var container = encoder.singleValueContainer() 221 | try container.encode(self.timeIntervalSinceReferenceDate) 222 | } 223 | } 224 | 225 | // MARK: - Supporting Types 226 | 227 | /// Time interval difference between two dates, in seconds. 228 | public typealias TimeInterval = Double 229 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/Extensions/Hexadecimal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Hexadecimal.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 3/2/16. 6 | // Copyright © 2016 PureSwift. All rights reserved. 7 | // 8 | 9 | internal extension FixedWidthInteger { 10 | 11 | func toHexadecimal() -> String { 12 | 13 | var string = String(self, radix: 16) 14 | while string.utf8.count < (MemoryLayout.size * 2) { 15 | string = "0" + string 16 | } 17 | return string.uppercased() 18 | } 19 | } 20 | 21 | internal extension UInt8 { 22 | 23 | init?(hexadecimal string: S) where S: StringProtocol { 24 | guard string.count == 2 else { return nil } 25 | self.init(string, radix: 16) 26 | } 27 | } 28 | 29 | internal extension Array where Element == UInt8 { 30 | 31 | init?(hexadecimal string: S) where S: StringProtocol { 32 | guard string.count % 2 == 0 else { return nil } 33 | let byteCount = string.count / 2 34 | self = (0 ..< byteCount).lazy 35 | .map { string[string.index(string.startIndex, offsetBy: $0 * 2) ..< string.index(string.startIndex, offsetBy: ($0 * 2) + 2)] } 36 | .compactMap(UInt8.init) 37 | } 38 | 39 | func toHexadecimal() -> String { 40 | return reduce("") { $0 + $1.toHexadecimal() } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/Extensions/Integer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Integer.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 6/4/20. 6 | // 7 | 8 | internal extension FixedWidthInteger { 9 | 10 | static var random: Self { 11 | return .random(in: .min ... .max) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/POSIXError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // POSIXError.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 7/22/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | #if canImport(Darwin) 10 | import Darwin 11 | #elseif canImport(Glibc) 12 | import Glibc 13 | #endif 14 | 15 | /// Describes an error in the POSIX error domain. 16 | public struct POSIXError: Error { 17 | 18 | public typealias Code = POSIXErrorCode 19 | 20 | public let code: POSIXErrorCode 21 | 22 | public init(code: POSIXErrorCode) { 23 | self.code = code 24 | } 25 | } 26 | 27 | internal extension POSIXError { 28 | 29 | /// Creates error from C ```errno```. 30 | static func fromErrno(file: StaticString = #file, 31 | line: UInt = #line, 32 | function: StaticString = #function) -> POSIXError { 33 | 34 | guard let code = POSIXErrorCode(rawValue: errno) 35 | else { fatalError("Invalid POSIX Error \(errno)", file: file, line: line) } 36 | 37 | return POSIXError(code: code) 38 | } 39 | } 40 | 41 | /// Wasm or another platform that does not provide POSIXErrorCode 42 | #if arch(wasm32) && (!os(Linux) && !canImport(Darwin) && !os(Windows)) 43 | 44 | public struct POSIXErrorCode: RawRepresentable, Equatable, Hashable { 45 | 46 | public let rawValue: Int32 47 | 48 | public init?(rawValue: Int32) { 49 | self.rawValue = rawValue 50 | } 51 | } 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/POSIXTime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // POSIXTime.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 7/19/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | #if canImport(Darwin) 10 | import Darwin.C 11 | #elseif canImport(Glibc) 12 | import Glibc 13 | #endif 14 | 15 | // MARK: - Date 16 | 17 | internal extension Date { 18 | 19 | /// Get seconds since Unix epoch (01/01/1970). 20 | static var timeIntervalSince1970: TimeInterval { 21 | 22 | do { 23 | #if arch(wasm32) 24 | return WebAssembly.timeIntervalSince1970() 25 | #else 26 | if #available(macOS 10.12, iOS 10, tvOS 10.0, *) { 27 | return try SystemClock.realTime.time().timeInterval 28 | } else { 29 | return try timeval.now().timeInterval 30 | } 31 | #endif 32 | } 33 | catch { fatalError("Unable to get current date \(error)") } 34 | } 35 | } 36 | 37 | // MARK: - WASM Fix 38 | 39 | #if arch(wasm32) 40 | public extension Date { 41 | 42 | public enum WebAssembly { 43 | 44 | public static var timeIntervalSince1970: () -> TimeInterval = { return try! timeval.now().timeInterval } // won't work 45 | } 46 | } 47 | #endif 48 | 49 | // MARK: - POSIX Time 50 | 51 | internal extension timeval { 52 | 53 | static func now() throws -> timeval { 54 | 55 | var timeStamp = timeval() 56 | guard gettimeofday(&timeStamp, nil) == 0 57 | else { throw POSIXError.fromErrno() } 58 | return timeStamp 59 | } 60 | 61 | init(timeInterval: Double) { 62 | 63 | let (integerValue, decimalValue) = modf(timeInterval) 64 | let million: Double = 1000000.0 65 | let microseconds = decimalValue * million 66 | self.init(tv_sec: .init(integerValue), tv_usec: .init(microseconds)) 67 | } 68 | 69 | var timeInterval: Double { 70 | 71 | let secondsSince1970 = Double(self.tv_sec) 72 | let million: Double = 1000000.0 73 | let microseconds = Double(self.tv_usec) / million 74 | return secondsSince1970 + microseconds 75 | } 76 | } 77 | 78 | internal extension timespec { 79 | 80 | init(timeInterval: Double) { 81 | 82 | let (integerValue, decimalValue) = modf(timeInterval) 83 | let billion: Double = 1000000000.0 84 | let nanoseconds = decimalValue * billion 85 | self.init(tv_sec: .init(integerValue), tv_nsec: .init(nanoseconds)) 86 | } 87 | 88 | var timeInterval: Double { 89 | 90 | let secondsSince1970 = Double(self.tv_sec) 91 | let billion: Double = 1000000000.0 92 | let nanoseconds = Double(self.tv_nsec) / billion 93 | return secondsSince1970 + nanoseconds 94 | } 95 | } 96 | 97 | internal extension tm { 98 | 99 | init(utcSecondsSince1970 seconds: time_t) { 100 | 101 | var seconds = seconds 102 | 103 | // don't free! 104 | // The return value points to a statically allocated struct which might be overwritten by subsequent calls to any of the date and time functions. 105 | // http://linux.die.net/man/3/gmtime 106 | let timePointer = gmtime(&seconds)! 107 | 108 | self = timePointer.pointee 109 | } 110 | } 111 | 112 | #if arch(wasm32) 113 | internal struct SystemClock: RawRepresentable, Equatable, Hashable { 114 | let rawValue: UInt32 115 | init(rawValue: UInt32) { 116 | self.rawValue = rawValue 117 | } 118 | } 119 | #else 120 | @available(macOS 10.12, iOS 10, tvOS 10.0, *) 121 | internal typealias SystemClock = clockid_t 122 | #endif 123 | 124 | #if !arch(wasm32) 125 | @available(macOS 10.12, iOS 10, tvOS 10.0, *) 126 | internal extension SystemClock { 127 | 128 | 129 | func time() throws -> timespec { 130 | var time = timespec() 131 | guard clock_gettime(self, &time) == 0 132 | else { throw POSIXError.fromErrno() } 133 | return time 134 | } 135 | 136 | #if os(macOS) || os(Linux) // not supported on iOS 137 | func setTime(_ newValue: timespec) throws { 138 | guard withUnsafePointer(to: newValue, { clock_settime(self, $0) == 0 }) 139 | else { throw POSIXError.fromErrno() } 140 | } 141 | #endif 142 | } 143 | 144 | #endif 145 | 146 | #if arch(wasm32) 147 | @_silgen_name("clock_gettime") 148 | internal func wasm_clock_gettime() -> Int32 // crashes with current swift-wasm 149 | 150 | /** 151 | 152 | Return the time value of a clock. Note: This is similar to clock_gettime in POSIX. 153 | 154 | `clock_time_get(id: clockid, precision: timestamp) -> (errno, timestamp)` 155 | 156 | - Parameter id: clockid The clock for which to return the time. 157 | - Parameter precision: timestamp The maximum lag (exclusive) that the returned time value may have, compared to its actual value. 158 | - Returns: error: errno 159 | time: timestamp The time value of the clock. 160 | 161 | https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp 162 | */ 163 | @_silgen_name("clock_time_get") 164 | internal func wasm_clock_time_get(_ id: UInt32, _ precision: UInt64, _ time: inout UInt64) -> Int16 165 | #endif 166 | 167 | @available(macOS 10.12, iOS 10, tvOS 10.0, *) 168 | internal extension SystemClock { 169 | 170 | /** 171 | System-wide clock that measures real (i.e., wall-clock) time. Setting this clock requires appropriate privileges. This clock is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), and by the incremental adjustments performed by adjtime(3) and NTP. 172 | */ 173 | static var realTime: SystemClock { 174 | #if arch(wasm32) 175 | return .init(rawValue: 0) 176 | #else 177 | return CLOCK_REALTIME 178 | #endif 179 | } 180 | 181 | /** 182 | Clock that cannot be set and represents monotonic time since some unspecified starting point. This clock is not affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP. 183 | */ 184 | static var monotonic: SystemClock { 185 | #if arch(wasm32) 186 | return .init(rawValue: 1) 187 | #else 188 | return CLOCK_MONOTONIC 189 | #endif 190 | } 191 | 192 | #if os(Linux) 193 | /// A faster but less precise version of CLOCK_REALTIME. Use when you need very fast, but not fine-grained timestamps. 194 | static var realTimeCourse: SystemClock { // (since Linux 2.6.32; Linux-specific) 195 | return CLOCK_REALTIME_COARSE 196 | } 197 | 198 | /// A faster but less precise version of CLOCK_MONOTONIC. Use when you need very fast, but not fine-grained timestamps. 199 | static var monotonicCourse: SystemClock { // (since Linux 2.6.32; Linux-specific) 200 | return CLOCK_MONOTONIC_COARSE 201 | } 202 | #endif 203 | } 204 | 205 | // MARK: - Cross-Platform Support 206 | 207 | #if canImport(Darwin) 208 | internal typealias POSIXMicroseconds = __darwin_suseconds_t 209 | #else 210 | 211 | #if arch(wasm32) 212 | internal typealias POSIXMicroseconds = Int32 213 | #else 214 | internal typealias POSIXMicroseconds = __suseconds_t 215 | #endif 216 | 217 | internal func modf(value: Double) -> (Double, Double) { 218 | 219 | var integerValue: Double = 0 220 | 221 | let decimalValue = modf(value, &integerValue) 222 | 223 | return (decimalValue, integerValue) 224 | } 225 | 226 | #endif 227 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/Thread.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Thread.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 4/5/16. 6 | // Copyright © 2016 PureSwift. All rights reserved. 7 | // 8 | 9 | #if canImport(Darwin) 10 | import Darwin 11 | #elseif canImport(Glibc) 12 | import Glibc 13 | #endif 14 | 15 | #if !arch(wasm32) 16 | 17 | /// POSIX Thread 18 | public final class Thread { 19 | 20 | // MARK: - Private Properties 21 | 22 | private let internalThread: pthread_t 23 | 24 | // MARK: - Intialization 25 | 26 | public init(block: @escaping () -> ()) throws { 27 | 28 | let holder = Unmanaged.passRetained(Closure(closure: block)) 29 | 30 | let pointer = holder.toOpaque() 31 | 32 | #if canImport(Glibc) 33 | 34 | var internalThread: pthread_t = 0 35 | 36 | guard pthread_create(&internalThread, nil, ThreadPrivateMain, pointer) == 0 37 | else { throw POSIXError.fromErrno() } 38 | 39 | self.internalThread = internalThread 40 | 41 | pthread_detach(internalThread) 42 | 43 | #elseif canImport(Darwin) 44 | 45 | var internalThread: pthread_t? = nil 46 | 47 | guard pthread_create(&internalThread, nil, ThreadPrivateMain, pointer) == 0 48 | else { throw POSIXError.fromErrno() } 49 | 50 | self.internalThread = internalThread! 51 | 52 | pthread_detach(internalThread!) 53 | 54 | #endif 55 | } 56 | 57 | // MARK: - Class Methods 58 | 59 | public static func exit(code: inout Int) { 60 | 61 | pthread_exit(&code) 62 | } 63 | 64 | // MARK: - Methods 65 | 66 | public func join() throws { 67 | 68 | let errorCode = pthread_join(internalThread, nil) 69 | 70 | guard errorCode == 0 71 | else { throw POSIXError(code: POSIXErrorCode(rawValue: errorCode)!) } 72 | } 73 | 74 | public func cancel() throws { 75 | 76 | let errorCode = pthread_cancel(internalThread) 77 | 78 | guard errorCode == 0 79 | else { throw POSIXError(code: POSIXErrorCode(rawValue: errorCode)!) } 80 | } 81 | } 82 | 83 | // MARK: - Private 84 | 85 | #if canImport(Glibc) 86 | 87 | private func ThreadPrivateMain(arg: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? { 88 | 89 | let unmanaged = Unmanaged.fromOpaque(arg!) 90 | 91 | unmanaged.takeUnretainedValue().closure() 92 | 93 | unmanaged.release() 94 | 95 | return nil 96 | } 97 | 98 | #elseif canImport(Darwin) 99 | 100 | private func ThreadPrivateMain(arg: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? { 101 | 102 | let unmanaged = Unmanaged.fromOpaque(arg) 103 | 104 | unmanaged.takeUnretainedValue().closure() 105 | 106 | unmanaged.release() 107 | 108 | return nil 109 | } 110 | 111 | #endif 112 | 113 | fileprivate extension Thread { 114 | 115 | final class Closure { 116 | 117 | let closure: () -> () 118 | 119 | init(closure: @escaping () -> ()) { 120 | 121 | self.closure = closure 122 | } 123 | } 124 | } 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/URL.swift: -------------------------------------------------------------------------------- 1 | // URL.swift 2 | // SwiftFoundation 3 | // 4 | // Created by Alsey Coleman Miller on 6/29/15. 5 | // Copyright © 2015 PureSwift. All rights reserved. 6 | // 7 | 8 | /** 9 | A URL is a type that can potentially contain the location of a resource on a remote server, the path of a local file on disk, or even an arbitrary piece of encoded data. 10 | 11 | You can construct URLs and access their parts. For URLs that represent local files, you can also manipulate properties of those files directly, such as changing the file's last modification date. Finally, you can pass URLs to other APIs to retrieve the contents of those URLs. For example, you can use the URLSession classes to access the contents of remote resources, as described in URL Session Programming Guide. 12 | 13 | URLs are the preferred way to refer to local files. Most objects that read data from or write data to a file have methods that accept a URL instead of a pathname as the file reference. For example, you can get the contents of a local file URL as `String` by calling `func init(contentsOf:encoding) throws`, or as a `Data` by calling `func init(contentsOf:options) throws`. 14 | */ 15 | public struct URL { 16 | 17 | internal let stringValue: String 18 | 19 | /// Initialize with string. 20 | /// 21 | /// Returns `nil` if a `URL` cannot be formed with the string (for example, if the string contains characters that are illegal in a URL, or is an empty string). 22 | public init?(string: String) { 23 | // TODO: Validate URL string 24 | guard string.isEmpty == false 25 | else { return nil } 26 | self.init(string: string) 27 | } 28 | 29 | /// Returns the absolute string for the URL. 30 | public var absoluteString: String { 31 | return stringValue 32 | } 33 | } 34 | 35 | // MARK: - Equatable 36 | 37 | extension URL: Equatable { 38 | 39 | public static func == (lhs: URL, rhs: URL) -> Bool { 40 | return lhs.stringValue == rhs.stringValue 41 | } 42 | } 43 | 44 | // MARK: - Hashable 45 | 46 | extension URL: Hashable { 47 | 48 | public func hash(into hasher: inout Hasher) { 49 | stringValue.hash(into: &hasher) 50 | } 51 | } 52 | 53 | // MARK: - CustomStringConvertible 54 | 55 | extension URL: CustomStringConvertible { 56 | 57 | public var description: String { 58 | return stringValue 59 | } 60 | } 61 | 62 | // MARK: - CustomDebugStringConvertible 63 | 64 | extension URL: CustomDebugStringConvertible { 65 | 66 | public var debugDescription: String { 67 | return description 68 | } 69 | } 70 | 71 | // MARK: - Codable 72 | 73 | extension URL: Codable { 74 | 75 | public init(from decoder: Decoder) throws { 76 | let container = try decoder.singleValueContainer() 77 | let string = try container.decode(String.self) 78 | guard let url = URL(string: string) else { 79 | throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, 80 | debugDescription: "Invalid URL string.")) 81 | } 82 | self = url 83 | } 84 | 85 | public func encode(to encoder: Encoder) throws { 86 | var container = encoder.singleValueContainer() 87 | try container.encode(stringValue) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Sources/SwiftFoundation/UUID.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UUID.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 6/29/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | /// A representation of a universally unique identifier (```UUID```). 10 | public struct UUID { 11 | 12 | // MARK: - Properties 13 | 14 | public let uuid: uuid_t 15 | 16 | // MARK: - Initialization 17 | 18 | /// Create a new UUID with RFC 4122 version 4 random bytes 19 | public init() { 20 | 21 | let uuid = uuid_t(.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random,.random) 22 | self.init(uuid: uuid) 23 | } 24 | 25 | /// Create a UUID from a `uuid_t`. 26 | public init(uuid: uuid_t) { 27 | self.uuid = uuid 28 | } 29 | 30 | // MARK: - UUID String 31 | 32 | /// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F". 33 | /// 34 | /// Returns nil for invalid strings. 35 | public init?(uuidString string: String) { 36 | guard string.count == UUID.stringLength 37 | else { return nil } 38 | let components = string 39 | .split(separator: "-") 40 | guard components[0].count == 8, 41 | components[1].count == 4, 42 | components[2].count == 4, 43 | components[3].count == 4, 44 | components[4].count == 12 45 | else { return nil } 46 | let hexString = components 47 | .reduce("") { $0 + $1 } 48 | assert(hexString.count == UUID.length * 2) 49 | guard let bytes = [UInt8](hexadecimal: hexString) 50 | else { return nil } 51 | assert(bytes.count == UUID.length) 52 | self.init(uuid: uuid_t(bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15])) 53 | } 54 | 55 | /// Returns a string created from the UUID, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" 56 | public var uuidString: String { 57 | return uuid.0.toHexadecimal() 58 | + uuid.1.toHexadecimal() 59 | + uuid.2.toHexadecimal() 60 | + uuid.3.toHexadecimal() 61 | + "-" 62 | + uuid.4.toHexadecimal() 63 | + uuid.5.toHexadecimal() 64 | + "-" 65 | + uuid.6.toHexadecimal() 66 | + uuid.7.toHexadecimal() 67 | + "-" 68 | + uuid.8.toHexadecimal() 69 | + uuid.9.toHexadecimal() 70 | + "-" 71 | + uuid.10.toHexadecimal() 72 | + uuid.11.toHexadecimal() 73 | + uuid.12.toHexadecimal() 74 | + uuid.13.toHexadecimal() 75 | + uuid.14.toHexadecimal() 76 | + uuid.15.toHexadecimal() 77 | } 78 | } 79 | 80 | internal extension SwiftFoundation.UUID { 81 | 82 | static var length: Int { return 16 } 83 | static var stringLength: Int { return 36 } 84 | } 85 | 86 | // MARK: - Equatable 87 | 88 | extension SwiftFoundation.UUID: Equatable { 89 | 90 | public static func == (lhs: SwiftFoundation.UUID, rhs: SwiftFoundation.UUID) -> Bool { 91 | return lhs.uuid.0 == rhs.uuid.0 && 92 | lhs.uuid.1 == rhs.uuid.1 && 93 | lhs.uuid.2 == rhs.uuid.2 && 94 | lhs.uuid.3 == rhs.uuid.3 && 95 | lhs.uuid.4 == rhs.uuid.4 && 96 | lhs.uuid.5 == rhs.uuid.5 && 97 | lhs.uuid.6 == rhs.uuid.6 && 98 | lhs.uuid.7 == rhs.uuid.7 && 99 | lhs.uuid.8 == rhs.uuid.8 && 100 | lhs.uuid.9 == rhs.uuid.9 && 101 | lhs.uuid.10 == rhs.uuid.10 && 102 | lhs.uuid.11 == rhs.uuid.11 && 103 | lhs.uuid.12 == rhs.uuid.12 && 104 | lhs.uuid.13 == rhs.uuid.13 && 105 | lhs.uuid.14 == rhs.uuid.14 && 106 | lhs.uuid.15 == rhs.uuid.15 107 | } 108 | } 109 | 110 | // MARK: - Hashable 111 | 112 | extension UUID: Hashable { 113 | 114 | public func hash(into hasher: inout Hasher) { 115 | withUnsafeBytes(of: uuid) { hasher.combine(bytes: $0) } 116 | } 117 | } 118 | 119 | // MARK: - CustomStringConvertible 120 | 121 | extension UUID: CustomStringConvertible { 122 | 123 | public var description: String { 124 | return uuidString 125 | } 126 | } 127 | 128 | // MARK: - CustomReflectable 129 | 130 | extension UUID: CustomReflectable { 131 | public var customMirror: Mirror { 132 | let c : [(label: String?, value: Any)] = [] 133 | let m = Mirror(self, children:c, displayStyle: .struct) 134 | return m 135 | } 136 | } 137 | 138 | // MARK: - Codable 139 | 140 | extension UUID: Codable { 141 | public init(from decoder: Decoder) throws { 142 | let container = try decoder.singleValueContainer() 143 | let uuidString = try container.decode(String.self) 144 | 145 | guard let uuid = UUID(uuidString: uuidString) else { 146 | throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, 147 | debugDescription: "Attempted to decode UUID from invalid UUID string.")) 148 | } 149 | 150 | self = uuid 151 | } 152 | 153 | public func encode(to encoder: Encoder) throws { 154 | var container = encoder.singleValueContainer() 155 | try container.encode(self.uuidString) 156 | } 157 | } 158 | 159 | // MARK: - Supporting Types 160 | 161 | public typealias uuid_t = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) 162 | public typealias uuid_string_t = (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8) 163 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/SwiftFoundationTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/SwiftFoundation_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "SwiftFoundation::SwiftFoundationPackageTests::ProductTarget" /* SwiftFoundationPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_50 /* Build configuration list for PBXAggregateTarget "SwiftFoundationPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_53 /* PBXTargetDependency */, 17 | ); 18 | name = SwiftFoundationPackageTests; 19 | productName = SwiftFoundationPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 6ED6D1BA24898E1D00504EC9 /* Hexadecimal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ED6D1B924898E1D00504EC9 /* Hexadecimal.swift */; }; 25 | 6ED6D1BC24898F1000504EC9 /* Integer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6ED6D1BB24898F1000504EC9 /* Integer.swift */; }; 26 | OBJ_33 /* Base64.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* Base64.swift */; }; 27 | OBJ_34 /* ComparisonResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* ComparisonResult.swift */; }; 28 | OBJ_35 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* Data.swift */; }; 29 | OBJ_36 /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* Date.swift */; }; 30 | OBJ_37 /* POSIXError.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* POSIXError.swift */; }; 31 | OBJ_38 /* POSIXTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* POSIXTime.swift */; }; 32 | OBJ_39 /* Thread.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* Thread.swift */; }; 33 | OBJ_40 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* URL.swift */; }; 34 | OBJ_41 /* UUID.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_17 /* UUID.swift */; }; 35 | OBJ_48 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 36 | OBJ_59 /* DataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_20 /* DataTests.swift */; }; 37 | OBJ_60 /* POSIXTimeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_21 /* POSIXTimeTests.swift */; }; 38 | OBJ_61 /* UUIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_22 /* UUIDTests.swift */; }; 39 | OBJ_63 /* SwiftFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "SwiftFoundation::SwiftFoundation::Product" /* SwiftFoundation.framework */; }; 40 | /* End PBXBuildFile section */ 41 | 42 | /* Begin PBXContainerItemProxy section */ 43 | 6ED6D1B4248988EE00504EC9 /* PBXContainerItemProxy */ = { 44 | isa = PBXContainerItemProxy; 45 | containerPortal = OBJ_1 /* Project object */; 46 | proxyType = 1; 47 | remoteGlobalIDString = "SwiftFoundation::SwiftFoundation"; 48 | remoteInfo = SwiftFoundation; 49 | }; 50 | 6ED6D1B5248988F000504EC9 /* PBXContainerItemProxy */ = { 51 | isa = PBXContainerItemProxy; 52 | containerPortal = OBJ_1 /* Project object */; 53 | proxyType = 1; 54 | remoteGlobalIDString = "SwiftFoundation::SwiftFoundationTests"; 55 | remoteInfo = SwiftFoundationTests; 56 | }; 57 | /* End PBXContainerItemProxy section */ 58 | 59 | /* Begin PBXFileReference section */ 60 | 6ED6D1B924898E1D00504EC9 /* Hexadecimal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hexadecimal.swift; sourceTree = ""; }; 61 | 6ED6D1BB24898F1000504EC9 /* Integer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Integer.swift; sourceTree = ""; }; 62 | OBJ_10 /* ComparisonResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparisonResult.swift; sourceTree = ""; }; 63 | OBJ_11 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; 64 | OBJ_12 /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = ""; }; 65 | OBJ_13 /* POSIXError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSIXError.swift; sourceTree = ""; }; 66 | OBJ_14 /* POSIXTime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSIXTime.swift; sourceTree = ""; }; 67 | OBJ_15 /* Thread.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thread.swift; sourceTree = ""; }; 68 | OBJ_16 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = ""; }; 69 | OBJ_17 /* UUID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUID.swift; sourceTree = ""; }; 70 | OBJ_20 /* DataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTests.swift; sourceTree = ""; }; 71 | OBJ_21 /* POSIXTimeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSIXTimeTests.swift; sourceTree = ""; }; 72 | OBJ_22 /* UUIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UUIDTests.swift; sourceTree = ""; }; 73 | OBJ_26 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; 74 | OBJ_27 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 75 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 76 | OBJ_9 /* Base64.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Base64.swift; sourceTree = ""; }; 77 | "SwiftFoundation::SwiftFoundation::Product" /* SwiftFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = SwiftFoundation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | "SwiftFoundation::SwiftFoundationTests::Product" /* SwiftFoundationTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = SwiftFoundationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 79 | /* End PBXFileReference section */ 80 | 81 | /* Begin PBXFrameworksBuildPhase section */ 82 | OBJ_42 /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 0; 85 | files = ( 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | OBJ_62 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 0; 92 | files = ( 93 | OBJ_63 /* SwiftFoundation.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | /* End PBXFrameworksBuildPhase section */ 98 | 99 | /* Begin PBXGroup section */ 100 | 6ED6D1B8248989EB00504EC9 /* Extensions */ = { 101 | isa = PBXGroup; 102 | children = ( 103 | 6ED6D1B924898E1D00504EC9 /* Hexadecimal.swift */, 104 | 6ED6D1BB24898F1000504EC9 /* Integer.swift */, 105 | ); 106 | path = Extensions; 107 | sourceTree = ""; 108 | }; 109 | OBJ_18 /* Tests */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | OBJ_19 /* SwiftFoundationTests */, 113 | ); 114 | name = Tests; 115 | sourceTree = SOURCE_ROOT; 116 | }; 117 | OBJ_19 /* SwiftFoundationTests */ = { 118 | isa = PBXGroup; 119 | children = ( 120 | OBJ_20 /* DataTests.swift */, 121 | OBJ_21 /* POSIXTimeTests.swift */, 122 | OBJ_22 /* UUIDTests.swift */, 123 | ); 124 | name = SwiftFoundationTests; 125 | path = Tests/SwiftFoundationTests; 126 | sourceTree = SOURCE_ROOT; 127 | }; 128 | OBJ_23 /* Products */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | "SwiftFoundation::SwiftFoundationTests::Product" /* SwiftFoundationTests.xctest */, 132 | "SwiftFoundation::SwiftFoundation::Product" /* SwiftFoundation.framework */, 133 | ); 134 | name = Products; 135 | sourceTree = BUILT_PRODUCTS_DIR; 136 | }; 137 | OBJ_5 /* */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | OBJ_6 /* Package.swift */, 141 | OBJ_7 /* Sources */, 142 | OBJ_18 /* Tests */, 143 | OBJ_23 /* Products */, 144 | OBJ_26 /* LICENSE */, 145 | OBJ_27 /* README.md */, 146 | ); 147 | name = ""; 148 | sourceTree = ""; 149 | }; 150 | OBJ_7 /* Sources */ = { 151 | isa = PBXGroup; 152 | children = ( 153 | OBJ_8 /* SwiftFoundation */, 154 | ); 155 | name = Sources; 156 | sourceTree = SOURCE_ROOT; 157 | }; 158 | OBJ_8 /* SwiftFoundation */ = { 159 | isa = PBXGroup; 160 | children = ( 161 | 6ED6D1B8248989EB00504EC9 /* Extensions */, 162 | OBJ_9 /* Base64.swift */, 163 | OBJ_10 /* ComparisonResult.swift */, 164 | OBJ_11 /* Data.swift */, 165 | OBJ_12 /* Date.swift */, 166 | OBJ_13 /* POSIXError.swift */, 167 | OBJ_14 /* POSIXTime.swift */, 168 | OBJ_15 /* Thread.swift */, 169 | OBJ_16 /* URL.swift */, 170 | OBJ_17 /* UUID.swift */, 171 | ); 172 | name = SwiftFoundation; 173 | path = Sources/SwiftFoundation; 174 | sourceTree = SOURCE_ROOT; 175 | }; 176 | /* End PBXGroup section */ 177 | 178 | /* Begin PBXNativeTarget section */ 179 | "SwiftFoundation::SwiftFoundation" /* SwiftFoundation */ = { 180 | isa = PBXNativeTarget; 181 | buildConfigurationList = OBJ_29 /* Build configuration list for PBXNativeTarget "SwiftFoundation" */; 182 | buildPhases = ( 183 | OBJ_32 /* Sources */, 184 | OBJ_42 /* Frameworks */, 185 | ); 186 | buildRules = ( 187 | ); 188 | dependencies = ( 189 | ); 190 | name = SwiftFoundation; 191 | productName = SwiftFoundation; 192 | productReference = "SwiftFoundation::SwiftFoundation::Product" /* SwiftFoundation.framework */; 193 | productType = "com.apple.product-type.framework"; 194 | }; 195 | "SwiftFoundation::SwiftFoundationTests" /* SwiftFoundationTests */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = OBJ_55 /* Build configuration list for PBXNativeTarget "SwiftFoundationTests" */; 198 | buildPhases = ( 199 | OBJ_58 /* Sources */, 200 | OBJ_62 /* Frameworks */, 201 | ); 202 | buildRules = ( 203 | ); 204 | dependencies = ( 205 | OBJ_64 /* PBXTargetDependency */, 206 | ); 207 | name = SwiftFoundationTests; 208 | productName = SwiftFoundationTests; 209 | productReference = "SwiftFoundation::SwiftFoundationTests::Product" /* SwiftFoundationTests.xctest */; 210 | productType = "com.apple.product-type.bundle.unit-test"; 211 | }; 212 | "SwiftFoundation::SwiftPMPackageDescription" /* SwiftFoundationPackageDescription */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = OBJ_44 /* Build configuration list for PBXNativeTarget "SwiftFoundationPackageDescription" */; 215 | buildPhases = ( 216 | OBJ_47 /* Sources */, 217 | ); 218 | buildRules = ( 219 | ); 220 | dependencies = ( 221 | ); 222 | name = SwiftFoundationPackageDescription; 223 | productName = SwiftFoundationPackageDescription; 224 | productType = "com.apple.product-type.framework"; 225 | }; 226 | /* End PBXNativeTarget section */ 227 | 228 | /* Begin PBXProject section */ 229 | OBJ_1 /* Project object */ = { 230 | isa = PBXProject; 231 | attributes = { 232 | LastSwiftMigration = 9999; 233 | LastUpgradeCheck = 9999; 234 | }; 235 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "SwiftFoundation" */; 236 | compatibilityVersion = "Xcode 3.2"; 237 | developmentRegion = en; 238 | hasScannedForEncodings = 0; 239 | knownRegions = ( 240 | en, 241 | ); 242 | mainGroup = OBJ_5 /* */; 243 | productRefGroup = OBJ_23 /* Products */; 244 | projectDirPath = ""; 245 | projectRoot = ""; 246 | targets = ( 247 | "SwiftFoundation::SwiftFoundation" /* SwiftFoundation */, 248 | "SwiftFoundation::SwiftPMPackageDescription" /* SwiftFoundationPackageDescription */, 249 | "SwiftFoundation::SwiftFoundationPackageTests::ProductTarget" /* SwiftFoundationPackageTests */, 250 | "SwiftFoundation::SwiftFoundationTests" /* SwiftFoundationTests */, 251 | ); 252 | }; 253 | /* End PBXProject section */ 254 | 255 | /* Begin PBXSourcesBuildPhase section */ 256 | OBJ_32 /* Sources */ = { 257 | isa = PBXSourcesBuildPhase; 258 | buildActionMask = 0; 259 | files = ( 260 | OBJ_33 /* Base64.swift in Sources */, 261 | OBJ_34 /* ComparisonResult.swift in Sources */, 262 | OBJ_35 /* Data.swift in Sources */, 263 | 6ED6D1BA24898E1D00504EC9 /* Hexadecimal.swift in Sources */, 264 | OBJ_36 /* Date.swift in Sources */, 265 | OBJ_37 /* POSIXError.swift in Sources */, 266 | OBJ_38 /* POSIXTime.swift in Sources */, 267 | OBJ_39 /* Thread.swift in Sources */, 268 | OBJ_40 /* URL.swift in Sources */, 269 | 6ED6D1BC24898F1000504EC9 /* Integer.swift in Sources */, 270 | OBJ_41 /* UUID.swift in Sources */, 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | OBJ_47 /* Sources */ = { 275 | isa = PBXSourcesBuildPhase; 276 | buildActionMask = 0; 277 | files = ( 278 | OBJ_48 /* Package.swift in Sources */, 279 | ); 280 | runOnlyForDeploymentPostprocessing = 0; 281 | }; 282 | OBJ_58 /* Sources */ = { 283 | isa = PBXSourcesBuildPhase; 284 | buildActionMask = 0; 285 | files = ( 286 | OBJ_59 /* DataTests.swift in Sources */, 287 | OBJ_60 /* POSIXTimeTests.swift in Sources */, 288 | OBJ_61 /* UUIDTests.swift in Sources */, 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | }; 292 | /* End PBXSourcesBuildPhase section */ 293 | 294 | /* Begin PBXTargetDependency section */ 295 | OBJ_53 /* PBXTargetDependency */ = { 296 | isa = PBXTargetDependency; 297 | target = "SwiftFoundation::SwiftFoundationTests" /* SwiftFoundationTests */; 298 | targetProxy = 6ED6D1B5248988F000504EC9 /* PBXContainerItemProxy */; 299 | }; 300 | OBJ_64 /* PBXTargetDependency */ = { 301 | isa = PBXTargetDependency; 302 | target = "SwiftFoundation::SwiftFoundation" /* SwiftFoundation */; 303 | targetProxy = 6ED6D1B4248988EE00504EC9 /* PBXContainerItemProxy */; 304 | }; 305 | /* End PBXTargetDependency section */ 306 | 307 | /* Begin XCBuildConfiguration section */ 308 | OBJ_3 /* Debug */ = { 309 | isa = XCBuildConfiguration; 310 | buildSettings = { 311 | CLANG_ENABLE_OBJC_ARC = YES; 312 | COMBINE_HIDPI_IMAGES = YES; 313 | COPY_PHASE_STRIP = NO; 314 | DEBUG_INFORMATION_FORMAT = dwarf; 315 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 316 | ENABLE_NS_ASSERTIONS = YES; 317 | GCC_OPTIMIZATION_LEVEL = 0; 318 | GCC_PREPROCESSOR_DEFINITIONS = ( 319 | "$(inherited)", 320 | "SWIFT_PACKAGE=1", 321 | "DEBUG=1", 322 | ); 323 | MACOSX_DEPLOYMENT_TARGET = 10.10; 324 | ONLY_ACTIVE_ARCH = YES; 325 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 326 | PRODUCT_NAME = "$(TARGET_NAME)"; 327 | SDKROOT = macosx; 328 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 329 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG"; 330 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 331 | USE_HEADERMAP = NO; 332 | }; 333 | name = Debug; 334 | }; 335 | OBJ_30 /* Debug */ = { 336 | isa = XCBuildConfiguration; 337 | buildSettings = { 338 | ENABLE_TESTABILITY = YES; 339 | FRAMEWORK_SEARCH_PATHS = ( 340 | "$(inherited)", 341 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 342 | ); 343 | HEADER_SEARCH_PATHS = "$(inherited)"; 344 | INFOPLIST_FILE = SwiftFoundation.xcodeproj/SwiftFoundation_Info.plist; 345 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 346 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 347 | MACOSX_DEPLOYMENT_TARGET = 10.10; 348 | OTHER_CFLAGS = "$(inherited)"; 349 | OTHER_LDFLAGS = "$(inherited)"; 350 | OTHER_SWIFT_FLAGS = "$(inherited)"; 351 | PRODUCT_BUNDLE_IDENTIFIER = SwiftFoundation; 352 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 353 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 354 | SKIP_INSTALL = YES; 355 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 356 | SWIFT_VERSION = 5.0; 357 | TARGET_NAME = SwiftFoundation; 358 | TVOS_DEPLOYMENT_TARGET = 9.0; 359 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 360 | }; 361 | name = Debug; 362 | }; 363 | OBJ_31 /* Release */ = { 364 | isa = XCBuildConfiguration; 365 | buildSettings = { 366 | ENABLE_TESTABILITY = YES; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 370 | ); 371 | HEADER_SEARCH_PATHS = "$(inherited)"; 372 | INFOPLIST_FILE = SwiftFoundation.xcodeproj/SwiftFoundation_Info.plist; 373 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 375 | MACOSX_DEPLOYMENT_TARGET = 10.10; 376 | OTHER_CFLAGS = "$(inherited)"; 377 | OTHER_LDFLAGS = "$(inherited)"; 378 | OTHER_SWIFT_FLAGS = "$(inherited)"; 379 | PRODUCT_BUNDLE_IDENTIFIER = SwiftFoundation; 380 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 381 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 382 | SKIP_INSTALL = YES; 383 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 384 | SWIFT_VERSION = 5.0; 385 | TARGET_NAME = SwiftFoundation; 386 | TVOS_DEPLOYMENT_TARGET = 9.0; 387 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 388 | }; 389 | name = Release; 390 | }; 391 | OBJ_4 /* Release */ = { 392 | isa = XCBuildConfiguration; 393 | buildSettings = { 394 | CLANG_ENABLE_OBJC_ARC = YES; 395 | COMBINE_HIDPI_IMAGES = YES; 396 | COPY_PHASE_STRIP = YES; 397 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 398 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 399 | GCC_OPTIMIZATION_LEVEL = s; 400 | GCC_PREPROCESSOR_DEFINITIONS = ( 401 | "$(inherited)", 402 | "SWIFT_PACKAGE=1", 403 | ); 404 | MACOSX_DEPLOYMENT_TARGET = 10.10; 405 | OTHER_SWIFT_FLAGS = "$(inherited) -DXcode"; 406 | PRODUCT_NAME = "$(TARGET_NAME)"; 407 | SDKROOT = macosx; 408 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 409 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE"; 410 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 411 | USE_HEADERMAP = NO; 412 | }; 413 | name = Release; 414 | }; 415 | OBJ_45 /* Debug */ = { 416 | isa = XCBuildConfiguration; 417 | buildSettings = { 418 | LD = /usr/bin/true; 419 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.1.0"; 420 | SWIFT_VERSION = 5.0; 421 | }; 422 | name = Debug; 423 | }; 424 | OBJ_46 /* Release */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | LD = /usr/bin/true; 428 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -package-description-version 5.1.0"; 429 | SWIFT_VERSION = 5.0; 430 | }; 431 | name = Release; 432 | }; 433 | OBJ_51 /* Debug */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | }; 437 | name = Debug; 438 | }; 439 | OBJ_52 /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | }; 443 | name = Release; 444 | }; 445 | OBJ_56 /* Debug */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | CLANG_ENABLE_MODULES = YES; 449 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 450 | FRAMEWORK_SEARCH_PATHS = ( 451 | "$(inherited)", 452 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 453 | ); 454 | HEADER_SEARCH_PATHS = "$(inherited)"; 455 | INFOPLIST_FILE = SwiftFoundation.xcodeproj/SwiftFoundationTests_Info.plist; 456 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 457 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 458 | MACOSX_DEPLOYMENT_TARGET = 10.10; 459 | OTHER_CFLAGS = "$(inherited)"; 460 | OTHER_LDFLAGS = "$(inherited)"; 461 | OTHER_SWIFT_FLAGS = "$(inherited)"; 462 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 463 | SWIFT_VERSION = 5.0; 464 | TARGET_NAME = SwiftFoundationTests; 465 | TVOS_DEPLOYMENT_TARGET = 9.0; 466 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 467 | }; 468 | name = Debug; 469 | }; 470 | OBJ_57 /* Release */ = { 471 | isa = XCBuildConfiguration; 472 | buildSettings = { 473 | CLANG_ENABLE_MODULES = YES; 474 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 475 | FRAMEWORK_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 478 | ); 479 | HEADER_SEARCH_PATHS = "$(inherited)"; 480 | INFOPLIST_FILE = SwiftFoundation.xcodeproj/SwiftFoundationTests_Info.plist; 481 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 482 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks"; 483 | MACOSX_DEPLOYMENT_TARGET = 10.10; 484 | OTHER_CFLAGS = "$(inherited)"; 485 | OTHER_LDFLAGS = "$(inherited)"; 486 | OTHER_SWIFT_FLAGS = "$(inherited)"; 487 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; 488 | SWIFT_VERSION = 5.0; 489 | TARGET_NAME = SwiftFoundationTests; 490 | TVOS_DEPLOYMENT_TARGET = 9.0; 491 | WATCHOS_DEPLOYMENT_TARGET = 2.0; 492 | }; 493 | name = Release; 494 | }; 495 | /* End XCBuildConfiguration section */ 496 | 497 | /* Begin XCConfigurationList section */ 498 | OBJ_2 /* Build configuration list for PBXProject "SwiftFoundation" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | OBJ_3 /* Debug */, 502 | OBJ_4 /* Release */, 503 | ); 504 | defaultConfigurationIsVisible = 0; 505 | defaultConfigurationName = Release; 506 | }; 507 | OBJ_29 /* Build configuration list for PBXNativeTarget "SwiftFoundation" */ = { 508 | isa = XCConfigurationList; 509 | buildConfigurations = ( 510 | OBJ_30 /* Debug */, 511 | OBJ_31 /* Release */, 512 | ); 513 | defaultConfigurationIsVisible = 0; 514 | defaultConfigurationName = Release; 515 | }; 516 | OBJ_44 /* Build configuration list for PBXNativeTarget "SwiftFoundationPackageDescription" */ = { 517 | isa = XCConfigurationList; 518 | buildConfigurations = ( 519 | OBJ_45 /* Debug */, 520 | OBJ_46 /* Release */, 521 | ); 522 | defaultConfigurationIsVisible = 0; 523 | defaultConfigurationName = Release; 524 | }; 525 | OBJ_50 /* Build configuration list for PBXAggregateTarget "SwiftFoundationPackageTests" */ = { 526 | isa = XCConfigurationList; 527 | buildConfigurations = ( 528 | OBJ_51 /* Debug */, 529 | OBJ_52 /* Release */, 530 | ); 531 | defaultConfigurationIsVisible = 0; 532 | defaultConfigurationName = Release; 533 | }; 534 | OBJ_55 /* Build configuration list for PBXNativeTarget "SwiftFoundationTests" */ = { 535 | isa = XCConfigurationList; 536 | buildConfigurations = ( 537 | OBJ_56 /* Debug */, 538 | OBJ_57 /* Release */, 539 | ); 540 | defaultConfigurationIsVisible = 0; 541 | defaultConfigurationName = Release; 542 | }; 543 | /* End XCConfigurationList section */ 544 | }; 545 | rootObject = OBJ_1 /* Project object */; 546 | } 547 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/project.xcworkspace/xcuserdata/coleman.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PureSwift/SwiftFoundation/71f697068674cf97b59a850f7aae9f016705c66d/SwiftFoundation.xcodeproj/project.xcworkspace/xcuserdata/coleman.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SwiftFoundation.xcodeproj/xcshareddata/xcschemes/SwiftFoundation-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 54 | 60 | 61 | 63 | 64 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 12/16/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import SwiftFoundationTests 11 | 12 | XCTMain([ 13 | testCase(UUIDTests.allTests), 14 | testCase(DataTests.allTests) 15 | ]) 16 | 17 | -------------------------------------------------------------------------------- /Tests/SwiftFoundationTests/DataTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataTests.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 1/10/16. 6 | // Copyright © 2016 PureSwift. All rights reserved. 7 | // 8 | 9 | #if os(Linux) 10 | import Glibc 11 | #endif 12 | 13 | import XCTest 14 | @testable import SwiftFoundation 15 | 16 | final class DataTests: XCTestCase { 17 | 18 | static let allTests = [ 19 | ("testFromBytePointer", testFromBytePointer) 20 | ] 21 | 22 | func testFromBytePointer() { 23 | /* 24 | let string = "TestData" 25 | 26 | let testData = Data(string.utf8) 27 | 28 | XCTAssert(testData.isEmpty == false, "Could not create test data") 29 | 30 | let dataPointer = UnsafeMutablePointer.allocate(capacity: testData.count) 31 | 32 | defer { dataPointer.deallocate() } 33 | 34 | memcpy(dataPointer, testData.bytes, testData.count) 35 | 36 | let data = Data(bytes: dataPointer, count: testData.count) 37 | 38 | XCTAssert(data == testData, "\(data) == \(testData)") 39 | */ 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Tests/SwiftFoundationTests/POSIXTimeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // POSIXTimeTests.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 7/22/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 10 | import Darwin.C 11 | #elseif os(Linux) 12 | import Glibc 13 | #endif 14 | 15 | import XCTest 16 | @testable import SwiftFoundation 17 | 18 | final class POSIXTimeTests: XCTestCase { 19 | 20 | static let allTests: [(String, (POSIXTimeTests) -> () throws -> Void)] = 21 | [("testGetTimeOfDay", testGetTimeOfDay), 22 | ("testTimeVal", testTimeVal), 23 | ("testTimeSpec", testTimeSpec)] 24 | 25 | func testGetTimeOfDay() { 26 | 27 | var time = timeval() 28 | 29 | do { time = try timeval.timeOfDay() } 30 | 31 | catch { 32 | 33 | XCTFail("Error getting time: \(error)") 34 | } 35 | 36 | print("Current time: \(time)") 37 | } 38 | 39 | func testTimeVal() { 40 | 41 | let date = SwiftFoundation.Date() 42 | 43 | let time = timeval(timeInterval: date.timeIntervalSince1970) 44 | 45 | XCTAssert(Int(time.timeInterval) == Int(date.timeIntervalSince1970), "TimeVal derived interval: \(time.timeInterval) must equal Date's timeIntervalSince1970 \(date.timeIntervalSince1970)") 46 | } 47 | 48 | func testTimeSpec() { 49 | 50 | let date = SwiftFoundation.Date() 51 | 52 | let time = timespec(timeInterval: date.timeIntervalSince1970) 53 | 54 | #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) 55 | 56 | XCTAssert(time.timeInterval == date.timeIntervalSince1970, "timespec: \(time.timeInterval) == Date: \(date)") 57 | 58 | #elseif os(Linux) 59 | 60 | // do nothing for now 61 | 62 | #endif 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Tests/SwiftFoundationTests/UUIDTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UUIDTests.swift 3 | // SwiftFoundation 4 | // 5 | // Created by Alsey Coleman Miller on 7/2/15. 6 | // Copyright © 2015 PureSwift. All rights reserved. 7 | // 8 | 9 | #if canImport(Foundation) 10 | import Foundation 11 | #endif 12 | import XCTest 13 | @testable import SwiftFoundation 14 | 15 | final class UUIDTests: XCTestCase { 16 | 17 | static let allTests = [ 18 | ("testEquality", testEquality), 19 | ("testInvalid", testInvalid), 20 | ("testString", testString), 21 | ("testDescription", testDescription), 22 | ("testHashable", testHashable), 23 | ("testCodable", testCodable), 24 | ("testCreateRandomUUID", testCreateRandomUUID), 25 | ("testUUIDString", testUUIDString), 26 | ("testCreateFromString", testCreateFromString), 27 | ("testBytes", testBytes) 28 | ] 29 | 30 | typealias UUID = SwiftFoundation.UUID 31 | 32 | func testEquality() { 33 | let uuidA = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F") 34 | let uuidB = UUID(uuidString: "e621e1f8-c36c-495a-93fc-0c247a3e6e5f") 35 | let uuidC = UUID(uuid: (0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f)) 36 | let uuidD = UUID() 37 | 38 | XCTAssertEqual(uuidA, uuidB, "String case must not matter.") 39 | XCTAssertEqual(uuidA, uuidC, "A UUID initialized with a string must be equal to the same UUID initialized with its UnsafePointer equivalent representation.") 40 | XCTAssertNotEqual(uuidC, uuidD, "Two different UUIDs must not be equal.") 41 | } 42 | 43 | func testInvalid() { 44 | let uuid = UUID(uuidString: "Invalid UUID") 45 | XCTAssertNil(uuid, "The convenience initializer `init?(uuidString string:)` must return nil for an invalid UUID string.") 46 | } 47 | 48 | func testString() { 49 | let uuid = UUID(uuid: (0xe6,0x21,0xe1,0xf8,0xc3,0x6c,0x49,0x5a,0x93,0xfc,0x0c,0x24,0x7a,0x3e,0x6e,0x5f)) 50 | XCTAssertEqual(uuid.uuidString, "E621E1F8-C36C-495A-93FC-0C247A3E6E5F", "The uuidString representation must be uppercase.") 51 | } 52 | 53 | func testDescription() { 54 | let uuid = UUID() 55 | XCTAssertEqual(uuid.description, uuid.uuidString, "The description must be the same as the uuidString.") 56 | } 57 | 58 | func testHashable() { 59 | let uuid = UUID() 60 | XCTAssertEqual(uuid.hashValue, uuid.hashValue) 61 | XCTAssertNotEqual(uuid.hashValue, 0) 62 | XCTAssertNotEqual(uuid.hashValue, UUID().hashValue) 63 | } 64 | 65 | #if canImport(Foundation) 66 | 67 | /// Needs JSONEncoder / JSONDecoder 68 | func testCodable() { 69 | 70 | let uuid = UUID(uuidString: "5BFEB194-68C4-48E8-8F43-3C586364CB6F")! 71 | let value = ["uuid": uuid] 72 | let data = Foundation.Data(#"{"uuid":"5BFEB194-68C4-48E8-8F43-3C586364CB6F"}"#.utf8) 73 | let encoder = JSONEncoder() 74 | XCTAssertEqual(try encoder.encode(value), data) 75 | let decoder = JSONDecoder() 76 | XCTAssertEqual(try decoder.decode(type(of: value), from: data), value) 77 | } 78 | 79 | #endif 80 | 81 | func testCreateRandomUUID() { 82 | 83 | // try to create without crashing 84 | let uuid = UUID() 85 | print(uuid) 86 | } 87 | 88 | func testUUIDString() { 89 | 90 | let stringValue = "5BFEB194-68C4-48E8-8F43-3C586364CB6F" 91 | 92 | guard let uuid = UUID(uuidString: stringValue) 93 | else { XCTFail("Could not create UUID from " + stringValue); return } 94 | 95 | XCTAssert(uuid.uuidString == stringValue, "UUID is \(uuid), should be \(stringValue)") 96 | } 97 | 98 | func testCreateFromString() { 99 | 100 | let stringValue = "5BFEB194-68C4-48E8-8F43-3C586364CB6F" 101 | XCTAssert((UUID(uuidString: stringValue) != nil), "Could not create UUID with string \"\(stringValue)\"") 102 | XCTAssert((UUID(uuidString: "BadInput") == nil), "UUID should not be created") 103 | } 104 | 105 | func testBytes() { 106 | 107 | let bytes: uuid_t = (91, 254, 177, 148, 104, 196, 72, 232, 143, 67, 60, 88, 99, 100, 203, 111) 108 | 109 | let stringValue = "5BFEB194-68C4-48E8-8F43-3C586364CB6F" 110 | 111 | XCTAssertEqual(UUID(uuidString: stringValue), UUID(uuid: bytes), "Unexpected UUID data") 112 | } 113 | } 114 | --------------------------------------------------------------------------------