Skip to content

Commit f2f13ef

Browse files
authored
Add GRPCStreamStateMachine (#1787)
1 parent 53e2739 commit f2f13ef

File tree

8 files changed

+4097
-20
lines changed

8 files changed

+4097
-20
lines changed

Sources/GRPCCore/Internal/Base64.swift

Lines changed: 147 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@
2626
met:
2727

2828
1. Redistributions of source code must retain the above copyright
29-
notice, this list of conditions and the following disclaimer.
29+
notice, this list of conditions and the following disclaimer.
3030

3131
2. Redistributions in binary form must reproduce the above copyright
32-
notice, this list of conditions and the following disclaimer in the
33-
documentation and/or other materials provided with the distribution.
32+
notice, this list of conditions and the following disclaimer in the
33+
documentation and/or other materials provided with the distribution.
3434

3535
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
3636
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -70,25 +70,59 @@
7070
SOFTWARE.
7171
*/
7272

73-
internal enum Base64 {}
74-
75-
extension Base64 {
76-
internal struct DecodingOptions: OptionSet {
73+
// swift-format-ignore: DontRepeatTypeInStaticProperties
74+
enum Base64 {
75+
struct DecodingOptions: OptionSet {
7776
internal let rawValue: UInt
7877
internal init(rawValue: UInt) { self.rawValue = rawValue }
7978

8079
internal static let base64UrlAlphabet = DecodingOptions(rawValue: UInt(1 << 0))
8180
internal static let omitPaddingCharacter = DecodingOptions(rawValue: UInt(1 << 1))
8281
}
8382

84-
internal enum DecodingError: Error, Equatable {
83+
enum DecodingError: Error, Equatable {
8584
case invalidLength
8685
case invalidCharacter(UInt8)
8786
case unexpectedPaddingCharacter
8887
case unexpectedEnd
8988
}
9089

91-
internal static func decode(
90+
static func encode<Buffer: Collection>(bytes: Buffer) -> String where Buffer.Element == UInt8 {
91+
guard !bytes.isEmpty else {
92+
return ""
93+
}
94+
95+
// In Base64, 3 bytes become 4 output characters, and we pad to the
96+
// nearest multiple of four.
97+
let base64StringLength = ((bytes.count + 2) / 3) * 4
98+
let alphabet = Base64.encodeBase64
99+
100+
return String(customUnsafeUninitializedCapacity: base64StringLength) { backingStorage in
101+
var input = bytes.makeIterator()
102+
var offset = 0
103+
while let firstByte = input.next() {
104+
let secondByte = input.next()
105+
let thirdByte = input.next()
106+
107+
backingStorage[offset] = Base64.encode(alphabet: alphabet, firstByte: firstByte)
108+
backingStorage[offset + 1] = Base64.encode(
109+
alphabet: alphabet,
110+
firstByte: firstByte,
111+
secondByte: secondByte
112+
)
113+
backingStorage[offset + 2] = Base64.encode(
114+
alphabet: alphabet,
115+
secondByte: secondByte,
116+
thirdByte: thirdByte
117+
)
118+
backingStorage[offset + 3] = Base64.encode(alphabet: alphabet, thirdByte: thirdByte)
119+
offset += 4
120+
}
121+
return offset
122+
}
123+
}
124+
125+
static func decode(
92126
string encoded: String,
93127
options: DecodingOptions = []
94128
) throws -> [UInt8] {
@@ -204,7 +238,7 @@ extension Base64 {
204238
}
205239
}
206240

207-
static func withUnsafeDecodingTablesAsBufferPointers<R>(
241+
private static func withUnsafeDecodingTablesAsBufferPointers<R>(
208242
options: Base64.DecodingOptions,
209243
_ body: (
210244
UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>, UnsafeBufferPointer<UInt32>,
@@ -232,10 +266,64 @@ extension Base64 {
232266
}
233267
}
234268

235-
internal static let encodePaddingCharacter: UInt8 = 61
236-
static let badCharacter: UInt32 = 0x01FF_FFFF
269+
private static let encodePaddingCharacter: UInt8 = 61
270+
271+
private static let encodeBase64: [UInt8] = [
272+
UInt8(ascii: "A"), UInt8(ascii: "B"), UInt8(ascii: "C"), UInt8(ascii: "D"),
273+
UInt8(ascii: "E"), UInt8(ascii: "F"), UInt8(ascii: "G"), UInt8(ascii: "H"),
274+
UInt8(ascii: "I"), UInt8(ascii: "J"), UInt8(ascii: "K"), UInt8(ascii: "L"),
275+
UInt8(ascii: "M"), UInt8(ascii: "N"), UInt8(ascii: "O"), UInt8(ascii: "P"),
276+
UInt8(ascii: "Q"), UInt8(ascii: "R"), UInt8(ascii: "S"), UInt8(ascii: "T"),
277+
UInt8(ascii: "U"), UInt8(ascii: "V"), UInt8(ascii: "W"), UInt8(ascii: "X"),
278+
UInt8(ascii: "Y"), UInt8(ascii: "Z"), UInt8(ascii: "a"), UInt8(ascii: "b"),
279+
UInt8(ascii: "c"), UInt8(ascii: "d"), UInt8(ascii: "e"), UInt8(ascii: "f"),
280+
UInt8(ascii: "g"), UInt8(ascii: "h"), UInt8(ascii: "i"), UInt8(ascii: "j"),
281+
UInt8(ascii: "k"), UInt8(ascii: "l"), UInt8(ascii: "m"), UInt8(ascii: "n"),
282+
UInt8(ascii: "o"), UInt8(ascii: "p"), UInt8(ascii: "q"), UInt8(ascii: "r"),
283+
UInt8(ascii: "s"), UInt8(ascii: "t"), UInt8(ascii: "u"), UInt8(ascii: "v"),
284+
UInt8(ascii: "w"), UInt8(ascii: "x"), UInt8(ascii: "y"), UInt8(ascii: "z"),
285+
UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"), UInt8(ascii: "3"),
286+
UInt8(ascii: "4"), UInt8(ascii: "5"), UInt8(ascii: "6"), UInt8(ascii: "7"),
287+
UInt8(ascii: "8"), UInt8(ascii: "9"), UInt8(ascii: "+"), UInt8(ascii: "/"),
288+
]
289+
290+
private static func encode(alphabet: [UInt8], firstByte: UInt8) -> UInt8 {
291+
let index = firstByte >> 2
292+
return alphabet[Int(index)]
293+
}
294+
295+
private static func encode(alphabet: [UInt8], firstByte: UInt8, secondByte: UInt8?) -> UInt8 {
296+
var index = (firstByte & 0b00000011) << 4
297+
if let secondByte = secondByte {
298+
index += (secondByte & 0b11110000) >> 4
299+
}
300+
return alphabet[Int(index)]
301+
}
237302

238-
static let decoding0: [UInt32] = [
303+
private static func encode(alphabet: [UInt8], secondByte: UInt8?, thirdByte: UInt8?) -> UInt8 {
304+
guard let secondByte = secondByte else {
305+
// No second byte means we are just emitting padding.
306+
return Base64.encodePaddingCharacter
307+
}
308+
var index = (secondByte & 0b00001111) << 2
309+
if let thirdByte = thirdByte {
310+
index += (thirdByte & 0b11000000) >> 6
311+
}
312+
return alphabet[Int(index)]
313+
}
314+
315+
private static func encode(alphabet: [UInt8], thirdByte: UInt8?) -> UInt8 {
316+
guard let thirdByte = thirdByte else {
317+
// No third byte means just padding.
318+
return Base64.encodePaddingCharacter
319+
}
320+
let index = thirdByte & 0b00111111
321+
return alphabet[Int(index)]
322+
}
323+
324+
private static let badCharacter: UInt32 = 0x01FF_FFFF
325+
326+
private static let decoding0: [UInt32] = [
239327
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
240328
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
241329
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
@@ -281,7 +369,7 @@ extension Base64 {
281369
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
282370
]
283371

284-
static let decoding1: [UInt32] = [
372+
private static let decoding1: [UInt32] = [
285373
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
286374
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
287375
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
@@ -327,7 +415,7 @@ extension Base64 {
327415
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
328416
]
329417

330-
static let decoding2: [UInt32] = [
418+
private static let decoding2: [UInt32] = [
331419
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
332420
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
333421
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
@@ -373,7 +461,7 @@ extension Base64 {
373461
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
374462
]
375463

376-
static let decoding3: [UInt32] = [
464+
private static let decoding3: [UInt32] = [
377465
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
378466
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
379467
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
@@ -419,7 +507,7 @@ extension Base64 {
419507
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
420508
]
421509

422-
static let decoding0url: [UInt32] = [
510+
private static let decoding0url: [UInt32] = [
423511
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0
424512
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6
425513
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12
@@ -465,7 +553,7 @@ extension Base64 {
465553
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
466554
]
467555

468-
static let decoding1url: [UInt32] = [
556+
private static let decoding1url: [UInt32] = [
469557
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0
470558
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6
471559
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12
@@ -511,7 +599,7 @@ extension Base64 {
511599
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
512600
]
513601

514-
static let decoding2url: [UInt32] = [
602+
private static let decoding2url: [UInt32] = [
515603
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0
516604
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6
517605
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12
@@ -557,7 +645,7 @@ extension Base64 {
557645
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
558646
]
559647

560-
static let decoding3url: [UInt32] = [
648+
private static let decoding3url: [UInt32] = [
561649
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 0
562650
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 6
563651
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, // 12
@@ -603,3 +691,42 @@ extension Base64 {
603691
0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF, 0x01FF_FFFF,
604692
]
605693
}
694+
695+
extension String {
696+
/// This is a backport of a proposed String initializer that will allow writing directly into an uninitialized String's backing memory.
697+
///
698+
/// As this API does not exist prior to 5.3 on Linux, or on older Apple platforms, we fake it out with a pointer and accept the extra copy.
699+
init(
700+
backportUnsafeUninitializedCapacity capacity: Int,
701+
initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer<UInt8>) throws -> Int
702+
) rethrows {
703+
704+
// The buffer will store zero terminated C string
705+
let buffer = UnsafeMutableBufferPointer<UInt8>.allocate(capacity: capacity + 1)
706+
defer {
707+
buffer.deallocate()
708+
}
709+
710+
let initializedCount = try initializer(buffer)
711+
precondition(initializedCount <= capacity, "Overran buffer in initializer!")
712+
713+
// add zero termination
714+
buffer[initializedCount] = 0
715+
716+
self = String(cString: buffer.baseAddress!)
717+
}
718+
719+
init(
720+
customUnsafeUninitializedCapacity capacity: Int,
721+
initializingUTF8With initializer: (_ buffer: UnsafeMutableBufferPointer<UInt8>) throws -> Int
722+
) rethrows {
723+
if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) {
724+
try self.init(unsafeUninitializedCapacity: capacity, initializingUTF8With: initializer)
725+
} else {
726+
try self.init(
727+
backportUnsafeUninitializedCapacity: capacity,
728+
initializingUTF8With: initializer
729+
)
730+
}
731+
}
732+
}

Sources/GRPCCore/Metadata.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ public struct Metadata: Sendable, Hashable {
8585
public enum Value: Sendable, Hashable {
8686
case string(String)
8787
case binary([UInt8])
88+
89+
/// The value as a String. If it was originally stored as a binary, the base64-encoded String version
90+
/// of the binary data will be returned instead.
91+
public func encoded() -> String {
92+
switch self {
93+
case .string(let string):
94+
return string
95+
case .binary(let bytes):
96+
return Base64.encode(bytes: bytes)
97+
}
98+
}
8899
}
89100

90101
/// A metadata key-value pair.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
/// Supported message compression algorithms.
18+
///
19+
/// These algorithms are indicated in the "grpc-encoding" header. As such, a lack of "grpc-encoding"
20+
/// header indicates that there is no message compression.
21+
public struct CompressionAlgorithm: Hashable, Sendable {
22+
/// Identity compression; "no" compression but indicated via the "grpc-encoding" header.
23+
public static let identity = CompressionAlgorithm(.identity)
24+
public static let deflate = CompressionAlgorithm(.deflate)
25+
public static let gzip = CompressionAlgorithm(.gzip)
26+
27+
// The order here is important: most compression to least.
28+
public static let all: [CompressionAlgorithm] = [.gzip, .deflate, .identity]
29+
30+
public var name: String {
31+
return self.algorithm.rawValue
32+
}
33+
34+
internal enum Algorithm: String {
35+
case identity
36+
case deflate
37+
case gzip
38+
}
39+
40+
internal let algorithm: Algorithm
41+
42+
private init(_ algorithm: Algorithm) {
43+
self.algorithm = algorithm
44+
}
45+
46+
internal init?(rawValue: String) {
47+
guard let algorithm = Algorithm(rawValue: rawValue) else {
48+
return nil
49+
}
50+
self.algorithm = algorithm
51+
}
52+
}

0 commit comments

Comments
 (0)