Index: third_party/protobuf/objectivec/Tests/GPBCodedOuputStreamTests.m |
diff --git a/third_party/protobuf/objectivec/Tests/GPBCodedOuputStreamTests.m b/third_party/protobuf/objectivec/Tests/GPBCodedOuputStreamTests.m |
index 0723b645f75c2c79733dee19fbb95a3b43d42a5f..2ad326beb96a722d4e998fd51c7f0d273ba9869d 100644 |
--- a/third_party/protobuf/objectivec/Tests/GPBCodedOuputStreamTests.m |
+++ b/third_party/protobuf/objectivec/Tests/GPBCodedOuputStreamTests.m |
@@ -193,6 +193,32 @@ |
} |
} |
+- (void)assertWriteStringNoTag:(NSData*)data |
+ value:(NSString *)value |
+ context:(NSString *)contextMessage { |
+ NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory]; |
+ GPBCodedOutputStream* output = |
+ [GPBCodedOutputStream streamWithOutputStream:rawOutput]; |
+ [output writeStringNoTag:value]; |
+ [output flush]; |
+ |
+ NSData* actual = |
+ [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; |
+ XCTAssertEqualObjects(data, actual, @"%@", contextMessage); |
+ |
+ // Try different block sizes. |
+ for (int blockSize = 1; blockSize <= 16; blockSize *= 2) { |
+ rawOutput = [NSOutputStream outputStreamToMemory]; |
+ output = [GPBCodedOutputStream streamWithOutputStream:rawOutput |
+ bufferSize:blockSize]; |
+ [output writeStringNoTag:value]; |
+ [output flush]; |
+ |
+ actual = [rawOutput propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; |
+ XCTAssertEqualObjects(data, actual, @"%@", contextMessage); |
+ } |
+} |
+ |
- (void)testWriteVarint1 { |
[self assertWriteVarint:bytes(0x00) value:0]; |
} |
@@ -337,4 +363,64 @@ |
XCTAssertEqualObjects(rawBytes, goldenData); |
} |
+- (void)testCFStringGetCStringPtrAndStringsWithNullChars { |
+ // This test exists to verify that CFStrings with embedded NULLs still expose |
+ // their raw buffer if they are backed by UTF8 storage. If this fails, the |
+ // quick/direct access paths in GPBCodedOutputStream that depend on |
+ // CFStringGetCStringPtr need to be re-evalutated (maybe just removed). |
+ // And yes, we do get NULLs in strings from some servers. |
+ |
+ char zeroTest[] = "\0Test\0String"; |
+ // Note: there is a \0 at the end of this since it is a c-string. |
+ NSString *asNSString = [[NSString alloc] initWithBytes:zeroTest |
+ length:sizeof(zeroTest) |
+ encoding:NSUTF8StringEncoding]; |
+ const char *cString = |
+ CFStringGetCStringPtr((CFStringRef)asNSString, kCFStringEncodingUTF8); |
+ XCTAssertTrue(cString != NULL); |
+ // Again, if the above assert fails, then it means NSString no longer exposes |
+ // the raw utf8 storage of a string created from utf8 input, so the code using |
+ // CFStringGetCStringPtr in GPBCodedOutputStream will still work (it will take |
+ // a different code path); but the optimizations for when |
+ // CFStringGetCStringPtr does work could possibly go away. |
+ |
+ XCTAssertEqual(sizeof(zeroTest), |
+ [asNSString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]); |
+ XCTAssertTrue(0 == memcmp(cString, zeroTest, sizeof(zeroTest))); |
+ [asNSString release]; |
+} |
+ |
+- (void)testWriteStringsWithZeroChar { |
+ // Unicode allows `\0` as a character, and NSString is a class cluster, so |
+ // there are a few different classes that could end up beind a given string. |
+ // Historically, we've seen differences based on constant strings in code and |
+ // strings built via the NSString apis. So this round trips them to ensure |
+ // they are acting as expected. |
+ |
+ NSArray<NSString *> *strs = @[ |
+ @"\0at start", |
+ @"in\0middle", |
+ @"at end\0", |
+ ]; |
+ int i = 0; |
+ for (NSString *str in strs) { |
+ NSData *asUTF8 = [str dataUsingEncoding:NSUTF8StringEncoding]; |
+ NSMutableData *expected = [NSMutableData data]; |
+ uint8_t lengthByte = (uint8_t)asUTF8.length; |
+ [expected appendBytes:&lengthByte length:1]; |
+ [expected appendData:asUTF8]; |
+ |
+ NSString *context = [NSString stringWithFormat:@"Loop %d - Literal", i]; |
+ [self assertWriteStringNoTag:expected value:str context:context]; |
+ |
+ // Force a new string to be built which gets a different class from the |
+ // NSString class cluster than the literal did. |
+ NSString *str2 = [NSString stringWithFormat:@"%@", str]; |
+ context = [NSString stringWithFormat:@"Loop %d - Built", i]; |
+ [self assertWriteStringNoTag:expected value:str2 context:context]; |
+ |
+ ++i; |
+ } |
+} |
+ |
@end |