StringUtils.swift 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. // Sources/SwiftProtobuf/StringUtils.swift - String utility functions
  2. //
  3. // Copyright (c) 2014 - 2016 Apple Inc. and the project authors
  4. // Licensed under Apache License v2.0 with Runtime Library Exception
  5. //
  6. // See LICENSE.txt for license information:
  7. // https://github.com/apple/swift-protobuf/blob/main/LICENSE.txt
  8. //
  9. // -----------------------------------------------------------------------------
  10. ///
  11. /// Utility functions for converting UTF8 bytes into Strings.
  12. /// These functions must:
  13. /// * Accept any valid UTF8, including a zero byte (which is
  14. /// a valid UTF8 encoding of U+0000)
  15. /// * Return nil for any invalid UTF8
  16. /// * Be fast (since they're extensively used by all decoders
  17. /// and even some of the encoders)
  18. ///
  19. // -----------------------------------------------------------------------------
  20. import Foundation
  21. // Note: Once our minimum support version is at least Swift 5.3, we should
  22. // probably recast the following to use String(unsafeUninitializedCapacity:)
  23. // Note: We're trying to avoid Foundation's String(format:) since that's not
  24. // universally available.
  25. private func formatZeroPaddedInt(_ value: Int32, digits: Int) -> String {
  26. precondition(value >= 0)
  27. let s = String(value)
  28. if s.count >= digits {
  29. return s
  30. } else {
  31. let pad = String(repeating: "0", count: digits - s.count)
  32. return pad + s
  33. }
  34. }
  35. internal func twoDigit(_ value: Int32) -> String {
  36. formatZeroPaddedInt(value, digits: 2)
  37. }
  38. internal func threeDigit(_ value: Int32) -> String {
  39. formatZeroPaddedInt(value, digits: 3)
  40. }
  41. internal func fourDigit(_ value: Int32) -> String {
  42. formatZeroPaddedInt(value, digits: 4)
  43. }
  44. internal func sixDigit(_ value: Int32) -> String {
  45. formatZeroPaddedInt(value, digits: 6)
  46. }
  47. internal func nineDigit(_ value: Int32) -> String {
  48. formatZeroPaddedInt(value, digits: 9)
  49. }
  50. // Wrapper that takes a buffer and start/end offsets
  51. internal func utf8ToString(
  52. bytes: UnsafeRawBufferPointer,
  53. start: UnsafeRawBufferPointer.Index,
  54. end: UnsafeRawBufferPointer.Index
  55. ) -> String? {
  56. utf8ToString(bytes: bytes.baseAddress! + start, count: end - start)
  57. }
  58. // Swift 4 introduced new faster String facilities
  59. // that seem to work consistently across all platforms.
  60. // Notes on performance:
  61. //
  62. // The pre-verification here only takes about 10% of
  63. // the time needed for constructing the string.
  64. // Eliminating it would provide only a very minor
  65. // speed improvement.
  66. //
  67. // On macOS, this is only about 25% faster than
  68. // the Foundation initializer used below for Swift 3.
  69. // On Linux, the Foundation initializer is much
  70. // slower than on macOS, so this is a much bigger
  71. // win there.
  72. internal func utf8ToString(bytes: UnsafeRawPointer, count: Int) -> String? {
  73. if count == 0 {
  74. return String()
  75. }
  76. let codeUnits = UnsafeRawBufferPointer(start: bytes, count: count)
  77. let sourceEncoding = Unicode.UTF8.self
  78. // Verify that the UTF-8 is valid.
  79. var p = sourceEncoding.ForwardParser()
  80. var i = codeUnits.makeIterator()
  81. Loop: while true {
  82. switch p.parseScalar(from: &i) {
  83. case .valid(_):
  84. break
  85. case .error:
  86. return nil
  87. case .emptyInput:
  88. break Loop
  89. }
  90. }
  91. // This initializer is fast but does not reject broken
  92. // UTF-8 (which is why we validate the UTF-8 above).
  93. return String(decoding: codeUnits, as: sourceEncoding)
  94. }