-
Notifications
You must be signed in to change notification settings - Fork 125
/
Copy pathByteString.swift
164 lines (142 loc) · 5.46 KB
/
ByteString.swift
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
161
162
163
164
/*
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 behavor 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 {
/// 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 {
guard let description = validDescription else {
fatalError("invalid byte string: \(cString)")
}
return description
}
/// 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)
}
}