1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
/*
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<UInt8>) {
_bytes = Array(contents)
}
/// Create a byte string from an byte buffer.
@inlinable
public init<S: Sequence> (_ 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<T>(_ 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<CChar>.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)
}
}
|