Merge pull request #1060 from thomasvl/drop_spinlocks
Drop all use of OSSpinLock
This commit is contained in:
commit
6b228f3532
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */,
|
||||
|
@ -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 */,
|
||||
|
@ -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);
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user