Skip to content

Commit 178e102

Browse files
authored
Message encoder/decoder (#1664)
1 parent 6d8108c commit 178e102

File tree

3 files changed

+138
-0
lines changed

3 files changed

+138
-0
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2023, 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+
/// Serializes a message into a sequence of bytes.
18+
///
19+
/// Message serializers convert an input message to a sequence of bytes. Serializers are used to
20+
/// convert messages into a form which is suitable for sending over a network. The reverse
21+
/// operation, deserialization, is performed by a ``MessageDeserializer``.
22+
///
23+
/// Serializers are used frequently and implementations should take care to ensure that
24+
/// serialization is as cheap as possible.
25+
public protocol MessageSerializer<Message>: Sendable {
26+
/// The type of message this serializer can serialize.
27+
associatedtype Message
28+
29+
/// Serializes a ``Message`` into a sequence of bytes.
30+
///
31+
/// - Parameter message: The message to serialize.
32+
/// - Returns: The serialized bytes of a message.
33+
func serialize(_ message: Message) throws -> [UInt8]
34+
}
35+
36+
/// Deserializes a sequence of bytes into a message.
37+
///
38+
/// Message deserializers convert a sequence of bytes into a message. Deserializers are used to
39+
/// convert bytes received from the network into an application specific message. The reverse
40+
/// operation, serialization, is performed by a ``MessageSerializer``.
41+
///
42+
/// Deserializers are used frequently and implementations should take care to ensure that
43+
/// deserialization is as cheap as possible.
44+
public protocol MessageDeserializer<Message>: Sendable {
45+
/// The type of message this deserializer can deserialize.
46+
associatedtype Message
47+
48+
/// Deserializes a sequence of bytes into a ``Message``.
49+
///
50+
/// - Parameter serializedMessageBytes: The bytes to deserialize.
51+
/// - Returns: The deserialized message.
52+
func deserialize(_ serializedMessageBytes: [UInt8]) throws -> Message
53+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2023, 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+
import GRPCCore
17+
import XCTest
18+
19+
final class CodingTests: XCTestCase {
20+
func testJSONRoundtrip() throws {
21+
// This test just demonstrates that the API is suitable.
22+
23+
struct Message: Codable, Hashable {
24+
var foo: String
25+
var bar: Int
26+
var baz: Baz
27+
28+
struct Baz: Codable, Hashable {
29+
var bazzy: Double
30+
}
31+
}
32+
33+
let message = Message(foo: "foo", bar: 42, baz: .init(bazzy: 3.1415))
34+
35+
let serializer = JSONSerializer<Message>()
36+
let deserializer = JSONDeserializer<Message>()
37+
38+
let bytes = try serializer.serialize(message)
39+
let roundTrip = try deserializer.deserialize(bytes)
40+
XCTAssertEqual(roundTrip, message)
41+
}
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Copyright 2023, 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+
import GRPCCore
17+
18+
import struct Foundation.Data
19+
import class Foundation.JSONDecoder
20+
import class Foundation.JSONEncoder
21+
22+
private let jsonEncoder = JSONEncoder()
23+
private let jsonDecoder = JSONDecoder()
24+
25+
struct JSONSerializer<Message: Codable>: MessageSerializer {
26+
func serialize(_ message: Message) throws -> [UInt8] {
27+
do {
28+
return try Array(jsonEncoder.encode(message))
29+
} catch {
30+
throw RPCError(code: .internalError, message: "Can't serialize message to JSON. \(error)")
31+
}
32+
}
33+
}
34+
35+
struct JSONDeserializer<Message: Codable>: MessageDeserializer {
36+
func deserialize(_ serializedMessageBytes: [UInt8]) throws -> Message {
37+
do {
38+
return try jsonDecoder.decode(Message.self, from: Data(serializedMessageBytes))
39+
} catch {
40+
throw RPCError(code: .internalError, message: "Can't deserialze message from JSON. \(error)")
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)