Skip to content

Commit f091154

Browse files
authored
Make 'GRPCStatus' a struct (#989)
Motivation: `GRPCStatus` is a `class` because it was too wide to fit within the inline storage of an existential container and would incur unnecessary heap allocations as a result. In #942 the implementation of the `GRPCStatus.Code` stored by the status was changed from an `enum` to a `struct`. As a result, the width of `GRPCStatus` (as a `struct`) is now 24 and may therefore be stored inline in an existential container. Unfortunately, this now makes `_GRPCClientResponsePart` too wide! This can be addressed by further narrowing the `Code` stored by the status. Modifications: - Narrow `GRPCStatus.Code` by storing its `rawValue` internally as a `UInt8` - Make `GRPCStatus` a `struct` instead of a `class` (note: we already have a test in place to validate it may be stored inline in an existential container). Result: - `GRPCStatus` only allocates when the `message` isn't `nil`
1 parent 7aac780 commit f091154

File tree

1 file changed

+33
-32
lines changed

1 file changed

+33
-32
lines changed

Sources/GRPC/GRPCStatus.swift

Lines changed: 33 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,12 @@ import NIOHTTP1
1919
import NIOHTTP2
2020

2121
/// Encapsulates the result of a gRPC call.
22-
///
23-
/// We use a `class` here for a couple of reasons:
24-
/// - The size of the equivalent `struct` is larger than the value buffer in an existential
25-
/// container so would incur a heap allocation each time a `GRPCStatus` is passed to a function
26-
/// taking an `Error`.
27-
/// - We aren't using value semantics (since all properties are constant).
28-
public final class GRPCStatus: Error {
29-
/// The status code of the RPC.
30-
public let code: Code
22+
public struct GRPCStatus: Error {
3123
/// The status message of the RPC.
32-
public let message: String?
24+
public var message: String?
25+
26+
/// The status code of the RPC.
27+
public var code: Code
3328

3429
/// Whether the status is '.ok'.
3530
public var isOk: Bool {
@@ -75,19 +70,25 @@ extension GRPCStatus {
7570
/// Status codes for gRPC operations (replicated from `status_code_enum.h` in the
7671
/// [gRPC core library](https://github.com/grpc/grpc)).
7772
public struct Code: Hashable, CustomStringConvertible {
78-
public let rawValue: Int
73+
// `rawValue` must be an `Int` for API reasons and we don't need (or want) to store anything so
74+
// wide, a `UInt8` is fine.
75+
private let _rawValue: UInt8
76+
77+
public var rawValue: Int {
78+
return Int(self._rawValue)
79+
}
7980

8081
public init?(rawValue: Int) {
8182
switch rawValue {
8283
case 0 ... 16:
83-
self.rawValue = rawValue
84+
self._rawValue = UInt8(truncatingIfNeeded: rawValue)
8485
default:
8586
return nil
8687
}
8788
}
8889

89-
private init(_ code: Int) {
90-
self.rawValue = code
90+
private init(_ code: UInt8) {
91+
self._rawValue = code
9192
}
9293

9394
/// Not an error; returned on success.
@@ -199,41 +200,41 @@ extension GRPCStatus {
199200
public var description: String {
200201
switch self {
201202
case .ok:
202-
return "ok (\(self.rawValue))"
203+
return "ok (\(self._rawValue))"
203204
case .cancelled:
204-
return "cancelled (\(self.rawValue))"
205+
return "cancelled (\(self._rawValue))"
205206
case .unknown:
206-
return "unknown (\(self.rawValue))"
207+
return "unknown (\(self._rawValue))"
207208
case .invalidArgument:
208-
return "invalid argument (\(self.rawValue))"
209+
return "invalid argument (\(self._rawValue))"
209210
case .deadlineExceeded:
210-
return "deadline exceeded (\(self.rawValue))"
211+
return "deadline exceeded (\(self._rawValue))"
211212
case .notFound:
212-
return "not found (\(self.rawValue))"
213+
return "not found (\(self._rawValue))"
213214
case .alreadyExists:
214-
return "already exists (\(self.rawValue))"
215+
return "already exists (\(self._rawValue))"
215216
case .permissionDenied:
216-
return "permission denied (\(self.rawValue))"
217+
return "permission denied (\(self._rawValue))"
217218
case .resourceExhausted:
218-
return "resource exhausted (\(self.rawValue))"
219+
return "resource exhausted (\(self._rawValue))"
219220
case .failedPrecondition:
220-
return "failed precondition (\(self.rawValue))"
221+
return "failed precondition (\(self._rawValue))"
221222
case .aborted:
222-
return "aborted (\(self.rawValue))"
223+
return "aborted (\(self._rawValue))"
223224
case .outOfRange:
224-
return "out of range (\(self.rawValue))"
225+
return "out of range (\(self._rawValue))"
225226
case .unimplemented:
226-
return "unimplemented (\(self.rawValue))"
227+
return "unimplemented (\(self._rawValue))"
227228
case .internalError:
228-
return "internal error (\(self.rawValue))"
229+
return "internal error (\(self._rawValue))"
229230
case .unavailable:
230-
return "unavailable (\(self.rawValue))"
231+
return "unavailable (\(self._rawValue))"
231232
case .dataLoss:
232-
return "data loss (\(self.rawValue))"
233+
return "data loss (\(self._rawValue))"
233234
case .unauthenticated:
234-
return "unauthenticated (\(self.rawValue))"
235+
return "unauthenticated (\(self._rawValue))"
235236
default:
236-
return String(describing: self.rawValue)
237+
return String(describing: self._rawValue)
237238
}
238239
}
239240
}

0 commit comments

Comments
 (0)