File: ByteString.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (209 lines) | stat: -rw-r--r-- 7,340 bytes parent folder | download
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 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 the list of Swift project authors
//
//===----------------------------------------------------------------------===//

public import struct Foundation.Data
public import protocol Foundation.DataProtocol

/// 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 `OutputByteStream` class and then convert to a `ByteString` when complete.
@DebugDescription
public struct ByteString: ExpressibleByArrayLiteral, Sendable {
    /// The buffer contents.
    fileprivate let _bytes: [UInt8]

    /// Create an empty byte string.
    public init() {
        _bytes = []
    }

    /// Create a byte string from a byte array literal.
    public init(arrayLiteral contents: UInt8...) {
        _bytes = contents
    }

    /// Create a byte string from an array of bytes.
    public init(_ contents: some Sequence<UInt8>) {
        _bytes = [UInt8](contents)
    }

    /// Create a byte string from the UTF8 encoding of a string.
    public init(encodingAsUTF8 string: String) {
        // FIXME: We used to have a very valuable ast path for contiguous strings. See: <rdar://problem/29208285>
        _bytes = [UInt8](string.utf8)
    }

    /// Access the byte string contents as an array.
    public var bytes: [UInt8] {
        return _bytes
    }

    public func withContiguousStorageIfAvailable<R>(_ body: (UnsafeBufferPointer<StringData.Element>) throws -> R) rethrows -> R? {
        return try _bytes.withContiguousStorageIfAvailable(body)
    }

    /// Check if the byte string is empty.
    public var isEmpty: Bool {
        return _bytes.isEmpty
    }

    /// Return the byte string size.
    public var count: Int {
        return _bytes.count
    }

    //@available(*, deprecated, renamed: "unsafeStringValue", message: "this method silently replaces invalid UTF-8 sequences with the replacement character (U+FFFD); prefer the failable variant `stringValue` instead")
    public var asString: String {
        return unsafeStringValue
    }

    /// Return the string decoded as a UTF-8 sequence.
    /// Invalid UTF-8 sequences are replaced with the Unicode REPLACEMENT CHARACTER (U+FFFD).
    ///
    /// In most cases, `stringValue` is preferred. Only use this property when you are absolutely certain that the `ByteString` contains valid UTF-8 data, or that potential data loss is acceptable for the use case.
    public var unsafeStringValue: String {
        return String(decoding: _bytes, as: Unicode.UTF8.self)
    }

    /// Return the string decoded as a UTF-8 sequence, if possible.
    public var stringValue: String? {
        return String(bytes: _bytes, encoding: .utf8)
    }

    /// Returns a Boolean value indicating whether the byte string begins with the specified prefix.
    public func hasPrefix(_ prefix: ByteString) -> Bool {
        _bytes.hasPrefix(prefix._bytes)
    }

    /// Returns a Boolean value indicating whether the byte string ends with the specified suffix.
    public func hasSuffix(_ suffix: ByteString) -> Bool {
        _bytes.hasSuffix(suffix._bytes)
    }

    public static func +=(lhs: inout ByteString, rhs: ByteString) {
        lhs = lhs + rhs
    }

    public static func +(lhs: ByteString, rhs: ByteString) -> ByteString {
        return ByteString(lhs._bytes + rhs.bytes)
    }
}

/// Conform to CustomStringConvertible.
extension ByteString: CustomStringConvertible {
    public var description: String {
        // For now, default to the "readable string" representation.
        return "<ByteString:\"\(bytes.asReadableString())\">"
    }
}

/// Hashable conformance for a ByteString.
extension ByteString: Hashable { }

/// Comparison with strings (as UTF8).
public func ==(lhs: ByteString, rhs: String) -> Bool {
    // FIXME: Is Swift's String.UTF8View.count O(1)?
    let utf8 = rhs.utf8
    if lhs.bytes.count != utf8.count {
        return false
    }
    for (i, c) in utf8.enumerated() {
        if lhs.bytes[i] != c {
            return false
        }
    }
    return true
}

/// ByteStreamable conformance for a ByteString.
extension ByteString: ByteStreamable {
    public func writeTo(_ stream: OutputByteStream) {
        stream.write(_bytes)
    }
}

/// StringLiteralConvertible 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)
    }
}

/// Serialization
extension ByteString: Serializable {
    public func serialize<T: Serializer>(to serializer: T) {
        serializer.serialize(_bytes)
    }

    public init(from deserializer: any Deserializer) throws {
        self._bytes = try deserializer.deserialize()
    }
}

extension ByteString: DataProtocol, Collection {
    public typealias StringData = Array<UInt8>
    public typealias Regions = StringData.Regions
    public typealias Element = StringData.Element
    public typealias Index = StringData.Index
    public typealias SubSequence = StringData.SubSequence
    public typealias Indices = StringData.Indices

    public subscript(position: Self.Index) -> Self.Element {
        _bytes[position]
    }

    public subscript(bounds: Range<Self.Index>) -> Self.SubSequence {
        _bytes[bounds]
    }

    public var regions: StringData.Regions {
        _bytes.regions
    }

    public var startIndex: StringData.Index {
        _bytes.startIndex
    }

    public var endIndex: StringData.Index {
        _bytes.endIndex
    }
}

/// Codable conformance for a ByteString.
extension ByteString: Codable {
    public func encode(to encoder: any Swift.Encoder) throws {
        try Data(_bytes).encode(to: encoder)
    }

    public init(from data: Data) throws {
        if let str = String(data: data, encoding: .utf8) {
            self.init(encodingAsUTF8: str)
        } else {
            throw StubError.error("unable to convert data to string")
        }
    }

    public init(from decoder: any Swift.Decoder) throws {
        try self.init(encodingAsUTF8: String(from: decoder))
    }
}