Skip to content

Instantly share code, notes, and snippets.

@willtemperley
Created June 26, 2023 17:58
Show Gist options
  • Save willtemperley/ed780f3977b70064f6f878e4a9353c4d to your computer and use it in GitHub Desktop.
Save willtemperley/ed780f3977b70064f6f878e4a9353c4d to your computer and use it in GitHub Desktop.
Demonstrates Issue 1410 in Swift Protobuf
//
// SwiftProtobufIssue1410Tests.swift
//
// Created by Will Temperley on 26/06/2023.
//
import Foundation
import XCTest
import SwiftProtobuf
class ParseBinaryDelimitedProtobufFileTest: XCTestCase {
/**
Creates a URL for a temporary file on disk. Registers a teardown block to
delete a file at that URL (if one exists) during test teardown.
*/
func temporaryFileURL() -> URL {
let directory = NSTemporaryDirectory()
let filename = UUID().uuidString
let fileURL = URL(fileURLWithPath: directory).appendingPathComponent(filename)
addTeardownBlock {
do {
let fileManager = FileManager.default
if fileManager.fileExists(atPath: fileURL.path) {
try fileManager.removeItem(at: fileURL)
XCTAssertFalse(fileManager.fileExists(atPath: fileURL.path))
}
} catch {
XCTFail("Error while deleting temporary file: \(error)")
}
}
return fileURL
}
func writeTestProtobuf(_ fileURL: URL) throws {
let outputStream = OutputStream(url: fileURL, append: false)!
outputStream.open()
for index in 1...1000 {
var message = SearchRequest()
message.pageNumber = Int64(index)
message.resultsPerPage = 100
try BinaryDelimited.serialize(message: message, to: outputStream)
}
outputStream.close()
}
//Reading directly from a file fails with a truncated error
func testReadDirectFromFile() throws {
let fileURL = temporaryFileURL()
try writeTestProtobuf(fileURL)
let inputStream = InputStream(url: fileURL)!
inputStream.open()
var messages = [SearchRequest]()
while (inputStream.hasBytesAvailable) {
let message = try BinaryDelimited.parse(messageType: SearchRequest.self, from: inputStream)
messages.append(message)
}
inputStream.close()
assert(messages.count == 1000)
}
//Reading the file contents into a data object first runs fine
func testReadFileContentsToData() throws {
let fileURL = temporaryFileURL()
try writeTestProtobuf(fileURL)
let data = try Data(contentsOf: fileURL)
let inputStream = InputStream(data: data)
inputStream.open()
var messages = [SearchRequest]()
while (inputStream.hasBytesAvailable) {
let message = try BinaryDelimited.parse(messageType: SearchRequest.self, from: inputStream)
messages.append(message)
}
inputStream.close()
assert(messages.count == 1000)
}
}
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
struct SearchRequest {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var query: String = String()
var pageNumber: Int64 = 0
var resultsPerPage: Int64 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
#if swift(>=5.5) && canImport(_Concurrency)
extension SearchRequest: @unchecked Sendable {}
#endif // swift(>=5.5) && canImport(_Concurrency)
// MARK: - Code below here is support for the SwiftProtobuf runtime.
extension SearchRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = "SearchRequest"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "query"),
2: .standard(proto: "page_number"),
3: .standard(proto: "results_per_page"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.query) }()
case 2: try { try decoder.decodeSingularInt64Field(value: &self.pageNumber) }()
case 3: try { try decoder.decodeSingularInt64Field(value: &self.resultsPerPage) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.query.isEmpty {
try visitor.visitSingularStringField(value: self.query, fieldNumber: 1)
}
if self.pageNumber != 0 {
try visitor.visitSingularInt64Field(value: self.pageNumber, fieldNumber: 2)
}
if self.resultsPerPage != 0 {
try visitor.visitSingularInt64Field(value: self.resultsPerPage, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: SearchRequest, rhs: SearchRequest) -> Bool {
if lhs.query != rhs.query {return false}
if lhs.pageNumber != rhs.pageNumber {return false}
if lhs.resultsPerPage != rhs.resultsPerPage {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment