diff --git a/wire-runtime-swift/src/main/swift/ProtoCodable/ProtoWriter.swift b/wire-runtime-swift/src/main/swift/ProtoCodable/ProtoWriter.swift index 48be635151..be19e507c5 100644 --- a/wire-runtime-swift/src/main/swift/ProtoCodable/ProtoWriter.swift +++ b/wire-runtime-swift/src/main/swift/ProtoCodable/ProtoWriter.swift @@ -708,13 +708,13 @@ public final class ProtoWriter { } if let isProto3 = isProto3 { - messageStackIndex += 1 - if messageStackIndex >= messageStackCapacity { + if messageStackIndex + 1 >= messageStackCapacity { expandMessageStack() } let frame = MessageFrame( isProto3: isProto3 ) + messageStackIndex += 1 messageStack.advanced(by: messageStackIndex).initialize(to: frame) // Cache this value as an ivar for quick access. diff --git a/wire-runtime-swift/src/test/proto/nested4.proto b/wire-runtime-swift/src/test/proto/nested4.proto index e4eda1bd73..f6843e4c1d 100644 --- a/wire-runtime-swift/src/test/proto/nested4.proto +++ b/wire-runtime-swift/src/test/proto/nested4.proto @@ -14,8 +14,11 @@ * limitations under the License. */ syntax = "proto2"; +import "nested5.proto"; message Nested4 { required string name = 1; + + optional Nested5 nested = 2; } diff --git a/wire-runtime-swift/src/test/proto/nested5.proto b/wire-runtime-swift/src/test/proto/nested5.proto new file mode 100644 index 0000000000..aa64d1925f --- /dev/null +++ b/wire-runtime-swift/src/test/proto/nested5.proto @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto2"; + +import "nested6.proto"; + +message Nested5 { + required string name = 1; + + optional Nested6 nested = 2; +} + diff --git a/wire-runtime-swift/src/test/proto/nested6.proto b/wire-runtime-swift/src/test/proto/nested6.proto new file mode 100644 index 0000000000..6a8fa56bf4 --- /dev/null +++ b/wire-runtime-swift/src/test/proto/nested6.proto @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Square Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +syntax = "proto2"; + +message Nested6 { + required string name = 1; +} + diff --git a/wire-runtime-swift/src/test/swift/ProtoWriterTests.swift b/wire-runtime-swift/src/test/swift/ProtoWriterTests.swift index 403392e457..b776c2124f 100644 --- a/wire-runtime-swift/src/test/swift/ProtoWriterTests.swift +++ b/wire-runtime-swift/src/test/swift/ProtoWriterTests.swift @@ -255,6 +255,56 @@ final class ProtoWriterTests: XCTestCase { """) } + func testEncodeMessageWithStackExpansion() throws { + let writer = ProtoWriter() + let message = Nested1(name: "name1") { v1 in + v1.nested = Nested2(name: "name2") { v2 in + v2.nested = Nested3(name: "name3") { v3 in + v3.nested = Nested4(name: "name4") { v4 in + v4.nested = Nested5(name: "name5") { v5 in + v5.nested = Nested6(name: "name6") + } + } + } + } + } + + try writer.encode(tag: 1, value: message) + + assertBufferEqual(writer, """ + 0A // (Tag 1 | Length Delimited) + 34 // Length 52 for Nested1 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name1 + 6E616D6531 // UTF-8 Value "name1" + 12 // (Tag 2 | Length Delimited) + 2B // Length 43 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name2 + 6E616D6532 // UTF-8 Value "name2" + 12 // (Tag 2 | Length Delimited) + 22 // Length 34 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name3 + 6E616D6533 // UTF-8 Value "name3" + 12 // (Tag 2 | Length Delimited) + 19 // Length 25 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name4 + 6E616D6534 // UTF-8 Value "name4" + 12 // (Tag 2 | Length Delimited) + 10 // Length 16 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name5 + 6E616D6535 // UTF-8 Value "name5" + 12 // (Tag 2 | Length Delimited) + 07 // Length 7 + 0A // (Tag 1 | Length Delimited) + 05 // Length 5 for name6 + 6E616D6536 // UTF-8 Value "name6" + """) + } + func testEncodeString() throws { let writer = ProtoWriter() try writer.encode(tag: 1, value: "foo")