Skip to content

Commit ea3de62

Browse files
committed
First commit
0 parents  commit ea3de62

26 files changed

+811
-0
lines changed

.gitignore

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
.swiftpm
3+
/.build
4+
/Packages
5+
/*.xcodeproj
6+
xcuserdata/

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2020 Nicolas Bachschmidt
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.

Package.resolved

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"object": {
3+
"pins": [
4+
{
5+
"package": "swift-crypto",
6+
"repositoryURL": "https://github.com/apple/swift-crypto.git",
7+
"state": {
8+
"branch": null,
9+
"revision": "afe4c9b5f65b612a2bd4b1cb5ac70eb5841f278e",
10+
"version": "1.1.0"
11+
}
12+
}
13+
]
14+
},
15+
"version": 1
16+
}

Package.swift

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version:5.1
2+
import PackageDescription
3+
4+
let package = Package(
5+
name: "UUIDKit",
6+
platforms: [
7+
.macOS(.v10_15),
8+
.iOS(.v13),
9+
.watchOS(.v6),
10+
.tvOS(.v13),
11+
],
12+
products: [
13+
.library(name: "UUIDKit", targets: ["UUIDKit"]),
14+
],
15+
dependencies: [
16+
.package(url: "https://github.com/apple/swift-crypto.git", from: "1.0.0"),
17+
],
18+
targets: [
19+
.target(name: "CUUIDKit"),
20+
.target(name: "UUIDKit", dependencies: [
21+
.target(name: "CUUIDKit"),
22+
.product(name: "Crypto", package: "swift-crypto"),
23+
]),
24+
.testTarget(name: "UUIDKitTests", dependencies: [
25+
.target(name: "UUIDKit"),
26+
]),
27+
]
28+
)

README.md

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# UUIDKit
2+
3+
[![MIT License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
4+
[![Swift 5.1](https://img.shields.io/badge/swift-5.1-brightgreen.svg)](https://swift.org)
5+
6+
UUIDKit is a Swift library for generating and working with Universally Unique Identifiers (UUIDs) as specified in [RFC 4122](https://tools.ietf.org/html/rfc4122.html).
7+
8+
It extends the existing Foundation's `UUID` type.
9+
10+
## Generating UUIDs
11+
12+
The default initializer for `UUID` returns a random (version 4) UUID. This library adds methods for creating version 1, version 3 and version 5 UUIDs.
13+
14+
### UUID.v1
15+
16+
This method returns a time-based (version 1) UUID.
17+
18+
```swift
19+
let uuidv1 = UUID.v1()
20+
```
21+
22+
Note that this method may compromise privacy as it returns a UUID containing the Ethernet address of the user's computer.
23+
24+
### UUID.v4
25+
26+
This method returns a random (version 4) UUID. You may pass a custom random number generator.
27+
28+
```swift
29+
var generator = SystemRandomNumberGenerator()
30+
let uuidv4 = UUID.v4(using: generator)
31+
```
32+
33+
### UUID.v3 and UUID.v5
34+
35+
These methods return name-based UUIDs using either MD5 (version 3) or SHA-1 (version 5) hashing.
36+
37+
```swift
38+
let uuidv3 = UUID.v3(name: "thats.an.example", namespace: .dns)
39+
let uuidv5 = UUID.v5(name: "http://example.com/index.html", namespace: .url)
40+
```
41+
42+
Name-based UUIDs are built by combining a unique name with a namespace identifier. UUIDKit contains a list of pre-defined namespaces (`.dns`, `.url`, `.oid` and `.x500`); but any UUID may be used as a namespace.
43+
44+
```swift
45+
let customNamespace = UUID.Namespace(UUID(uuidString: "34cd6bf4-3f41-4717-95ea-131762f60af8")!)
46+
```
47+
48+
## License
49+
50+
This project is licensed for use under the MIT License (MIT). Please see [LICENSE](LICENSE) for more information.

Sources/CUUIDKit/include/CUUIDKit.h

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef UUID_KIT_H
2+
#define UUID_KIT_H
3+
4+
#if defined(__linux__)
5+
#include <linux/if_packet.h>
6+
#include <net/if_arp.h>
7+
#endif
8+
9+
#endif

Sources/CUUIDKit/src/CUUIDKit.c

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#include "CUUIDKit.h"

Sources/UUIDKit/Clock.swift

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import Foundation
2+
3+
extension UUID {
4+
internal final class Clock {
5+
func next() -> (timestamp: Timestamp, clockSequence: ClockSequence) {
6+
queue.sync {
7+
let timestamp = Timestamp()
8+
if timestamp <= lastTimestamp {
9+
clockSequence = (clockSequence &+ 1) & 0x3f
10+
}
11+
lastTimestamp = timestamp
12+
return (timestamp, clockSequence)
13+
}
14+
}
15+
16+
static let `default` = Clock()
17+
18+
private let queue = DispatchQueue(label: "UUIDKit:Foundation.UUID.Clock")
19+
private var clockSequence = ClockSequence.random(in: 0...(.max)) & 0x3f
20+
private var lastTimestamp = Timestamp.zero
21+
}
22+
}

Sources/UUIDKit/ClockSequence.swift

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Foundation
2+
3+
extension UUID {
4+
internal typealias ClockSequence = UInt16
5+
}

Sources/UUIDKit/Creation.swift

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import Foundation
2+
3+
extension UUID {
4+
/// The nil UUID with all 128 bits set to zero.
5+
public static let null = UUID(uuid: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
6+
7+
/// Creates a new time-based (version 1) UUID.
8+
///
9+
/// The UUID is built from the current time and the Ethernet address. When the Ethernet address
10+
/// cannot be determined, a random value is used instead.
11+
///
12+
/// When a random node is used, the least significand bit of byte 10 is set to 1.
13+
///
14+
/// - warning: This method may expose the Ethernet address of the user's computer. If privacy is
15+
/// a concern, you should use a random UUID (`.v4`) instead.
16+
public static func v1() -> UUID {
17+
UUIDv1().rawValue
18+
}
19+
20+
/// Creates a new name-based (version 3) UUID using MD5 hashing.
21+
///
22+
/// - warning: Unless backward compatibility is an issue, SHA-1 (`.v5`) should be preferred.
23+
/// - parameter name: A value that is unique within the given namespace.
24+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
25+
public static func v3<T>(name: T, namespace: Namespace) -> UUID where T: Collection, T.Element == UInt8 {
26+
UUIDv3(name: name, namespace: namespace).rawValue
27+
}
28+
29+
/// Creates a new name-based (version 3) UUID using MD5 hashing.
30+
///
31+
/// - warning: Unless backward compatibility is an issue, SHA-1 (`.v5`) should be preferred.
32+
/// - parameter name: A value that is unique within the given namespace.
33+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
34+
public static func v3<T>(name: T, namespace: Namespace) -> UUID where T: DataProtocol {
35+
UUIDv3(name: name, namespace: namespace).rawValue
36+
}
37+
38+
/// Creates a new name-based (version 3) UUID using MD5 hashing.
39+
///
40+
/// - warning: Unless backward compatibility is an issue, SHA-1 (`.v5`) should be preferred.
41+
/// - parameter name: A value that is unique within the given namespace.
42+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
43+
public static func v3<T>(name: T, namespace: Namespace) -> UUID where T: StringProtocol {
44+
UUIDv3(name: name, namespace: namespace).rawValue
45+
}
46+
47+
/// Creates a new random (version 4) UUID.
48+
///
49+
/// The system's default source of random is used to create the new UUID.
50+
public static func v4() -> UUID {
51+
UUIDv4().rawValue
52+
}
53+
54+
/// Creates a new random (version 4) UUID, using the given generator as a source for randomness.
55+
///
56+
/// - parameter generator: The random number generator to use when creating the new random UUID.
57+
public static func v4<T>(using generator: inout T) -> UUID where T: RandomNumberGenerator {
58+
UUIDv4(using: &generator).rawValue
59+
}
60+
61+
/// Creates a new name-based (version 5) UUID using SHA-1 hashing.
62+
///
63+
/// - parameter name: A value that is unique within the given namespace.
64+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
65+
public static func v5<T>(name: T, namespace: Namespace) -> UUID where T: Collection, T.Element == UInt8 {
66+
UUIDv5(name: name, namespace: namespace).rawValue
67+
}
68+
69+
/// Creates a new name-based (version 5) UUID using SHA-1 hashing.
70+
///
71+
/// - parameter name: A value that is unique within the given namespace.
72+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
73+
public static func v5<T>(name: T, namespace: Namespace) -> UUID where T: DataProtocol {
74+
UUIDv5(name: name, namespace: namespace).rawValue
75+
}
76+
77+
/// Creates a new name-based (version 5) UUID using SHA-1 hashing.
78+
///
79+
/// - parameter name: A value that is unique within the given namespace.
80+
/// - parameter namespace: An UUID namespace used as a prefix for `name`.
81+
public static func v5<T>(name: T, namespace: Namespace) -> UUID where T: StringProtocol {
82+
UUIDv5(name: name, namespace: namespace).rawValue
83+
}
84+
}
85+
86+
extension UUID {
87+
internal init(bytes source: UnsafeRawBufferPointer, version: Version) {
88+
precondition(source.count >= 16)
89+
var bytes = UUID.null.uuid
90+
withUnsafeMutableBytes(of: &bytes) { target in
91+
_ = source.copyBytes(to: target, count: 16)
92+
}
93+
bytes.8 = bytes.8 & 0x3f | 0x80
94+
bytes.6 = bytes.6 & 0x0f | version << 4
95+
self.init(uuid: bytes)
96+
assert(self.version == version)
97+
}
98+
}

Sources/UUIDKit/Crypto.swift

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Crypto
2+
import Foundation
3+
4+
extension UUID {
5+
internal init<T>(digest: T, version: Version) where T: Digest {
6+
self = digest.withUnsafeBytes { bytes in
7+
UUID(bytes: bytes, version: version)
8+
}
9+
}
10+
}
11+
12+
extension HashFunction {
13+
internal mutating func update(uuid: UUID) {
14+
withUnsafeBytes(of: uuid.uuid) { bytes in
15+
update(bufferPointer: bytes)
16+
}
17+
}
18+
19+
internal mutating func update<T>(collection: T) where T: Collection, T.Element == UInt8 {
20+
collection.withContiguousStorageIfAvailable { storage in
21+
storage.withUnsafeBytes { bytes in
22+
update(bufferPointer: bytes)
23+
}
24+
} ?? update(data: Data(collection))
25+
}
26+
}

Sources/UUIDKit/Exports.swift

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@_exported import struct Foundation.UUID

Sources/UUIDKit/Namespace.swift

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import Foundation
2+
3+
extension UUID {
4+
/// A UUID prefix used when generating a name-based UUID using either `UUID.v3` or `UUID.v5`.
5+
///
6+
/// You can generate a new UUID as a namespace or use one of the pre-defined values
7+
/// (`.dns`, `.url`, `.oid` or `.x500`).
8+
public struct Namespace: Hashable, RawRepresentable {
9+
/// Creates a namespace with the specified UUID.
10+
///
11+
/// - parameter uuid: The UUID to use as the namespace.
12+
public init(_ uuid: UUID) {
13+
self.init(rawValue: uuid)
14+
}
15+
16+
public init(rawValue: UUID) {
17+
self.rawValue = rawValue
18+
}
19+
20+
public let rawValue: UUID
21+
22+
/// The namespace to use with a fully-qualified domain name.
23+
public static let dns = Namespace(UUID(
24+
uuid: (0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8)
25+
))
26+
27+
/// The namespace to use with a URL.
28+
public static let url = Namespace(UUID(
29+
uuid: (0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8)
30+
))
31+
32+
/// The namespace to use with a ISO OID.
33+
public static let oid = Namespace(UUID(
34+
uuid: (0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8)
35+
))
36+
37+
/// The namespace to use with a X.500 DN.
38+
public static let x500 = Namespace(UUID(
39+
uuid: (0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8)
40+
))
41+
}
42+
}

0 commit comments

Comments
 (0)