-
Notifications
You must be signed in to change notification settings - Fork 570
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Swift: encode & decode size-delimited messages #2424
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -215,12 +215,43 @@ public final class ProtoEncoder { | |
|
||
let writer = ProtoWriter( | ||
data: .init(capacity: structSize), | ||
outputFormatting: [], | ||
outputFormatting: outputFormatting, | ||
rootMessageProtoSyntax: syntax | ||
) | ||
writer.outputFormatting = outputFormatting | ||
|
||
try encoder(writer) | ||
|
||
return Data(writer.buffer, copyBytes: false) | ||
} | ||
|
||
public func encodeSizeDelimited<T: ProtoEncodable>(_ values: [T]) throws -> Data { | ||
// Use the size of the struct as an initial estimate for the space needed. | ||
let structSize = MemoryLayout.size(ofValue: T.self) | ||
|
||
// Reserve space for the largest varint size | ||
let varintSize = 8 | ||
|
||
let fullBuffer = WriteBuffer(capacity: (structSize + varintSize) * values.count) | ||
|
||
for value in values { | ||
let writer = ProtoWriter( | ||
data: .init(), | ||
outputFormatting: outputFormatting, | ||
rootMessageProtoSyntax: T.self.protoSyntax ?? .proto2 | ||
) | ||
|
||
try value.encode(to: writer) | ||
|
||
if writer.buffer.count == 0 { | ||
continue | ||
} | ||
|
||
// write this value's size + contents to the main buffer | ||
fullBuffer.writeVarint(UInt64(writer.buffer.count), at: fullBuffer.count) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI: We have some tricks inside the writer to avoid this write-then-copy inside of the ProtoWriter (see Basically, most messages will be in the size range that requires (IIRC) two bytes of varint, so we reserve two bytes, then write out value into the full buffer. If it happens to not take two bytes for its size, only then do we go back and move it to add more or less room for the prefix size. In the future, we could extract this into an extension on
Again, all a good future enhancement. Not required now. |
||
fullBuffer.append(writer.buffer) | ||
} | ||
|
||
return Data(fullBuffer, copyBytes: false) | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,4 +61,20 @@ final class RoundTripTests: XCTestCase { | |
XCTAssertEqual(decodedEmpty, empty) | ||
} | ||
|
||
func testSizeDelimited() throws { | ||
let values = [ | ||
Person3(name: "John Doe", id: 123), | ||
Person3(name: "Jane Doe", id: 456) { | ||
$0.email = "[email protected]" | ||
} | ||
] | ||
|
||
let encoder = ProtoEncoder() | ||
let data = try encoder.encodeSizeDelimited(values) | ||
|
||
let decoder = ProtoDecoder() | ||
let decodedValues = try decoder.decodeSizeDelimited(Person3.self, from: data) | ||
|
||
XCTAssertEqual(decodedValues, values) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the same coding style as what we already have