String+Extensions.swift 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /*
  2. * Copyright 2023, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. //===----------------------------------------------------------------------===//
  17. //
  18. // This source file is part of the SwiftNIO open source project
  19. //
  20. // Copyright (c) 2017-2023 Apple Inc. and the SwiftNIO project authors
  21. // Licensed under Apache License v2.0
  22. //
  23. // See LICENSE.txt for license information
  24. // See CONTRIBUTORS.txt for the list of SwiftNIO project authors
  25. //
  26. // SPDX-License-Identifier: Apache-2.0
  27. //
  28. //===----------------------------------------------------------------------===//
  29. extension UInt8 {
  30. @inlinable
  31. @available(gRPCSwift 2.0, *)
  32. var isASCII: Bool {
  33. return self <= 127
  34. }
  35. }
  36. extension String.UTF8View {
  37. /// Compares two UTF8 strings as case insensitive ASCII bytes.
  38. ///
  39. /// - Parameter bytes: The string constant in the form of a collection of `UInt8`
  40. /// - Returns: Whether the collection contains **EXACTLY** this array or no, but by ignoring case.
  41. @inlinable
  42. @available(gRPCSwift 2.0, *)
  43. func compareCaseInsensitiveASCIIBytes(to other: String.UTF8View) -> Bool {
  44. // fast path: we can get the underlying bytes of both
  45. let maybeMaybeResult = self.withContiguousStorageIfAvailable { lhsBuffer -> Bool? in
  46. other.withContiguousStorageIfAvailable { rhsBuffer in
  47. if lhsBuffer.count != rhsBuffer.count {
  48. return false
  49. }
  50. for idx in 0 ..< lhsBuffer.count {
  51. // let's hope this gets vectorised ;)
  52. if lhsBuffer[idx] & 0xdf != rhsBuffer[idx] & 0xdf && lhsBuffer[idx].isASCII {
  53. return false
  54. }
  55. }
  56. return true
  57. }
  58. }
  59. if let maybeResult = maybeMaybeResult, let result = maybeResult {
  60. return result
  61. } else {
  62. return self._compareCaseInsensitiveASCIIBytesSlowPath(to: other)
  63. }
  64. }
  65. @inlinable
  66. @inline(never)
  67. @available(gRPCSwift 2.0, *)
  68. func _compareCaseInsensitiveASCIIBytesSlowPath(to other: String.UTF8View) -> Bool {
  69. return self.elementsEqual(other, by: { return (($0 & 0xdf) == ($1 & 0xdf) && $0.isASCII) })
  70. }
  71. }
  72. extension String {
  73. @inlinable
  74. @available(gRPCSwift 2.0, *)
  75. func isEqualCaseInsensitiveASCIIBytes(to: String) -> Bool {
  76. return self.utf8.compareCaseInsensitiveASCIIBytes(to: to.utf8)
  77. }
  78. }