From 16f6f9fede7be03cf90bd997a37eca13ea161c48 Mon Sep 17 00:00:00 2001 From: Tony Arnold Date: Tue, 16 May 2023 22:03:16 +1000 Subject: [PATCH 1/4] Initial attempt to replace C-based UUID functions with Swift implementations --- Sources/FoundationEssentials/UUID.swift | 67 +++++++++++++++---------- 1 file changed, 40 insertions(+), 27 deletions(-) diff --git a/Sources/FoundationEssentials/UUID.swift b/Sources/FoundationEssentials/UUID.swift index 08037ec75..8cde0612b 100644 --- a/Sources/FoundationEssentials/UUID.swift +++ b/Sources/FoundationEssentials/UUID.swift @@ -9,12 +9,6 @@ // //===----------------------------------------------------------------------===// -#if FOUNDATION_FRAMEWORK -@_implementationOnly import _CShims // uuid.h -#else -package import _CShims // uuid.h -#endif - public typealias uuid_t = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) 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) @@ -25,10 +19,12 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { /* Create a new UUID with RFC 4122 version 4 random bytes */ public init() { - withUnsafeMutablePointer(to: &uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size) { - uuid_generate_random($0) - } + var randomBits = (0 ... 15).map { _ in UInt8.random(in: .min ... .max) } + randomBits[6] = (randomBits[6] & 0x0F) | 0x40 + randomBits[8] = (randomBits[8] & 0x3F) | 0x80 + + uuid = randomBits.withUnsafeBytes { buffer in + return buffer.bindMemory(to: uuid_t.self)[0] } } @@ -36,14 +32,18 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { /// /// Returns nil for invalid strings. public init?(uuidString string: __shared String) { - let res = withUnsafeMutablePointer(to: &uuid) { - $0.withMemoryRebound(to: UInt8.self, capacity: 16) { - return uuid_parse(string, $0) - } - } - if res != 0 { + let components = string + .replacing("-", with: "") + .split(by: 2) + .compactMap { UInt8($0, radix: 16) } + + guard components.count == 16 else { return nil } + + uuid = components.withUnsafeBytes { buffer in + return buffer.bindMemory(to: uuid_t.self)[0] + } } /// Create a UUID from a `uuid_t`. @@ -53,17 +53,7 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { /// Returns a string created from the UUID, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" public var uuidString: String { - var bytes: uuid_string_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - return withUnsafePointer(to: uuid) { valPtr in - valPtr.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size) { val in - withUnsafeMutablePointer(to: &bytes) { strPtr in - strPtr.withMemoryRebound(to: CChar.self, capacity: MemoryLayout.size) { str in - uuid_unparse_upper(val, str) - return String(cString: str) - } - } - } - } + "\(Self.formatToHexString(uuid.0))\(Self.formatToHexString(uuid.1))\(Self.formatToHexString(uuid.2))\(Self.formatToHexString(uuid.3))-\(Self.formatToHexString(uuid.4))\(Self.formatToHexString(uuid.5))-\(Self.formatToHexString(uuid.6))\(Self.formatToHexString(uuid.7))-\(Self.formatToHexString(uuid.8))\(Self.formatToHexString(uuid.9))-\(Self.formatToHexString(uuid.10))\(Self.formatToHexString(uuid.11))\(Self.formatToHexString(uuid.12))\(Self.formatToHexString(uuid.13))\(Self.formatToHexString(uuid.14))\(Self.formatToHexString(uuid.15))" } public func hash(into hasher: inout Hasher) { @@ -98,6 +88,14 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { lhs.uuid.14 == rhs.uuid.14 && lhs.uuid.15 == rhs.uuid.15 } + + private static func formatToHexString(_ value: UInt8) -> String { + var result = String(value, radix: 16, uppercase: true) + if result.count == 1 { + result = "0" + result + } + return result + } } @available(macOS 10.8, iOS 6.0, tvOS 9.0, watchOS 2.0, *) @@ -154,3 +152,18 @@ extension UUID : Comparable { return result < 0 } } + +private extension String { + func split(by length: Int) -> [String] { + var startIndex = self.startIndex + var results = [Substring]() + + while startIndex < self.endIndex { + let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex + results.append(self[startIndex.. Date: Tue, 16 May 2023 22:03:29 +1000 Subject: [PATCH 2/4] Remove UUID code from _CShims --- Sources/_CShims/include/uuid.h | 85 ----------- Sources/_CShims/uuid.c | 267 --------------------------------- 2 files changed, 352 deletions(-) delete mode 100644 Sources/_CShims/include/uuid.h delete mode 100644 Sources/_CShims/uuid.c diff --git a/Sources/_CShims/include/uuid.h b/Sources/_CShims/include/uuid.h deleted file mode 100644 index 690339656..000000000 --- a/Sources/_CShims/include/uuid.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. - * - * %Begin-Header% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#ifndef _UUID_UUID_H -#define _UUID_UUID_H - -#include "_CShimsTargetConditionals.h" - -#if TARGET_OS_MAC -#include -#include -#else -#include -typedef unsigned char __darwin_uuid_t[16]; -typedef char __darwin_uuid_string_t[37]; -#ifdef uuid_t -#undef uuid_t -#endif -typedef __darwin_uuid_t uuid_t; -#endif - -#ifndef _UUID_STRING_T -#define _UUID_STRING_T -typedef __darwin_uuid_string_t uuid_string_t; -#endif /* _UUID_STRING_T */ - -#define UUID_DEFINE(name,u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15) \ - static const uuid_t name __attribute__ ((unused)) = {u0,u1,u2,u3,u4,u5,u6,u7,u8,u9,u10,u11,u12,u13,u14,u15} - -#ifdef __cplusplus -extern "C" { -#endif - -void uuid_clear(uuid_t uu); - -int uuid_compare(const uuid_t uu1, const uuid_t uu2); - -void uuid_copy(uuid_t dst, const uuid_t src); - -void uuid_generate(uuid_t out); -void uuid_generate_random(uuid_t out); -void uuid_generate_time(uuid_t out); - -int uuid_is_null(const uuid_t uu); - -int uuid_parse(const uuid_string_t in, uuid_t uu); - -void uuid_unparse(const uuid_t uu, uuid_string_t out); -void uuid_unparse_lower(const uuid_t uu, uuid_string_t out); -void uuid_unparse_upper(const uuid_t uu, uuid_string_t out); - -#ifdef __cplusplus -} -#endif - -#endif /* _UUID_UUID_H */ diff --git a/Sources/_CShims/uuid.c b/Sources/_CShims/uuid.c deleted file mode 100644 index 25bae4127..000000000 --- a/Sources/_CShims/uuid.c +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. - * - * %Begin-Header% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, and the entire permission notice in its entirety, - * including the disclaimer of warranties. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote - * products derived from this software without specific prior - * written permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF - * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT - * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE - * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH - * DAMAGE. - * %End-Header% - */ - -#include "include/uuid.h" - -#if __has_include() -#include -#endif - -#include -#include -#include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) -#include -#elif defined(_WIN32) -#include -#define WIN32_LEAN_AND_MEAN -#include -#include -#endif -#include - -#if TARGET_OS_MAC -#include -#include - -#include - -#include -#include -#include - -static inline void nanotime(struct timespec *tv) { - uint64_t now = mach_absolute_time(); - tv->tv_sec = now / 1000000000; - tv->tv_nsec = now - (tv->tv_sec * 1000000000); -} - -#elif TARGET_OS_LINUX || TARGET_OS_BSD || TARGET_OS_WASI -#include - -static inline void nanotime(struct timespec *tv) { - clock_gettime(CLOCK_MONOTONIC, tv); -} - -#elif TARGET_OS_WINDOWS -#include - -static inline void nanotime(struct timespec *tv) { - FILETIME ftTime; - - GetSystemTimePreciseAsFileTime(&ftTime); - - uint64_t Value = (((uint64_t)ftTime.dwHighDateTime << 32) | ftTime.dwLowDateTime); - - tv->tv_sec = Value / 1000000000; - tv->tv_nsec = Value - (tv->tv_sec * 1000000000); -} -#endif - -#if TARGET_OS_WINDOWS -static inline void read_random(void *buffer, unsigned numBytes) { - BCryptGenRandom(NULL, buffer, numBytes, - BCRYPT_RNG_USE_ENTROPY_IN_BUFFER | BCRYPT_USE_SYSTEM_PREFERRED_RNG); -} -#elif TARGET_OS_WASI -#include - -static inline void read_random(void *buffer, unsigned numBytes) { - getentropy(buffer, numBytes); -} -#else -static inline void read_random(void *buffer, unsigned numBytes) { - int fd = open("/dev/urandom", O_RDONLY); - read(fd, buffer, numBytes); - close(fd); -} -#endif - - -UUID_DEFINE(UUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - -static void -read_node(uint8_t *node) -{ -#if NETWORKING - struct ifnet *ifp; - struct ifaddr *ifa; - struct sockaddr_dl *sdl; - - ifnet_head_lock_shared(); - TAILQ_FOREACH(ifp, &ifnet_head, if_link) { - TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { - sdl = (struct sockaddr_dl *)ifa->ifa_addr; - if (sdl && sdl->sdl_family == AF_LINK && sdl->sdl_type == IFT_ETHER) { - memcpy(node, LLADDR(sdl), 6); - ifnet_head_done(); - return; - } - } - } - ifnet_head_done(); -#endif /* NETWORKING */ - - read_random(node, 6); - node[0] |= 0x01; -} - -static uint64_t -read_time(void) -{ - struct timespec tv; - - nanotime(&tv); - - return (tv.tv_sec * 10000000ULL) + (tv.tv_nsec / 100ULL) + 0x01B21DD213814000ULL; -} - -void -uuid_clear(uuid_t uu) -{ - memset(uu, 0, sizeof(uuid_t)); -} - -int -uuid_compare(const uuid_t uu1, const uuid_t uu2) -{ - return memcmp(uu1, uu2, sizeof(uuid_t)); -} - -void -uuid_copy(uuid_t dst, const uuid_t src) -{ - memcpy(dst, src, sizeof(uuid_t)); -} - -void -uuid_generate_random(uuid_t out) -{ - read_random(out, sizeof(uuid_t)); - - out[6] = (out[6] & 0x0F) | 0x40; - out[8] = (out[8] & 0x3F) | 0x80; -} - -void -uuid_generate_time(uuid_t out) -{ - uint64_t time; - - read_node(&out[10]); - read_random(&out[8], 2); - - time = read_time(); - out[0] = (uint8_t)(time >> 24); - out[1] = (uint8_t)(time >> 16); - out[2] = (uint8_t)(time >> 8); - out[3] = (uint8_t)time; - out[4] = (uint8_t)(time >> 40); - out[5] = (uint8_t)(time >> 32); - out[6] = (uint8_t)(time >> 56); - out[7] = (uint8_t)(time >> 48); - - out[6] = (out[6] & 0x0F) | 0x10; - out[8] = (out[8] & 0x3F) | 0x80; -} - -void -uuid_generate(uuid_t out) -{ - uuid_generate_random(out); -} - -int -uuid_is_null(const uuid_t uu) -{ - return !memcmp(uu, UUID_NULL, sizeof(uuid_t)); -} - -int -uuid_parse(const uuid_string_t in, uuid_t uu) -{ - int n = 0; - - sscanf(in, - "%2hhx%2hhx%2hhx%2hhx-" - "%2hhx%2hhx-" - "%2hhx%2hhx-" - "%2hhx%2hhx-" - "%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%n", - &uu[0], &uu[1], &uu[2], &uu[3], - &uu[4], &uu[5], - &uu[6], &uu[7], - &uu[8], &uu[9], - &uu[10], &uu[11], &uu[12], &uu[13], &uu[14], &uu[15], &n); - - return (n != 36 || in[n] != '\0' ? -1 : 0); -} - -void -uuid_unparse_lower(const uuid_t uu, uuid_string_t out) -{ - snprintf(out, - sizeof(uuid_string_t), - "%02x%02x%02x%02x-" - "%02x%02x-" - "%02x%02x-" - "%02x%02x-" - "%02x%02x%02x%02x%02x%02x", - uu[0], uu[1], uu[2], uu[3], - uu[4], uu[5], - uu[6], uu[7], - uu[8], uu[9], - uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); -} - -void -uuid_unparse_upper(const uuid_t uu, uuid_string_t out) -{ - snprintf(out, - sizeof(uuid_string_t), - "%02X%02X%02X%02X-" - "%02X%02X-" - "%02X%02X-" - "%02X%02X-" - "%02X%02X%02X%02X%02X%02X", - uu[0], uu[1], uu[2], uu[3], - uu[4], uu[5], - uu[6], uu[7], - uu[8], uu[9], - uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]); -} - -void -uuid_unparse(const uuid_t uu, uuid_string_t out) -{ - uuid_unparse_upper(uu, out); -} From c6fab7aca3bbbcc45d361d92bd6191dd7f397e2b Mon Sep 17 00:00:00 2001 From: Tony Arnold Date: Wed, 17 May 2023 09:12:00 +1000 Subject: [PATCH 3/4] Begin to separate the UUIDv4 specific code --- Sources/FoundationEssentials/UUID.swift | 48 +++++++-------------- Sources/FoundationEssentials/UUIDv4.swift | 52 +++++++++++++++++++++++ 2 files changed, 67 insertions(+), 33 deletions(-) create mode 100644 Sources/FoundationEssentials/UUIDv4.swift diff --git a/Sources/FoundationEssentials/UUID.swift b/Sources/FoundationEssentials/UUID.swift index 8cde0612b..a428dc5a4 100644 --- a/Sources/FoundationEssentials/UUID.swift +++ b/Sources/FoundationEssentials/UUID.swift @@ -17,32 +17,29 @@ public typealias uuid_string_t = (Int8, Int8, Int8, Int8, Int8, Int8, Int8, Int8 public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { public private(set) var uuid: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) - /* Create a new UUID with RFC 4122 version 4 random bytes */ - public init() { - var randomBits = (0 ... 15).map { _ in UInt8.random(in: .min ... .max) } - randomBits[6] = (randomBits[6] & 0x0F) | 0x40 - randomBits[8] = (randomBits[8] & 0x3F) | 0x80 + public enum Version { + case v4 + } - uuid = randomBits.withUnsafeBytes { buffer in - return buffer.bindMemory(to: uuid_t.self)[0] + /* Create a new UUID with RFC 4122 version 4 random bytes */ + public init(version: Version = .v4) { + switch version { + case .v4: + self.uuid = Self.v4_generatedRandom() } } /// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F". /// /// Returns nil for invalid strings. - public init?(uuidString string: __shared String) { - let components = string - .replacing("-", with: "") - .split(by: 2) - .compactMap { UInt8($0, radix: 16) } - - guard components.count == 16 else { - return nil - } + public init?(version: Version = .v4, uuidString string: __shared String) { + switch version { + case .v4: + guard let parsedResult = Self.v4_parse(uuidString: uuidString) else { + return nil + } - uuid = components.withUnsafeBytes { buffer in - return buffer.bindMemory(to: uuid_t.self)[0] + self.uuid = parsedResult } } @@ -152,18 +149,3 @@ extension UUID : Comparable { return result < 0 } } - -private extension String { - func split(by length: Int) -> [String] { - var startIndex = self.startIndex - var results = [Substring]() - - while startIndex < self.endIndex { - let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex - results.append(self[startIndex.. uuid_t { + var randomBits = (0 ... 15).map { _ in UInt8.random(in: .min ... .max) } + randomBits[6] = (randomBits[6] & 0x0F) | 0x40 + randomBits[8] = (randomBits[8] & 0x3F) | 0x80 + + return randomBits.withUnsafeBytes { buffer in + return buffer.bindMemory(to: uuid_t.self)[0] + } + } + + static func v4_parse(uuidString string: __shared String) -> uuid_t? { + let components = string + .replacing("-", with: "") + .split(by: 2) + .compactMap { UInt8($0, radix: 16) } + + guard components.count == 16 else { + return nil + } + + return components.withUnsafeBytes { buffer in + return buffer.bindMemory(to: uuid_t.self)[0] + } + } +} + +private extension String { + func split(by length: Int) -> [String] { + var startIndex = self.startIndex + var results = [Substring]() + + while startIndex < self.endIndex { + let endIndex = self.index(startIndex, offsetBy: length, limitedBy: self.endIndex) ?? self.endIndex + results.append(self[startIndex.. Date: Sun, 4 Jun 2023 13:39:29 +1000 Subject: [PATCH 4/4] Restore the original UUID initializers --- Sources/FoundationEssentials/UUID.swift | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Sources/FoundationEssentials/UUID.swift b/Sources/FoundationEssentials/UUID.swift index a428dc5a4..f3073c28a 100644 --- a/Sources/FoundationEssentials/UUID.swift +++ b/Sources/FoundationEssentials/UUID.swift @@ -22,7 +22,12 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { } /* Create a new UUID with RFC 4122 version 4 random bytes */ - public init(version: Version = .v4) { + public init() { + self.init(version: .v4) + } + + /* Create a new UUID with RFC 4122 version 4 random bytes */ + public init(version: Version) { switch version { case .v4: self.uuid = Self.v4_generatedRandom() @@ -32,7 +37,14 @@ public struct UUID : Hashable, Equatable, CustomStringConvertible, Sendable { /// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F". /// /// Returns nil for invalid strings. - public init?(version: Version = .v4, uuidString string: __shared String) { + public init?(uuidString string: __shared String) { + self.init(version: .v4, uuidString: string) + } + + /// Create a UUID from a string such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F". + /// + /// Returns nil for invalid strings. + public init?(version: Version, uuidString string: __shared String) { switch version { case .v4: guard let parsedResult = Self.v4_parse(uuidString: uuidString) else {