aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Thomas Van Lenten <thomasvl@google.com>2015-12-17 14:35:44 -0500
committerGravatar Thomas Van Lenten <thomasvl@google.com>2015-12-17 16:05:50 -0500
commitd6590d653415c0bfacf97e7f768dd3c994cb8d26 (patch)
tree8c0f6f98eb867d520e95d30d32a6d0e4fe55906c
parentafbc89a263d57bb2e36ce5f07b979c739bb2e8cd (diff)
Drop all use of OSSpinLock
Apple engineers have pointed out that OSSpinLocks are vulnerable to live locking on iOS in cases of priority inversion: . http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/ . https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html - Use a dispatch_semaphore_t within the extension registry. - Use a dispatch_semaphore_t for protecting autocreation within messages. - Drop the custom/internal GPBString class since we don't have really good numbers to judge the locking replacements and it isn't required. We can always bring it back with real data in the future.
-rw-r--r--Makefile.am1
-rw-r--r--objectivec/GPBCodedInputStream.m329
-rw-r--r--objectivec/GPBCodedInputStream_PackagePrivate.h17
-rw-r--r--objectivec/GPBMessage.m14
-rw-r--r--objectivec/GPBMessage_PackagePrivate.h7
-rw-r--r--objectivec/GPBRootObject.m28
-rw-r--r--objectivec/GPBUtilities.m4
-rw-r--r--objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj4
-rw-r--r--objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj4
-rw-r--r--objectivec/Tests/GPBCodedInputStreamTests.m6
-rw-r--r--objectivec/Tests/GPBStringTests.m513
11 files changed, 53 insertions, 874 deletions
diff --git a/Makefile.am b/Makefile.am
index bb735177..3eb46d7a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -468,7 +468,6 @@ objectivec_EXTRA_DIST= \
objectivec/Tests/GPBMessageTests.m \
objectivec/Tests/GPBObjectiveCPlusPlusTest.mm \
objectivec/Tests/GPBPerfTests.m \
- objectivec/Tests/GPBStringTests.m \
objectivec/Tests/GPBSwiftTests.swift \
objectivec/Tests/GPBTestUtilities.h \
objectivec/Tests/GPBTestUtilities.m \
diff --git a/objectivec/GPBCodedInputStream.m b/objectivec/GPBCodedInputStream.m
index 9371228a..fd877838 100644
--- a/objectivec/GPBCodedInputStream.m
+++ b/objectivec/GPBCodedInputStream.m
@@ -216,7 +216,17 @@ NSString *GPBCodedInputStreamReadRetainedString(
result = @"";
} else {
CheckSize(state, size);
- result = GPBCreateGPBStringWithUTF8(&state->bytes[state->bufferPos], size);
+ result = [[NSString alloc] initWithBytes:&state->bytes[state->bufferPos]
+ length:size
+ encoding:NSUTF8StringEncoding];
+ if (!result) {
+ result = @"";
+#ifdef DEBUG
+ // https://developers.google.com/protocol-buffers/docs/proto#scalar
+ NSLog(@"UTF8 failure, is some field type 'string' when it should be "
+ @"'bytes'?");
+#endif
+ }
state->bufferPos += size;
}
return result;
@@ -478,320 +488,3 @@ void GPBCodedInputStreamCheckLastTagWas(GPBCodedInputStreamState *state,
}
@end
-
-@implementation GPBString {
- @package
- CFStringRef string_;
- unsigned char *utf8_;
- NSUInteger utf8Len_;
-
- // This lock is used to gate access to utf8_. Once GPBStringInitStringValue()
- // has been called, string_ will be filled in, and utf8_ will be NULL.
- OSSpinLock lock_;
-
- BOOL hasBOM_;
- BOOL is7BitAscii_;
-}
-
-// Returns true if the passed in bytes are 7 bit ascii.
-// This routine needs to be fast.
-static bool AreBytesIn7BitASCII(const uint8_t *bytes, NSUInteger len) {
-// In the loops below, it's more efficient to collect rather than do
-// conditional at every step.
-#if __LP64__
- // Align bytes. This is especially important in case of 3 byte BOM.
- while (len > 0 && ((size_t)bytes & 0x07)) {
- if (*bytes++ & 0x80) return false;
- len--;
- }
- while (len >= 32) {
- uint64_t val = *(const uint64_t *)bytes;
- uint64_t hiBits = (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- hiBits |= (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- hiBits |= (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- if (hiBits | (val & 0x8080808080808080ULL)) return false;
- bytes += 8;
- len -= 32;
- }
-
- while (len >= 16) {
- uint64_t val = *(const uint64_t *)bytes;
- uint64_t hiBits = (val & 0x8080808080808080ULL);
- bytes += 8;
- val = *(const uint64_t *)bytes;
- if (hiBits | (val & 0x8080808080808080ULL)) return false;
- bytes += 8;
- len -= 16;
- }
-
- while (len >= 8) {
- uint64_t val = *(const uint64_t *)bytes;
- if (val & 0x8080808080808080ULL) return false;
- bytes += 8;
- len -= 8;
- }
-#else // __LP64__
- // Align bytes. This is especially important in case of 3 byte BOM.
- while (len > 0 && ((size_t)bytes & 0x03)) {
- if (*bytes++ & 0x80) return false;
- len--;
- }
- while (len >= 16) {
- uint32_t val = *(const uint32_t *)bytes;
- uint32_t hiBits = (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- hiBits |= (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- hiBits |= (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- if (hiBits | (val & 0x80808080U)) return false;
- bytes += 4;
- len -= 16;
- }
-
- while (len >= 8) {
- uint32_t val = *(const uint32_t *)bytes;
- uint32_t hiBits = (val & 0x80808080U);
- bytes += 4;
- val = *(const uint32_t *)bytes;
- if (hiBits | (val & 0x80808080U)) return false;
- bytes += 4;
- len -= 8;
- }
-#endif // __LP64__
-
- while (len >= 4) {
- uint32_t val = *(const uint32_t *)bytes;
- if (val & 0x80808080U) return false;
- bytes += 4;
- len -= 4;
- }
-
- while (len--) {
- if (*bytes++ & 0x80) return false;
- }
-
- return true;
-}
-
-static void GPBStringInitStringValue(GPBString *string) {
- OSSpinLockLock(&string->lock_);
- GPBStringInitStringValueAlreadyLocked(string);
- OSSpinLockUnlock(&string->lock_);
-}
-
-static void GPBStringInitStringValueAlreadyLocked(GPBString *string) {
- if (string->string_ == NULL && string->utf8_ != NULL) {
- // Using kCFAllocatorMalloc for contentsDeallocator, as buffer in
- // string->utf8_ is being handed off.
- string->string_ = CFStringCreateWithBytesNoCopy(
- NULL, string->utf8_, string->utf8Len_, kCFStringEncodingUTF8, false,
- kCFAllocatorMalloc);
- if (!string->string_) {
-#ifdef DEBUG
- // https://developers.google.com/protocol-buffers/docs/proto#scalar
- NSLog(@"UTF8 failure, is some field type 'string' when it should be "
- @"'bytes'?");
-#endif
- string->string_ = CFSTR("");
- string->utf8Len_ = 0;
- // On failure, we have to clean up the buffer.
- free(string->utf8_);
- }
- string->utf8_ = NULL;
- }
-}
-
-GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length) {
- GPBString *result = [[GPBString alloc] initWithBytes:bytes length:length];
- return result;
-}
-
-- (instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)length {
- self = [super init];
- if (self) {
- utf8_ = malloc(length);
- memcpy(utf8_, bytes, length);
- utf8Len_ = length;
- lock_ = OS_SPINLOCK_INIT;
- is7BitAscii_ = AreBytesIn7BitASCII(bytes, length);
- if (length >= 3 && memcmp(utf8_, "\xef\xbb\xbf", 3) == 0) {
- // We can't just remove the BOM from the string here, because in the case
- // where we have > 1 BOM at the beginning of the string, we will remove one,
- // and the internal NSString we create will remove the next one, and we will
- // end up with a GPBString != NSString issue.
- // We also just can't remove all the BOMs because then we would end up with
- // potential cases where a GPBString and an NSString made with the same
- // UTF8 buffer would in fact be different.
- // We record the fact we have a BOM, and use it as necessary to simulate
- // what NSString would return for various calls.
- hasBOM_ = YES;
-#if DEBUG
- // Sending BOMs across the line is just wasting bits.
- NSLog(@"Bad data? String should not have BOM!");
-#endif // DEBUG
- }
- }
- return self;
-}
-
-- (void)dealloc {
- if (string_ != NULL) {
- CFRelease(string_);
- }
- if (utf8_ != NULL) {
- free(utf8_);
- }
- [super dealloc];
-}
-
-// Required NSString overrides.
-- (NSUInteger)length {
- if (is7BitAscii_) {
- return utf8Len_;
- } else {
- GPBStringInitStringValue(self);
- return CFStringGetLength(string_);
- }
-}
-
-- (unichar)characterAtIndex:(NSUInteger)anIndex {
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- unichar result = utf8_[anIndex];
- OSSpinLockUnlock(&lock_);
- return result;
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- return CFStringGetCharacterAtIndex(string_, anIndex);
- }
-}
-
-// Override a couple of methods that typically want high performance.
-
-- (id)copyWithZone:(NSZone *)zone {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ copyWithZone:zone];
-}
-
-- (id)mutableCopyWithZone:(NSZone *)zone {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ mutableCopyWithZone:zone];
-}
-
-- (NSUInteger)hash {
- // Must convert to string here to make sure that the hash is always
- // consistent no matter what state the GPBString is in.
- GPBStringInitStringValue(self);
- return CFHash(string_);
-}
-
-- (BOOL)isEqual:(id)object {
- if (self == object) {
- return YES;
- }
- if ([object isKindOfClass:[NSString class]]) {
- GPBStringInitStringValue(self);
- return CFStringCompare(string_, (CFStringRef)object, 0) ==
- kCFCompareEqualTo;
- }
- return NO;
-}
-
-- (void)getCharacters:(unichar *)buffer range:(NSRange)aRange {
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- unsigned char *bytes = &(utf8_[aRange.location]);
- for (NSUInteger i = 0; i < aRange.length; ++i) {
- buffer[i] = bytes[i];
- }
- OSSpinLockUnlock(&lock_);
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- CFStringGetCharacters(string_, CFRangeMake(aRange.location, aRange.length),
- buffer);
- }
-}
-
-- (NSUInteger)lengthOfBytesUsingEncoding:(NSStringEncoding)encoding {
- if ((encoding == NSUTF8StringEncoding) ||
- (encoding == NSASCIIStringEncoding && is7BitAscii_)) {
- return utf8Len_ - (hasBOM_ ? 3 : 0);
- } else {
- GPBStringInitStringValue(self);
- return [(NSString *)string_ lengthOfBytesUsingEncoding:encoding];
- }
-}
-
-- (BOOL)getBytes:(void *)buffer
- maxLength:(NSUInteger)maxLength
- usedLength:(NSUInteger *)usedLength
- encoding:(NSStringEncoding)encoding
- options:(NSStringEncodingConversionOptions)options
- range:(NSRange)range
- remainingRange:(NSRangePointer)remainingRange {
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the maxLength argument is 0
- // (Radar 16385183). Therefore we have special cased it as a slow case so
- // that it behaves however Apple intends it to behave. It should be a rare
- // case.
- //
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the range is outside of the strings
- // length (Radar 16396177). Therefore we have special cased it as a slow
- // case so that it behaves however Apple intends it to behave. It should
- // be a rare case.
- //
- // We can optimize the UTF8StringEncoding and NSASCIIStringEncoding with no
- // options cases.
- if ((options == 0) &&
- (encoding == NSUTF8StringEncoding || encoding == NSASCIIStringEncoding) &&
- (maxLength != 0) &&
- (NSMaxRange(range) <= utf8Len_)) {
- // Might be able to optimize it.
- OSSpinLockLock(&lock_);
- if (is7BitAscii_ && utf8_) {
- NSUInteger length = range.length;
- length = (length < maxLength) ? length : maxLength;
- memcpy(buffer, utf8_ + range.location, length);
- if (usedLength) {
- *usedLength = length;
- }
- if (remainingRange) {
- remainingRange->location = range.location + length;
- remainingRange->length = range.length - length;
- }
- OSSpinLockUnlock(&lock_);
- if (length > 0) {
- return YES;
- } else {
- return NO;
- }
- } else {
- GPBStringInitStringValueAlreadyLocked(self);
- OSSpinLockUnlock(&lock_);
- }
- } else {
- GPBStringInitStringValue(self);
- }
- return [(NSString *)string_ getBytes:buffer
- maxLength:maxLength
- usedLength:usedLength
- encoding:encoding
- options:options
- range:range
- remainingRange:remainingRange];
-}
-
-@end
diff --git a/objectivec/GPBCodedInputStream_PackagePrivate.h b/objectivec/GPBCodedInputStream_PackagePrivate.h
index e482b4c9..90bd0c92 100644
--- a/objectivec/GPBCodedInputStream_PackagePrivate.h
+++ b/objectivec/GPBCodedInputStream_PackagePrivate.h
@@ -39,19 +39,6 @@
@class GPBUnknownFieldSet;
@class GPBFieldDescriptor;
-// GPBString is a string subclass that avoids the overhead of initializing
-// a full NSString until it is actually needed. Lots of protocol buffers contain
-// strings, and instantiating all of those strings and having them parsed to
-// verify correctness when the message was being read was expensive, when many
-// of the strings were never being used.
-//
-// Note for future-self. I tried implementing this using a NSProxy.
-// Turned out the performance was horrible in client apps because folks
-// like to use libraries like SBJSON that grab characters one at a time.
-// The proxy overhead was a killer.
-@interface GPBString : NSString
-@end
-
typedef struct GPBCodedInputStreamState {
const uint8_t *bytes;
size_t bufferSize;
@@ -92,10 +79,6 @@ typedef struct GPBCodedInputStreamState {
CF_EXTERN_C_BEGIN
-// Returns a GPBString with a +1 retain count.
-GPBString *GPBCreateGPBStringWithUTF8(const void *bytes, NSUInteger length)
- __attribute__((ns_returns_retained));
-
int32_t GPBCodedInputStreamReadTag(GPBCodedInputStreamState *state);
double GPBCodedInputStreamReadDouble(GPBCodedInputStreamState *state);
diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m
index d9080c3f..208cfe4e 100644
--- a/objectivec/GPBMessage.m
+++ b/objectivec/GPBMessage.m
@@ -568,13 +568,13 @@ static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
// Check again after getting the lock.
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
array = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!array) {
array = CreateArrayForField(field, self);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, array);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
}
return array;
}
@@ -598,13 +598,13 @@ static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
// Check again after getting the lock.
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!dict) {
dict = CreateMapForField(field, self);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, dict);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
}
return dict;
}
@@ -810,7 +810,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
messageStorage_ = (GPBMessage_StoragePtr)(
((uint8_t *)self) + class_getInstanceSize([self class]));
- readOnlyMutex_ = OS_SPINLOCK_INIT;
+ readOnlySemaphore_ = dispatch_semaphore_create(1);
}
return self;
@@ -1723,7 +1723,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
}
// Check for an autocreated value.
- OSSpinLockLock(&readOnlyMutex_);
+ dispatch_semaphore_wait(readOnlySemaphore_, DISPATCH_TIME_FOREVER);
value = [autocreatedExtensionMap_ objectForKey:extension];
if (!value) {
// Auto create the message extensions to match normal fields.
@@ -1740,7 +1740,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) {
[value release];
}
- OSSpinLockUnlock(&readOnlyMutex_);
+ dispatch_semaphore_signal(readOnlySemaphore_);
return value;
}
diff --git a/objectivec/GPBMessage_PackagePrivate.h b/objectivec/GPBMessage_PackagePrivate.h
index d4c3501a..b7e24fc9 100644
--- a/objectivec/GPBMessage_PackagePrivate.h
+++ b/objectivec/GPBMessage_PackagePrivate.h
@@ -62,7 +62,12 @@ typedef struct GPBMessage_Storage *GPBMessage_StoragePtr;
// by *read* operations such as getters (autocreation of message fields and
// message extensions, not setting of values). Used to guarantee thread safety
// for concurrent reads on the message.
- OSSpinLock readOnlyMutex_;
+ // NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+ // pointed out that they are vulnerable to live locking on iOS in cases of
+ // priority inversion:
+ // http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+ // https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+ dispatch_semaphore_t readOnlySemaphore_;
}
// Gets an extension value without autocreating the result if not found. (i.e.
diff --git a/objectivec/GPBRootObject.m b/objectivec/GPBRootObject.m
index 7036723f..4570716f 100644
--- a/objectivec/GPBRootObject.m
+++ b/objectivec/GPBRootObject.m
@@ -31,7 +31,6 @@
#import "GPBRootObject_PackagePrivate.h"
#import <objc/runtime.h>
-#import <libkern/OSAtomic.h>
#import <CoreFoundation/CoreFoundation.h>
@@ -96,13 +95,19 @@ static CFHashCode GPBRootExtensionKeyHash(const void *value) {
return jenkins_one_at_a_time_hash(key);
}
-static OSSpinLock gExtensionSingletonDictionaryLock_ = OS_SPINLOCK_INIT;
+// NOTE: OSSpinLock may seem like a good fit here but Apple engineers have
+// pointed out that they are vulnerable to live locking on iOS in cases of
+// priority inversion:
+// http://mjtsai.com/blog/2015/12/16/osspinlock-is-unsafe/
+// https://lists.swift.org/pipermail/swift-dev/Week-of-Mon-20151214/000372.html
+static dispatch_semaphore_t gExtensionSingletonDictionarySemaphore;
static CFMutableDictionaryRef gExtensionSingletonDictionary = NULL;
static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)initialize {
// Ensure the global is started up.
if (!gExtensionSingletonDictionary) {
+ gExtensionSingletonDictionarySemaphore = dispatch_semaphore_create(1);
CFDictionaryKeyCallBacks keyCallBacks = {
// See description above for reason for using custom dictionary.
0,
@@ -134,9 +139,10 @@ static GPBExtensionRegistry *gDefaultExtensionRegistry = NULL;
+ (void)globallyRegisterExtension:(GPBExtensionDescriptor *)field {
const char *key = [field singletonNameC];
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
CFDictionarySetValue(gExtensionSingletonDictionary, key, field);
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
}
static id ExtensionForName(id self, SEL _cmd) {
@@ -166,14 +172,24 @@ static id ExtensionForName(id self, SEL _cmd) {
key[classNameLen] = '_';
memcpy(&key[classNameLen + 1], selName, selNameLen);
key[classNameLen + 1 + selNameLen] = '\0';
- OSSpinLockLock(&gExtensionSingletonDictionaryLock_);
+
+ // NOTE: Even though this method is called from another C function,
+ // gExtensionSingletonDictionarySemaphore and gExtensionSingletonDictionary
+ // will always be initialized. This is because this call flow is just to
+ // lookup the Extension, meaning the code is calling an Extension class
+ // message on a Message or Root class. This guarantees that the class was
+ // initialized and Message classes ensure their Root was also initialized.
+ NSAssert(gExtensionSingletonDictionary, @"Startup order broken!");
+
+ dispatch_semaphore_wait(gExtensionSingletonDictionarySemaphore,
+ DISPATCH_TIME_FOREVER);
id extension = (id)CFDictionaryGetValue(gExtensionSingletonDictionary, key);
if (extension) {
// The method is getting wired in to the class, so no need to keep it in
// the dictionary.
CFDictionaryRemoveValue(gExtensionSingletonDictionary, key);
}
- OSSpinLockUnlock(&gExtensionSingletonDictionaryLock_);
+ dispatch_semaphore_signal(gExtensionSingletonDictionarySemaphore);
return extension;
}
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index 5ee61230..eaa28bbc 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -411,7 +411,7 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
return field.defaultValue.valueMessage;
}
- OSSpinLockLock(&self->readOnlyMutex_);
+ dispatch_semaphore_wait(self->readOnlySemaphore_, DISPATCH_TIME_FOREVER);
GPBMessage *result = GPBGetObjectIvarWithFieldNoAutocreate(self, field);
if (!result) {
// For non repeated messages, create the object, set it and return it.
@@ -420,7 +420,7 @@ id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) {
result = GPBCreateMessageWithAutocreator(field.msgClass, self, field);
GPBSetAutocreatedRetainedObjectIvarWithField(self, field, result);
}
- OSSpinLockUnlock(&self->readOnlyMutex_);
+ dispatch_semaphore_signal(self->readOnlySemaphore_);
return result;
}
diff --git a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
index 1c834bb2..6b34b9bf 100644
--- a/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_OSX.xcodeproj/project.pbxproj
@@ -28,7 +28,6 @@
8B8B615D17DF7056002EE618 /* GPBARCUnittestProtos.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
8B96157414C8C38C00A2AC0B /* GPBDescriptor.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */; };
8B96157514CA019D00A2AC0B /* Descriptor.pbobjc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BD3982214BE5B0C0081D629 /* Descriptor.pbobjc.m */; };
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */; };
8BBEA4A9147C727D00C4ADB7 /* GPBCodedInputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69B0F94FDF800A0C422 /* GPBCodedInputStreamTests.m */; };
8BBEA4AA147C727D00C4ADB7 /* GPBCodedOuputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */; };
8BBEA4AC147C727D00C4ADB7 /* GPBMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B6A30F94FDF800A0C422 /* GPBMessageTests.m */; };
@@ -158,7 +157,6 @@
8B8B615C17DF7056002EE618 /* GPBARCUnittestProtos.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBARCUnittestProtos.m; sourceTree = "<group>"; };
8B96157214C8B06000A2AC0B /* GPBDescriptor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GPBDescriptor.h; sourceTree = "<group>"; };
8B96157314C8C38C00A2AC0B /* GPBDescriptor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBDescriptor.m; sourceTree = "<group>"; };
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBStringTests.m; sourceTree = "<group>"; };
8BBD9DB016DD1DC8008E1EC1 /* unittest_lite.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_lite.proto; path = ../../src/google/protobuf/unittest_lite.proto; sourceTree = "<group>"; };
8BBEA4A6147C727100C4ADB7 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = "<group>"; };
@@ -419,7 +417,6 @@
F4487C7E1AAF62CD00531423 /* GPBMessageTests+Serialization.m */,
F4B51B1D1BBC610700744318 /* GPBObjectiveCPlusPlusTest.mm */,
F41C175C1833D3310064ED4D /* GPBPerfTests.m */,
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */,
8B4248BA1A8C256A00BC1EC6 /* GPBSwiftTests.swift */,
7461B6AB0F94FDF800A0C422 /* GPBTestUtilities.h */,
7461B6AC0F94FDF800A0C422 /* GPBTestUtilities.m */,
@@ -689,7 +686,6 @@
F41C175D1833D3310064ED4D /* GPBPerfTests.m in Sources */,
F4353D341AC06F10005A6198 /* GPBDictionaryTests+Bool.m in Sources */,
F4487C831AAF6AB300531423 /* GPBMessageTests+Merge.m in Sources */,
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */,
8BBEA4B6147C727D00C4ADB7 /* GPBUnknownFieldSetTest.m in Sources */,
F4353D371AC06F10005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D381AC06F10005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
diff --git a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
index 55013641..e9d3fc95 100644
--- a/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
+++ b/objectivec/ProtocolBuffers_iOS.xcodeproj/project.pbxproj
@@ -36,7 +36,6 @@
8B9A5EB41831993600A9D33B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B9A5EB31831993600A9D33B /* AppDelegate.m */; };
8B9A5EB61831993600A9D33B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8B9A5EB51831993600A9D33B /* Images.xcassets */; };
8B9A5EEC18330A0F00A9D33B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B9A5E9F1831913D00A9D33B /* UIKit.framework */; };
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */; };
8BBEA4A9147C727D00C4ADB7 /* GPBCodedInputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69B0F94FDF800A0C422 /* GPBCodedInputStreamTests.m */; };
8BBEA4AA147C727D00C4ADB7 /* GPBCodedOuputStreamTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B69D0F94FDF800A0C422 /* GPBCodedOuputStreamTests.m */; };
8BBEA4AC147C727D00C4ADB7 /* GPBMessageTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7461B6A30F94FDF800A0C422 /* GPBMessageTests.m */; };
@@ -179,7 +178,6 @@
8B9A5EAD1831993600A9D33B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8B9A5EB31831993600A9D33B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
8B9A5EB51831993600A9D33B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GPBStringTests.m; sourceTree = "<group>"; };
8BBD9DB016DD1DC8008E1EC1 /* unittest_lite.proto */ = {isa = PBXFileReference; lastKnownFileType = text; name = unittest_lite.proto; path = ../../src/google/protobuf/unittest_lite.proto; sourceTree = "<group>"; };
8BBEA4A6147C727100C4ADB7 /* UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
8BCF338814ED799900BC5317 /* GPBProtocolBuffers.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GPBProtocolBuffers.m; sourceTree = "<group>"; };
@@ -457,7 +455,6 @@
F4487C801AAF62FC00531423 /* GPBMessageTests+Serialization.m */,
F4B51B1B1BBC5C7100744318 /* GPBObjectiveCPlusPlusTest.mm */,
F41C175C1833D3310064ED4D /* GPBPerfTests.m */,
- 8BA9364418DA5F4B0056FA2A /* GPBStringTests.m */,
8B4248B31A8BD96E00BC1EC6 /* GPBSwiftTests.swift */,
7461B6AB0F94FDF800A0C422 /* GPBTestUtilities.h */,
7461B6AC0F94FDF800A0C422 /* GPBTestUtilities.m */,
@@ -785,7 +782,6 @@
F41C175D1833D3310064ED4D /* GPBPerfTests.m in Sources */,
F4353D421AC06F31005A6198 /* GPBDictionaryTests+Bool.m in Sources */,
F4487C851AAF6AC500531423 /* GPBMessageTests+Merge.m in Sources */,
- 8BA9364518DA5F4C0056FA2A /* GPBStringTests.m in Sources */,
8BBEA4B6147C727D00C4ADB7 /* GPBUnknownFieldSetTest.m in Sources */,
F4353D451AC06F31005A6198 /* GPBDictionaryTests+String.m in Sources */,
F4353D461AC06F31005A6198 /* GPBDictionaryTests+UInt32.m in Sources */,
diff --git a/objectivec/Tests/GPBCodedInputStreamTests.m b/objectivec/Tests/GPBCodedInputStreamTests.m
index 7c3c006b..b0e39d2c 100644
--- a/objectivec/Tests/GPBCodedInputStreamTests.m
+++ b/objectivec/Tests/GPBCodedInputStreamTests.m
@@ -266,6 +266,9 @@
}
// Verifies fix for b/10315336.
+// Note: Now that there isn't a custom string class under the hood, this test
+// isn't as critical, but it does cover bad input and if a custom class is added
+// again, it will help validate that class' handing of bad utf8.
- (void)testReadMalformedString {
NSOutputStream* rawOutput = [NSOutputStream outputStreamToMemory];
GPBCodedOutputStream* output =
@@ -276,7 +279,7 @@
[output writeRawVarint32:tag];
[output writeRawVarint32:5];
// Create an invalid utf-8 byte array.
- uint8_t bytes[5] = {0xc2, 0xf2};
+ uint8_t bytes[] = {0xc2, 0xf2, 0x0, 0x0, 0x0};
[output writeRawData:[NSData dataWithBytes:bytes length:sizeof(bytes)]];
[output flush];
@@ -286,6 +289,7 @@
TestAllTypes* message = [TestAllTypes parseFromCodedInputStream:input
extensionRegistry:nil
error:NULL];
+ XCTAssertNotNil(message);
// Make sure we can read string properties twice without crashing.
XCTAssertEqual([message.defaultString length], (NSUInteger)0);
XCTAssertEqualObjects(@"", message.defaultString);
diff --git a/objectivec/Tests/GPBStringTests.m b/objectivec/Tests/GPBStringTests.m
deleted file mode 100644
index 802afa7d..00000000
--- a/objectivec/Tests/GPBStringTests.m
+++ /dev/null
@@ -1,513 +0,0 @@
-// Protocol Buffers - Google's data interchange format
-// Copyright 2008 Google Inc. All rights reserved.
-// https://developers.google.com/protocol-buffers/
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google Inc. nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#import <XCTest/XCTest.h>
-
-#import "GPBCodedInputStream_PackagePrivate.h"
-#import "GPBTestUtilities.h"
-
-@interface TestClass : NSObject
-@property(nonatomic, retain) NSString *foo;
-@end
-
-@implementation TestClass
-@synthesize foo;
-@end
-
-@interface GPBStringTests : XCTestCase {
- NSMutableArray *nsStrings_;
- NSMutableArray *gpbStrings_;
-}
-
-@end
-
-@implementation GPBStringTests
-
-- (void)setUp {
- [super setUp];
- const char *strings[] = {
- "ascii string",
- "non-ascii string \xc3\xa9", // e with acute accent
- "\xe2\x99\xa1", // White Heart
- "mix \xe2\x99\xa4 string", // White Spade
-
- // Decomposed forms from http://www.unicode.org/reports/tr15/
- // 1.2 Singletons
- "\xe2\x84\xa8 = A\xcc\x8a = \xc3\x85", "\xe2\x84\xa6 = \xce\xa9",
-
- // 1.2 Canonical Composites
- "A\xcc\x8a = \xc3\x85",
- "o\xcc\x82 = \xc3\xb4",
-
- // 1.2 Multiple Combining Marks
- "s\xcc\xa3\xcc\x87 = \xe1\xb9\xa9",
- "\xe1\xb8\x8b\xcc\xa3 = d\xcc\xa3\xcc\x87 = \xe1\xb8\x8d \xcc\x87",
- "q\xcc\x87\xcc\xa3 = q\xcc\xa3\xcc\x87",
-
- // BOM
- "\xEF\xBB\xBF String with BOM",
- "String with \xEF\xBB\xBF in middle",
- "String with end bom \xEF\xBB\xBF",
- "\xEF\xBB\xBF\xe2\x99\xa1", // BOM White Heart
- "\xEF\xBB\xBF\xEF\xBB\xBF String with Two BOM",
-
- // Supplementary Plane
- "\xf0\x9d\x84\x9e", // MUSICAL SYMBOL G CLEF
-
- // Tags
- "\xf3\xa0\x80\x81", // Language Tag
-
- // Variation Selectors
- "\xf3\xa0\x84\x80", // VARIATION SELECTOR-17
-
- // Specials
- "\xef\xbb\xbf\xef\xbf\xbd\xef\xbf\xbf",
-
- // Left To Right/Right To Left
- // http://unicode.org/reports/tr9/
-
- // Hello! <RTL marker>!Merhaba<LTR marker>
- "Hello! \xE2\x80\x8F!\xd9\x85\xd8\xb1\xd8\xad\xd8\xa8\xd8\xa7\xE2\x80\x8E",
-
- "\xE2\x80\x8E LTR At Start",
- "LTR At End\xE2\x80\x8E",
- "\xE2\x80\x8F RTL At Start",
- "RTL At End\xE2\x80\x8F",
- "\xE2\x80\x8E\xE2\x80\x8E Double LTR \xE2\x80\x8E\xE2\x80\x8E",
- "\xE2\x80\x8F\xE2\x80\x8F Double RTL \xE2\x80\x8F\xE2\x80\x8F",
- "\xE2\x80\x8F\xE2\x80\x8E LTR-RTL LTR-RTL \xE2\x80\x8E\xE2\x80\x8F",
- };
- size_t stringsSize = GPBARRAYSIZE(strings);
- size_t numberUnicodeStrings = 17375;
- nsStrings_ = [[NSMutableArray alloc]
- initWithCapacity:stringsSize + numberUnicodeStrings];
- gpbStrings_ = [[NSMutableArray alloc]
- initWithCapacity:stringsSize + numberUnicodeStrings];
- for (size_t i = 0; i < stringsSize; ++i) {
- size_t length = strlen(strings[i]);
- NSString *nsString = [[NSString alloc] initWithBytes:strings[i]
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- GPBString *gpbString = GPBCreateGPBStringWithUTF8(strings[i], length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
-
- // Generate all UTF8 characters in a variety of strings
- // UTF8-1 - 1 Byte UTF 8 chars
- int length = 0x7F + 1;
- char *buffer = (char *)calloc(length, 1);
- for (int i = 0; i < length; ++i) {
- buffer[i] = (char)i;
- }
- NSString *nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- GPBString *gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
-
- // UTF8-2 - 2 Byte UTF 8 chars
- int pointLength = 0xbf - 0x80 + 1;
- length = pointLength * 2;
- buffer = (char *)calloc(length, 1);
- for (int i = 0xc2; i <= 0xdf; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- free(buffer);
-
- // UTF8-3 - 3 Byte UTF 8 chars
- length = pointLength * 3;
- buffer = (char *)calloc(length, 1);
- for (int i = 0xa0; i <= 0xbf; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)0xE0;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- for (int i = 0xe1; i <= 0xec; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- for (int i = 0x80; i <= 0x9f; ++i) {
- char *bufferPtr = buffer;
- for (int j = 0x80; j <= 0xbf; ++j) {
- (*bufferPtr++) = (char)0xED;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- for (int i = 0xee; i <= 0xef; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- free(buffer);
-
- // UTF8-4 - 4 Byte UTF 8 chars
- length = pointLength * 4;
- buffer = (char *)calloc(length, 1);
- for (int i = 0x90; i <= 0xbf; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)0xF0;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- for (int i = 0xf1; i <= 0xf3; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- for (int k = 0x80; k <= 0xbf; ++k) {
- char *bufferPtr = buffer;
- for (int m = 0x80; m <= 0xbf; ++m) {
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- (*bufferPtr++) = (char)m;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- }
- for (int i = 0x80; i <= 0x8f; ++i) {
- for (int j = 0x80; j <= 0xbf; ++j) {
- char *bufferPtr = buffer;
- for (int k = 0x80; k <= 0xbf; ++k) {
- (*bufferPtr++) = (char)0xF4;
- (*bufferPtr++) = (char)i;
- (*bufferPtr++) = (char)j;
- (*bufferPtr++) = (char)k;
- }
- nsString = [[NSString alloc] initWithBytes:buffer
- length:length
- encoding:NSUTF8StringEncoding];
- [nsStrings_ addObject:nsString];
- [nsString release];
- gpbString = GPBCreateGPBStringWithUTF8(buffer, length);
- [gpbStrings_ addObject:gpbString];
- [gpbString release];
- }
- }
- free(buffer);
-}
-
-- (void)tearDown {
- [nsStrings_ release];
- [gpbStrings_ release];
- [super tearDown];
-}
-
-- (void)testLength {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString length], [gpbString length], @"%@ %@", nsString,
- gpbString);
- ++i;
- }
-}
-
-- (void)testLengthOfBytesUsingEncoding {
- NSStringEncoding encodings[] = {
- NSUTF8StringEncoding,
- NSASCIIStringEncoding,
- NSISOLatin1StringEncoding,
- NSMacOSRomanStringEncoding,
- NSUTF16StringEncoding,
- NSUTF32StringEncoding,
- };
-
- for (size_t j = 0; j < GPBARRAYSIZE(encodings); ++j) {
- NSStringEncoding testEncoding = encodings[j];
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString lengthOfBytesUsingEncoding:testEncoding],
- [gpbString lengthOfBytesUsingEncoding:testEncoding],
- @"%@ %@", nsString, gpbString);
- ++i;
- }
- }
-}
-
-- (void)testHash {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqual([nsString hash], [gpbString hash], @"%@ %@", nsString,
- gpbString);
- ++i;
- }
-}
-
-- (void)testEquality {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testCharacterAtIndex {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- NSUInteger length = [nsString length];
- for (size_t j = 0; j < length; ++j) {
- unichar nsChar = [nsString characterAtIndex:j];
- unichar pbChar = [gpbString characterAtIndex:j];
- XCTAssertEqual(nsChar, pbChar, @"%@ %@ %zu", nsString, gpbString, j);
- }
- ++i;
- }
-}
-
-- (void)testCopy {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = [[gpbStrings_[i] copy] autorelease];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testMutableCopy {
- size_t i = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = [[gpbStrings_[i] mutableCopy] autorelease];
- XCTAssertEqualObjects(nsString, gpbString);
- ++i;
- }
-}
-
-- (void)testGetBytes {
- // Do an attempt at a reasonably exhaustive test of get bytes.
- // Get bytes with options other than 0 should always fall through to Apple
- // code so we don't bother testing that path.
- size_t i = 0;
- char pbBuffer[256];
- char nsBuffer[256];
- int count = 0;
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i];
- for (int j = 0; j < 100; ++j) {
- // [NSString getBytes:maxLength:usedLength:encoding:options:range:remainingRange]
- // does not return reliable results if the maxLength argument is 0,
- // or range is 0,0.
- // Radar 16385183
- NSUInteger length = [nsString length];
- NSUInteger maxBufferCount = (arc4random() % (length + 3)) + 1;
- NSUInteger rangeStart = arc4random() % length;
- NSUInteger rangeLength = arc4random() % (length - rangeStart);
-
- NSRange range = NSMakeRange(rangeStart, rangeLength);
-
- NSStringEncoding encodings[] = {
- NSASCIIStringEncoding,
- NSUTF8StringEncoding,
- NSUTF16StringEncoding,
- };
-
- for (size_t k = 0; k < GPBARRAYSIZE(encodings); ++k) {
- NSStringEncoding encoding = encodings[k];
- NSUInteger pbUsedBufferCount = 0;
- NSUInteger nsUsedBufferCount = 0;
- NSRange pbLeftOver = NSMakeRange(0, 0);
- NSRange nsLeftOver = NSMakeRange(0, 0);
-
- BOOL pbGotBytes = [gpbString getBytes:pbBuffer
- maxLength:maxBufferCount
- usedLength:&pbUsedBufferCount
- encoding:encoding
- options:0
- range:range
- remainingRange:&pbLeftOver];
- BOOL nsGotBytes = [nsString getBytes:nsBuffer
- maxLength:maxBufferCount
- usedLength:&nsUsedBufferCount
- encoding:encoding
- options:0
- range:range
- remainingRange:&nsLeftOver];
- XCTAssertEqual(
- (bool)pbGotBytes, (bool)nsGotBytes,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbUsedBufferCount, nsUsedBufferCount,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbLeftOver.location, nsLeftOver.location,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- XCTAssertEqual(
- pbLeftOver.length, nsLeftOver.length,
- @"PB %d '%@' vs '%@' Encoding:%tu MaxLength: %tu Range: %@ "
- @"Used: %tu, %tu LeftOver %@, %@)",
- count, gpbString, nsString, encoding, maxBufferCount,
- NSStringFromRange(range), pbUsedBufferCount, nsUsedBufferCount,
- NSStringFromRange(pbLeftOver), NSStringFromRange(nsLeftOver));
- ++count;
- }
- }
- ++i;
- }
-}
-
-- (void)testLengthAndGetBytes {
- // This test exists as an attempt to ferret out a bug.
- // http://b/13516532
- size_t i = 0;
- char pbBuffer[256];
- char nsBuffer[256];
- for (NSString *nsString in nsStrings_) {
- GPBString *gpbString = gpbStrings_[i++];
- NSUInteger nsLength =
- [nsString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- NSUInteger pbLength =
- [gpbString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
- XCTAssertEqual(nsLength, pbLength, @"%@ %@", nsString, gpbString);
- NSUInteger pbUsedBufferCount = 0;
- NSUInteger nsUsedBufferCount = 0;
- NSRange pbLeftOver = NSMakeRange(0, 0);
- NSRange nsLeftOver = NSMakeRange(0, 0);
- NSRange range = NSMakeRange(0, [gpbString length]);
- BOOL pbGotBytes = [gpbString getBytes:pbBuffer
- maxLength:sizeof(pbBuffer)
- usedLength:&pbUsedBufferCount
- encoding:NSUTF8StringEncoding
- options:0
- range:range
- remainingRange:&pbLeftOver];
- BOOL nsGotBytes = [nsString getBytes:nsBuffer
- maxLength:sizeof(nsBuffer)
- usedLength:&nsUsedBufferCount
- encoding:NSUTF8StringEncoding
- options:0
- range:range
- remainingRange:&nsLeftOver];
- XCTAssertTrue(pbGotBytes, @"%@", gpbString);
- XCTAssertTrue(nsGotBytes, @"%@", nsString);
- XCTAssertEqual(pbUsedBufferCount, pbLength, @"%@", gpbString);
- XCTAssertEqual(nsUsedBufferCount, nsLength, @"%@", nsString);
- }
-}
-
-@end