Coverage Report

Created: 2026-10-23 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/grpc-swift/Sources/GRPC/Serialization.swift
Line
Count
Source
1
/*
2
 * Copyright 2020, 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
import NIOCore
18
import NIOFoundationCompat
19
import SwiftProtobuf
20
21
import struct Foundation.Data
22
23
public protocol MessageSerializer {
24
  associatedtype Input
25
26
  /// Serializes `input` into a `ByteBuffer` allocated using the provided `allocator`.
27
  ///
28
  /// - Parameters:
29
  ///   - input: The element to serialize.
30
  ///   - allocator: A `ByteBufferAllocator`.
31
  @inlinable
32
  func serialize(_ input: Input, allocator: ByteBufferAllocator) throws -> ByteBuffer
33
}
34
35
public protocol MessageDeserializer {
36
  associatedtype Output
37
38
  /// Deserializes `byteBuffer` to produce a single `Output`.
39
  ///
40
  /// - Parameter byteBuffer: The `ByteBuffer` to deserialize.
41
  @inlinable
42
  func deserialize(byteBuffer: ByteBuffer) throws -> Output
43
}
44
45
// MARK: Protobuf
46
47
public struct ProtobufSerializer<Message: SwiftProtobuf.Message>: MessageSerializer {
48
  @inlinable
49
593k
  public init() {}
50
51
  @inlinable
52
6.96M
  public func serialize(_ message: Message, allocator: ByteBufferAllocator) throws -> ByteBuffer {
53
6.96M
    // Serialize the message.
54
6.96M
    let serialized = try message.serializedData()
55
6.96M
56
6.96M
    // Allocate enough space and an extra 5 leading bytes. This a minor optimisation win: the length
57
6.96M
    // prefixed message writer can re-use the leading 5 bytes without needing to allocate a new
58
6.96M
    // buffer and copy over the serialized message.
59
6.96M
    var buffer = allocator.buffer(capacity: serialized.count + 5)
60
6.96M
    buffer.writeRepeatingByte(0, count: 5)
61
6.96M
    buffer.moveReaderIndex(forwardBy: 5)
62
6.96M
63
6.96M
    // Now write the serialized message.
64
6.96M
    buffer.writeContiguousBytes(serialized)
65
6.96M
66
6.96M
    return buffer
67
6.96M
  }
68
}
69
70
public struct ProtobufDeserializer<Message: SwiftProtobuf.Message>: MessageDeserializer {
71
  @inlinable
72
593k
  public init() {}
73
74
  @inlinable
75
2.43M
  public func deserialize(byteBuffer: ByteBuffer) throws -> Message {
76
2.43M
    var buffer = byteBuffer
77
2.43M
    // '!' is okay; we can always read 'readableBytes'.
78
2.43M
    let data = buffer.readData(length: buffer.readableBytes)!
79
2.43M
    return try Message(serializedBytes: data)
80
2.43M
  }
81
}
82
83
// MARK: GRPCPayload
84
85
public struct GRPCPayloadSerializer<Message: GRPCPayload>: MessageSerializer {
86
  @inlinable
87
0
  public init() {}
88
89
  @inlinable
90
0
  public func serialize(_ message: Message, allocator: ByteBufferAllocator) throws -> ByteBuffer {
91
0
    // Reserve 5 leading bytes. This a minor optimisation win: the length prefixed message writer
92
0
    // can re-use the leading 5 bytes without needing to allocate a new buffer and copy over the
93
0
    // serialized message.
94
0
    var buffer = allocator.buffer(repeating: 0, count: 5)
95
0
96
0
    let readerIndex = buffer.readerIndex
97
0
    let writerIndex = buffer.writerIndex
98
0
99
0
    // Serialize the payload into the buffer.
100
0
    try message.serialize(into: &buffer)
101
0
102
0
    // Ensure 'serialize(into:)' didn't do anything strange.
103
0
    assert(buffer.readerIndex == readerIndex, "serialize(into:) must not move the readerIndex")
104
0
    assert(
105
0
      buffer.writerIndex >= writerIndex,
106
0
      "serialize(into:) must not move the writerIndex backwards"
107
0
    )
108
0
    assert(
109
0
      buffer.getBytes(at: readerIndex, length: 5) == Array(repeating: 0, count: 5),
110
0
      "serialize(into:) must not write over existing written bytes"
111
0
    )
112
0
113
0
    // 'read' the first 5 bytes so that the buffer's readable bytes are only the bytes of the
114
0
    // serialized message.
115
0
    buffer.moveReaderIndex(forwardBy: 5)
116
0
117
0
    return buffer
118
0
  }
119
}
120
121
public struct GRPCPayloadDeserializer<Message: GRPCPayload>: MessageDeserializer {
122
  @inlinable
123
0
  public init() {}
124
125
  @inlinable
126
0
  public func deserialize(byteBuffer: ByteBuffer) throws -> Message {
127
0
    var buffer = byteBuffer
128
0
    return try Message(serializedByteBuffer: &buffer)
129
0
  }
130
}
131
132
// MARK: - Any Serializer/Deserializer
133
134
internal struct AnySerializer<Input>: MessageSerializer {
135
  private let _serialize: (Input, ByteBufferAllocator) throws -> ByteBuffer
136
137
0
  init<Serializer: MessageSerializer>(wrapping other: Serializer) where Serializer.Input == Input {
138
0
    self._serialize = other.serialize(_:allocator:)
Unexecuted instantiation: $s4GRPC13AnySerializerV8wrappingACyxGqd___tc5InputQyd__RszAA07MessageC0Rd__lufc7NIOCore10ByteBufferVx_AI0hI9AllocatorVtKcqd__cfu_
Unexecuted instantiation: $s4GRPC13AnySerializerV8wrappingACyxGqd___tc5InputQyd__RszAA07MessageC0Rd__lufc7NIOCore10ByteBufferVx_AI0hI9AllocatorVtKcqd__cfu_AKx_AMtKcfu0_
139
0
  }
140
141
0
  internal func serialize(_ input: Input, allocator: ByteBufferAllocator) throws -> ByteBuffer {
142
0
    return try self._serialize(input, allocator)
143
0
  }
144
}
145
146
internal struct AnyDeserializer<Output>: MessageDeserializer {
147
  private let _deserialize: (ByteBuffer) throws -> Output
148
149
  init<Deserializer: MessageDeserializer>(
150
    wrapping other: Deserializer
151
0
  ) where Deserializer.Output == Output {
152
0
    self._deserialize = other.deserialize(byteBuffer:)
Unexecuted instantiation: $s4GRPC15AnyDeserializerV8wrappingACyxGqd___tc6OutputQyd__RszAA07MessageC0Rd__lufcx7NIOCore10ByteBufferVKcqd__cfu_
Unexecuted instantiation: $s4GRPC15AnyDeserializerV8wrappingACyxGqd___tc6OutputQyd__RszAA07MessageC0Rd__lufcx7NIOCore10ByteBufferVKcqd__cfu_xAKKcfu0_
153
0
  }
154
155
0
  internal func deserialize(byteBuffer: ByteBuffer) throws -> Output {
156
0
    return try self._deserialize(byteBuffer)
157
0
  }
158
}