|  | 
|  | 1 | +//===----------------------------------------------------------------------===// | 
|  | 2 | +// | 
|  | 3 | +// This source file is part of the SwiftAWSLambdaRuntime open source project | 
|  | 4 | +// | 
|  | 5 | +// Copyright (c) 2021 Apple Inc. and the SwiftAWSLambdaRuntime project authors | 
|  | 6 | +// Licensed under Apache License v2.0 | 
|  | 7 | +// | 
|  | 8 | +// See LICENSE.txt for license information | 
|  | 9 | +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors | 
|  | 10 | +// | 
|  | 11 | +// SPDX-License-Identifier: Apache-2.0 | 
|  | 12 | +// | 
|  | 13 | +//===----------------------------------------------------------------------===// | 
|  | 14 | + | 
|  | 15 | +import NIOCore | 
|  | 16 | + | 
|  | 17 | +// This is heavily inspired by: | 
|  | 18 | +// https://github.com/swift-extras/swift-extras-uuid | 
|  | 19 | + | 
|  | 20 | +struct LambdaRequestID { | 
|  | 21 | +    typealias uuid_t = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | 
|  | 22 | + | 
|  | 23 | +    var uuid: uuid_t { | 
|  | 24 | +        self._uuid | 
|  | 25 | +    } | 
|  | 26 | + | 
|  | 27 | +    static let null: uuid_t = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | 
|  | 28 | + | 
|  | 29 | +    /// Creates a random [v4](https://tools.ietf.org/html/rfc4122#section-4.1.3) UUID. | 
|  | 30 | +    init() { | 
|  | 31 | +        self = Self.generateRandom() | 
|  | 32 | +    } | 
|  | 33 | + | 
|  | 34 | +    init?(uuidString: String) { | 
|  | 35 | +        guard uuidString.utf8.count == 36 else { | 
|  | 36 | +            return nil | 
|  | 37 | +        } | 
|  | 38 | + | 
|  | 39 | +        if let requestID = uuidString.utf8.withContiguousStorageIfAvailable({ ptr -> LambdaRequestID? in | 
|  | 40 | +            let rawBufferPointer = UnsafeRawBufferPointer(ptr) | 
|  | 41 | +            let requestID = Self.fromPointer(rawBufferPointer) | 
|  | 42 | +            return requestID | 
|  | 43 | +        }) { | 
|  | 44 | +            if let requestID = requestID { | 
|  | 45 | +                self = requestID | 
|  | 46 | +            } else { | 
|  | 47 | +                return nil | 
|  | 48 | +            } | 
|  | 49 | +        } else { | 
|  | 50 | +            var newSwiftCopy = uuidString | 
|  | 51 | +            newSwiftCopy.makeContiguousUTF8() | 
|  | 52 | +            if let value = Self(uuidString: newSwiftCopy) { | 
|  | 53 | +                self = value | 
|  | 54 | +            } else { | 
|  | 55 | +                return nil | 
|  | 56 | +            } | 
|  | 57 | +        } | 
|  | 58 | +    } | 
|  | 59 | + | 
|  | 60 | +    /// Creates a UUID from a `uuid_t`. | 
|  | 61 | +    init(uuid: uuid_t) { | 
|  | 62 | +        self._uuid = uuid | 
|  | 63 | +    } | 
|  | 64 | + | 
|  | 65 | +    private let _uuid: uuid_t | 
|  | 66 | + | 
|  | 67 | +    /// Returns a string representation for the `LambdaRequestID`, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" | 
|  | 68 | +    var uuidString: String { | 
|  | 69 | +        self.uppercased | 
|  | 70 | +    } | 
|  | 71 | + | 
|  | 72 | +    /// Returns a lowercase string representation for the `LambdaRequestID`, such as "e621e1f8-c36c-495a-93fc-0c247a3e6e5f" | 
|  | 73 | +    var lowercased: String { | 
|  | 74 | +        var bytes = self.toAsciiBytesOnStack(characters: Self.lowercaseLookup) | 
|  | 75 | +        return withUnsafeBytes(of: &bytes) { | 
|  | 76 | +            String(decoding: $0, as: Unicode.UTF8.self) | 
|  | 77 | +        } | 
|  | 78 | +    } | 
|  | 79 | + | 
|  | 80 | +    /// Returns an uppercase string representation for the `LambdaRequestID`, such as "E621E1F8-C36C-495A-93FC-0C247A3E6E5F" | 
|  | 81 | +    var uppercased: String { | 
|  | 82 | +        var bytes = self.toAsciiBytesOnStack(characters: Self.uppercaseLookup) | 
|  | 83 | +        return withUnsafeBytes(of: &bytes) { | 
|  | 84 | +            String(decoding: $0, as: Unicode.UTF8.self) | 
|  | 85 | +        } | 
|  | 86 | +    } | 
|  | 87 | + | 
|  | 88 | +    /// thread safe secure random number generator. | 
|  | 89 | +    private static var generator = SystemRandomNumberGenerator() | 
|  | 90 | +    private static func generateRandom() -> Self { | 
|  | 91 | +        var _uuid: uuid_t = LambdaRequestID.null | 
|  | 92 | +        // https://tools.ietf.org/html/rfc4122#page-14 | 
|  | 93 | +        // o  Set all the other bits to randomly (or pseudo-randomly) chosen | 
|  | 94 | +        //    values. | 
|  | 95 | +        withUnsafeMutableBytes(of: &_uuid) { ptr in | 
|  | 96 | +            ptr.storeBytes(of: Self.generator.next(), toByteOffset: 0, as: UInt64.self) | 
|  | 97 | +            ptr.storeBytes(of: Self.generator.next(), toByteOffset: 8, as: UInt64.self) | 
|  | 98 | +        } | 
|  | 99 | + | 
|  | 100 | +        // o  Set the four most significant bits (bits 12 through 15) of the | 
|  | 101 | +        //    time_hi_and_version field to the 4-bit version number from | 
|  | 102 | +        //    Section 4.1.3. | 
|  | 103 | +        _uuid.6 = (_uuid.6 & 0x0F) | 0x40 | 
|  | 104 | + | 
|  | 105 | +        // o  Set the two most significant bits (bits 6 and 7) of the | 
|  | 106 | +        //    clock_seq_hi_and_reserved to zero and one, respectively. | 
|  | 107 | +        _uuid.8 = (_uuid.8 & 0x3F) | 0x80 | 
|  | 108 | +        return LambdaRequestID(uuid: _uuid) | 
|  | 109 | +    } | 
|  | 110 | +} | 
|  | 111 | + | 
|  | 112 | +// MARK: - Protocol extensions - | 
|  | 113 | + | 
|  | 114 | +extension LambdaRequestID: Equatable { | 
|  | 115 | +    // sadly no auto conformance from the compiler | 
|  | 116 | +    static func == (lhs: Self, rhs: Self) -> Bool { | 
|  | 117 | +        lhs._uuid.0 == rhs._uuid.0 && | 
|  | 118 | +            lhs._uuid.1 == rhs._uuid.1 && | 
|  | 119 | +            lhs._uuid.2 == rhs._uuid.2 && | 
|  | 120 | +            lhs._uuid.3 == rhs._uuid.3 && | 
|  | 121 | +            lhs._uuid.4 == rhs._uuid.4 && | 
|  | 122 | +            lhs._uuid.5 == rhs._uuid.5 && | 
|  | 123 | +            lhs._uuid.6 == rhs._uuid.6 && | 
|  | 124 | +            lhs._uuid.7 == rhs._uuid.7 && | 
|  | 125 | +            lhs._uuid.8 == rhs._uuid.8 && | 
|  | 126 | +            lhs._uuid.9 == rhs._uuid.9 && | 
|  | 127 | +            lhs._uuid.10 == rhs._uuid.10 && | 
|  | 128 | +            lhs._uuid.11 == rhs._uuid.11 && | 
|  | 129 | +            lhs._uuid.12 == rhs._uuid.12 && | 
|  | 130 | +            lhs._uuid.13 == rhs._uuid.13 && | 
|  | 131 | +            lhs._uuid.14 == rhs._uuid.14 && | 
|  | 132 | +            lhs._uuid.15 == rhs._uuid.15 | 
|  | 133 | +    } | 
|  | 134 | +} | 
|  | 135 | + | 
|  | 136 | +extension LambdaRequestID: Hashable { | 
|  | 137 | +    func hash(into hasher: inout Hasher) { | 
|  | 138 | +        var value = self._uuid | 
|  | 139 | +        withUnsafeBytes(of: &value) { ptr in | 
|  | 140 | +            hasher.combine(bytes: ptr) | 
|  | 141 | +        } | 
|  | 142 | +    } | 
|  | 143 | +} | 
|  | 144 | + | 
|  | 145 | +extension LambdaRequestID: CustomStringConvertible { | 
|  | 146 | +    var description: String { | 
|  | 147 | +        self.uuidString | 
|  | 148 | +    } | 
|  | 149 | +} | 
|  | 150 | + | 
|  | 151 | +extension LambdaRequestID: CustomDebugStringConvertible { | 
|  | 152 | +    var debugDescription: String { | 
|  | 153 | +        self.uuidString | 
|  | 154 | +    } | 
|  | 155 | +} | 
|  | 156 | + | 
|  | 157 | +extension LambdaRequestID: Decodable { | 
|  | 158 | +    init(from decoder: Decoder) throws { | 
|  | 159 | +        let container = try decoder.singleValueContainer() | 
|  | 160 | +        let uuidString = try container.decode(String.self) | 
|  | 161 | + | 
|  | 162 | +        guard let uuid = LambdaRequestID.fromString(uuidString) else { | 
|  | 163 | +            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Attempted to decode UUID from invalid UUID string.") | 
|  | 164 | +        } | 
|  | 165 | + | 
|  | 166 | +        self = uuid | 
|  | 167 | +    } | 
|  | 168 | +} | 
|  | 169 | + | 
|  | 170 | +extension LambdaRequestID: Encodable { | 
|  | 171 | +    func encode(to encoder: Encoder) throws { | 
|  | 172 | +        var container = encoder.singleValueContainer() | 
|  | 173 | +        try container.encode(self.uuidString) | 
|  | 174 | +    } | 
|  | 175 | +} | 
|  | 176 | + | 
|  | 177 | +// MARK: - Implementation details - | 
|  | 178 | + | 
|  | 179 | +extension LambdaRequestID { | 
|  | 180 | +    fileprivate static let lowercaseLookup: [UInt8] = [ | 
|  | 181 | +        UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), | 
|  | 182 | +        UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), | 
|  | 183 | +        UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "a"), UInt8(ascii: "b"), | 
|  | 184 | +        UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"), | 
|  | 185 | +    ] | 
|  | 186 | + | 
|  | 187 | +    fileprivate static let uppercaseLookup: [UInt8] = [ | 
|  | 188 | +        UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"), | 
|  | 189 | +        UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"), | 
|  | 190 | +        UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "A"), UInt8(ascii: "B"), | 
|  | 191 | +        UInt8(ascii: "C"), UInt8(ascii: "D"), UInt8(ascii: "E"), UInt8(ascii: "F"), | 
|  | 192 | +    ] | 
|  | 193 | + | 
|  | 194 | +    /// Use this type to create a backing store for a 8-4-4-4-12 UUID String on stack. | 
|  | 195 | +    /// | 
|  | 196 | +    /// Using this type we ensure to only have one allocation for creating a String even before Swift 5.3 and it can | 
|  | 197 | +    /// also be used as an intermediary before copying the string bytes into a NIO `ByteBuffer`. | 
|  | 198 | +    fileprivate typealias uuid_string_t = ( | 
|  | 199 | +        UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | 
|  | 200 | +        UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | 
|  | 201 | +        UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8 | 
|  | 202 | +    ) | 
|  | 203 | + | 
|  | 204 | +    fileprivate static let nullString: uuid_string_t = ( | 
|  | 205 | +        0, 0, 0, 0, 0, 0, 0, 0, UInt8(ascii: "-"), | 
|  | 206 | +        0, 0, 0, 0, UInt8(ascii: "-"), | 
|  | 207 | +        0, 0, 0, 0, UInt8(ascii: "-"), | 
|  | 208 | +        0, 0, 0, 0, UInt8(ascii: "-"), | 
|  | 209 | +        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 | 
|  | 210 | +    ) | 
|  | 211 | + | 
|  | 212 | +    fileprivate func toAsciiBytesOnStack(characters: [UInt8]) -> uuid_string_t { | 
|  | 213 | +        var string: uuid_string_t = Self.nullString | 
|  | 214 | +        // to get the best performance we access the lookup table's unsafe buffer pointer | 
|  | 215 | +        // since the lookup table has 16 elements and we shift the byte values in such a way | 
|  | 216 | +        // that the max value is 15 (last 4 bytes = 16 values). For this reason the lookups | 
|  | 217 | +        // are safe and we don't need Swifts safety guards. | 
|  | 218 | +         | 
|  | 219 | +        characters.withUnsafeBufferPointer { lookup in | 
|  | 220 | +            string.0 = lookup[Int(uuid.0 >> 4)] | 
|  | 221 | +            string.1 = lookup[Int(uuid.0 & 0x0F)] | 
|  | 222 | +            string.2 = lookup[Int(uuid.1 >> 4)] | 
|  | 223 | +            string.3 = lookup[Int(uuid.1 & 0x0F)] | 
|  | 224 | +            string.4 = lookup[Int(uuid.2 >> 4)] | 
|  | 225 | +            string.5 = lookup[Int(uuid.2 & 0x0F)] | 
|  | 226 | +            string.6 = lookup[Int(uuid.3 >> 4)] | 
|  | 227 | +            string.7 = lookup[Int(uuid.3 & 0x0F)] | 
|  | 228 | +            string.9 = lookup[Int(uuid.4 >> 4)] | 
|  | 229 | +            string.10 = lookup[Int(uuid.4 & 0x0F)] | 
|  | 230 | +            string.11 = lookup[Int(uuid.5 >> 4)] | 
|  | 231 | +            string.12 = lookup[Int(uuid.5 & 0x0F)] | 
|  | 232 | +            string.14 = lookup[Int(uuid.6 >> 4)] | 
|  | 233 | +            string.15 = lookup[Int(uuid.6 & 0x0F)] | 
|  | 234 | +            string.16 = lookup[Int(uuid.7 >> 4)] | 
|  | 235 | +            string.17 = lookup[Int(uuid.7 & 0x0F)] | 
|  | 236 | +            string.19 = lookup[Int(uuid.8 >> 4)] | 
|  | 237 | +            string.20 = lookup[Int(uuid.8 & 0x0F)] | 
|  | 238 | +            string.21 = lookup[Int(uuid.9 >> 4)] | 
|  | 239 | +            string.22 = lookup[Int(uuid.9 & 0x0F)] | 
|  | 240 | +            string.24 = lookup[Int(uuid.10 >> 4)] | 
|  | 241 | +            string.25 = lookup[Int(uuid.10 & 0x0F)] | 
|  | 242 | +            string.26 = lookup[Int(uuid.11 >> 4)] | 
|  | 243 | +            string.27 = lookup[Int(uuid.11 & 0x0F)] | 
|  | 244 | +            string.28 = lookup[Int(uuid.12 >> 4)] | 
|  | 245 | +            string.29 = lookup[Int(uuid.12 & 0x0F)] | 
|  | 246 | +            string.30 = lookup[Int(uuid.13 >> 4)] | 
|  | 247 | +            string.31 = lookup[Int(uuid.13 & 0x0F)] | 
|  | 248 | +            string.32 = lookup[Int(uuid.14 >> 4)] | 
|  | 249 | +            string.33 = lookup[Int(uuid.14 & 0x0F)] | 
|  | 250 | +            string.34 = lookup[Int(uuid.15 >> 4)] | 
|  | 251 | +            string.35 = lookup[Int(uuid.15 & 0x0F)] | 
|  | 252 | +        } | 
|  | 253 | + | 
|  | 254 | +        return string | 
|  | 255 | +    } | 
|  | 256 | + | 
|  | 257 | +    static func fromString(_ string: String) -> LambdaRequestID? { | 
|  | 258 | +        guard string.utf8.count == 36 else { | 
|  | 259 | +            // invalid length | 
|  | 260 | +            return nil | 
|  | 261 | +        } | 
|  | 262 | +        var string = string | 
|  | 263 | +        return string.withUTF8 { | 
|  | 264 | +            LambdaRequestID.fromPointer(.init($0)) | 
|  | 265 | +        } | 
|  | 266 | +    } | 
|  | 267 | +} | 
|  | 268 | + | 
|  | 269 | +extension LambdaRequestID { | 
|  | 270 | +    static func fromPointer(_ ptr: UnsafeRawBufferPointer) -> LambdaRequestID? { | 
|  | 271 | +        func uint4Value(from value: UInt8, valid: inout Bool) -> UInt8 { | 
|  | 272 | +            switch value { | 
|  | 273 | +            case UInt8(ascii: "0") ... UInt8(ascii: "9"): | 
|  | 274 | +                return value &- UInt8(ascii: "0") | 
|  | 275 | +            case UInt8(ascii: "a") ... UInt8(ascii: "f"): | 
|  | 276 | +                return value &- UInt8(ascii: "a") &+ 10 | 
|  | 277 | +            case UInt8(ascii: "A") ... UInt8(ascii: "F"): | 
|  | 278 | +                return value &- UInt8(ascii: "A") &+ 10 | 
|  | 279 | +            default: | 
|  | 280 | +                valid = false | 
|  | 281 | +                return 0 | 
|  | 282 | +            } | 
|  | 283 | +        } | 
|  | 284 | +         | 
|  | 285 | +        func dashCheck(from value: UInt8, valid: inout Bool) { | 
|  | 286 | +            if value != UInt8(ascii: "-") { | 
|  | 287 | +                valid = false | 
|  | 288 | +            } | 
|  | 289 | +        } | 
|  | 290 | +         | 
|  | 291 | +        precondition(ptr.count == 36) | 
|  | 292 | +        var uuid = Self.null | 
|  | 293 | +        var valid = true | 
|  | 294 | +        uuid.0 = uint4Value(from: ptr[0], valid: &valid) &<< 4 &+ uint4Value(from: ptr[1], valid: &valid) | 
|  | 295 | +        uuid.1 = uint4Value(from: ptr[2], valid: &valid) &<< 4 &+ uint4Value(from: ptr[3], valid: &valid) | 
|  | 296 | +        uuid.2 = uint4Value(from: ptr[4], valid: &valid) &<< 4 &+ uint4Value(from: ptr[5], valid: &valid) | 
|  | 297 | +        uuid.3 = uint4Value(from: ptr[6], valid: &valid) &<< 4 &+ uint4Value(from: ptr[7], valid: &valid) | 
|  | 298 | +        dashCheck(from: ptr[8], valid: &valid) | 
|  | 299 | +        uuid.4 = uint4Value(from: ptr[9], valid: &valid) &<< 4 &+ uint4Value(from: ptr[10], valid: &valid) | 
|  | 300 | +        uuid.5 = uint4Value(from: ptr[11], valid: &valid) &<< 4 &+ uint4Value(from: ptr[12], valid: &valid) | 
|  | 301 | +        dashCheck(from: ptr[13], valid: &valid) | 
|  | 302 | +        uuid.6 = uint4Value(from: ptr[14], valid: &valid) &<< 4 &+ uint4Value(from: ptr[15], valid: &valid) | 
|  | 303 | +        uuid.7 = uint4Value(from: ptr[16], valid: &valid) &<< 4 &+ uint4Value(from: ptr[17], valid: &valid) | 
|  | 304 | +        dashCheck(from: ptr[18], valid: &valid) | 
|  | 305 | +        uuid.8 = uint4Value(from: ptr[19], valid: &valid) &<< 4 &+ uint4Value(from: ptr[20], valid: &valid) | 
|  | 306 | +        uuid.9 = uint4Value(from: ptr[21], valid: &valid) &<< 4 &+ uint4Value(from: ptr[22], valid: &valid) | 
|  | 307 | +        dashCheck(from: ptr[23], valid: &valid) | 
|  | 308 | +        uuid.10 = uint4Value(from: ptr[24], valid: &valid) &<< 4 &+ uint4Value(from: ptr[25], valid: &valid) | 
|  | 309 | +        uuid.11 = uint4Value(from: ptr[26], valid: &valid) &<< 4 &+ uint4Value(from: ptr[27], valid: &valid) | 
|  | 310 | +        uuid.12 = uint4Value(from: ptr[28], valid: &valid) &<< 4 &+ uint4Value(from: ptr[29], valid: &valid) | 
|  | 311 | +        uuid.13 = uint4Value(from: ptr[30], valid: &valid) &<< 4 &+ uint4Value(from: ptr[31], valid: &valid) | 
|  | 312 | +        uuid.14 = uint4Value(from: ptr[32], valid: &valid) &<< 4 &+ uint4Value(from: ptr[33], valid: &valid) | 
|  | 313 | +        uuid.15 = uint4Value(from: ptr[34], valid: &valid) &<< 4 &+ uint4Value(from: ptr[35], valid: &valid) | 
|  | 314 | +         | 
|  | 315 | +        if valid { | 
|  | 316 | +            return LambdaRequestID(uuid: uuid) | 
|  | 317 | +        } | 
|  | 318 | + | 
|  | 319 | +        return nil | 
|  | 320 | +    } | 
|  | 321 | +} | 
|  | 322 | + | 
|  | 323 | +extension ByteBuffer { | 
|  | 324 | +    func getRequestID(at index: Int) -> LambdaRequestID? { | 
|  | 325 | +        guard let range = self.rangeWithinReadableBytes(index: index, length: 36) else { | 
|  | 326 | +            return nil | 
|  | 327 | +        } | 
|  | 328 | +        return self.withUnsafeReadableBytes { ptr in | 
|  | 329 | +            LambdaRequestID.fromPointer(UnsafeRawBufferPointer(fastRebase: ptr[range])) | 
|  | 330 | +        } | 
|  | 331 | +    } | 
|  | 332 | + | 
|  | 333 | +    mutating func readRequestID() -> LambdaRequestID? { | 
|  | 334 | +        guard let requestID = self.getRequestID(at: self.readerIndex) else { | 
|  | 335 | +            return nil | 
|  | 336 | +        } | 
|  | 337 | +        self.moveReaderIndex(forwardBy: 36) | 
|  | 338 | +        return requestID | 
|  | 339 | +    } | 
|  | 340 | + | 
|  | 341 | +    @discardableResult | 
|  | 342 | +    mutating func setRequestID(_ requestID: LambdaRequestID, at index: Int) -> Int { | 
|  | 343 | +        var localBytes = requestID.toAsciiBytesOnStack(characters: LambdaRequestID.lowercaseLookup) | 
|  | 344 | +        return withUnsafeBytes(of: &localBytes) { | 
|  | 345 | +            self.setBytes($0, at: index) | 
|  | 346 | +        } | 
|  | 347 | +    } | 
|  | 348 | + | 
|  | 349 | +    mutating func writeRequestID(_ requestID: LambdaRequestID) -> Int { | 
|  | 350 | +        let length = self.setRequestID(requestID, at: self.writerIndex) | 
|  | 351 | +        self.moveWriterIndex(forwardBy: length) | 
|  | 352 | +        return length | 
|  | 353 | +    } | 
|  | 354 | + | 
|  | 355 | +    // copy and pasted from NIOCore | 
|  | 356 | +    func rangeWithinReadableBytes(index: Int, length: Int) -> Range<Int>? { | 
|  | 357 | +        guard index >= self.readerIndex && length >= 0 else { | 
|  | 358 | +            return nil | 
|  | 359 | +        } | 
|  | 360 | + | 
|  | 361 | +        // both these &-s are safe, they can't underflow because both left & right side are >= 0 (and index >= readerIndex) | 
|  | 362 | +        let indexFromReaderIndex = index &- self.readerIndex | 
|  | 363 | +        assert(indexFromReaderIndex >= 0) | 
|  | 364 | +        guard indexFromReaderIndex <= self.readableBytes &- length else { | 
|  | 365 | +            return nil | 
|  | 366 | +        } | 
|  | 367 | + | 
|  | 368 | +        let upperBound = indexFromReaderIndex &+ length // safe, can't overflow, we checked it above. | 
|  | 369 | + | 
|  | 370 | +        // uncheckedBounds is safe because `length` is >= 0, so the lower bound will always be lower/equal to upper | 
|  | 371 | +        return Range<Int>(uncheckedBounds: (lower: indexFromReaderIndex, upper: upperBound)) | 
|  | 372 | +    } | 
|  | 373 | +} | 
|  | 374 | + | 
|  | 375 | +// copy and pasted from NIOCore | 
|  | 376 | +extension UnsafeRawBufferPointer { | 
|  | 377 | +    init(fastRebase slice: Slice<UnsafeRawBufferPointer>) { | 
|  | 378 | +        let base = slice.base.baseAddress?.advanced(by: slice.startIndex) | 
|  | 379 | +        self.init(start: base, count: slice.endIndex &- slice.startIndex) | 
|  | 380 | +    } | 
|  | 381 | +} | 
0 commit comments