/* This source file is part of the Swift.org open source project Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors Licensed under Apache License v2.0 with Runtime Library Exception See http://swift.org/LICENSE.txt for license information See http://swift.org/CONTRIBUTORS.txt for Swift project authors */ import Foundation /// A `ByteString` represents a sequence of bytes. /// /// This struct provides useful operations for working with buffers of /// bytes. Conceptually it is just a contiguous array of bytes (UInt8), but it /// contains methods and default behavior suitable for common operations done /// using bytes strings. /// /// This struct *is not* intended to be used for significant mutation of byte /// strings, we wish to retain the flexibility to micro-optimize the memory /// allocation of the storage (for example, by inlining the storage for small /// strings or and by eliminating wasted space in growable arrays). For /// construction of byte arrays, clients should use the `WritableByteStream` class /// and then convert to a `ByteString` when complete. public struct ByteString: ExpressibleByArrayLiteral, Hashable, Sendable { /// The buffer contents. @usableFromInline internal var _bytes: [UInt8] /// Create an empty byte string. @inlinable public init() { _bytes = [] } /// Create a byte string from a byte array literal. @inlinable public init(arrayLiteral contents: UInt8...) { _bytes = contents } /// Create a byte string from an array of bytes. @inlinable public init(_ contents: [UInt8]) { _bytes = contents } /// Create a byte string from an array slice. @inlinable public init(_ contents: ArraySlice) { _bytes = Array(contents) } /// Create a byte string from an byte buffer. @inlinable public init (_ contents: S) where S.Iterator.Element == UInt8 { _bytes = [UInt8](contents) } /// Create a byte string from the UTF8 encoding of a string. @inlinable public init(encodingAsUTF8 string: String) { _bytes = [UInt8](string.utf8) } /// Access the byte string contents as an array. @inlinable public var contents: [UInt8] { return _bytes } /// Return the byte string size. @inlinable public var count: Int { return _bytes.count } /// Gives a non-escaping closure temporary access to an immutable `Data` instance wrapping the `ByteString` without /// copying any memory around. /// /// - Parameters: /// - closure: The closure that will have access to a `Data` instance for the duration of its lifetime. @inlinable public func withData(_ closure: (Data) throws -> T) rethrows -> T { return try _bytes.withUnsafeBytes { pointer -> T in let mutatingPointer = UnsafeMutableRawPointer(mutating: pointer.baseAddress!) let data = Data(bytesNoCopy: mutatingPointer, count: pointer.count, deallocator: .none) return try closure(data) } } /// Returns a `String` lowercase hexadecimal representation of the contents of the `ByteString`. @inlinable public var hexadecimalRepresentation: String { _bytes.reduce("") { var str = String($1, radix: 16) // The above method does not do zero padding. if str.count == 1 { str = "0" + str } return $0 + str } } } /// Conform to CustomDebugStringConvertible. extension ByteString: CustomStringConvertible { /// Return the string decoded as a UTF8 sequence, or traps if not possible. public var description: String { return cString } /// Return the string decoded as a UTF8 sequence, if possible. @inlinable public var validDescription: String? { // FIXME: This is very inefficient, we need a way to pass a buffer. It // is also wrong if the string contains embedded '\0' characters. let tmp = _bytes + [UInt8(0)] return tmp.withUnsafeBufferPointer { ptr in return String(validatingUTF8: unsafeBitCast(ptr.baseAddress, to: UnsafePointer.self)) } } /// Return the string decoded as a UTF8 sequence, substituting replacement /// characters for ill-formed UTF8 sequences. @inlinable public var cString: String { return String(decoding: _bytes, as: Unicode.UTF8.self) } @available(*, deprecated, message: "use description or validDescription instead") public var asString: String? { return validDescription } } /// ByteStreamable conformance for a ByteString. extension ByteString: ByteStreamable { @inlinable public func write(to stream: WritableByteStream) { stream.write(_bytes) } } /// StringLiteralConvertable conformance for a ByteString. extension ByteString: ExpressibleByStringLiteral { public typealias UnicodeScalarLiteralType = StringLiteralType public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) { _bytes = [UInt8](value.utf8) } public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) { _bytes = [UInt8](value.utf8) } public init(stringLiteral value: StringLiteralType) { _bytes = [UInt8](value.utf8) } }