diff options
Diffstat (limited to 'objectivec/GPBWellKnownTypes.m')
-rw-r--r-- | objectivec/GPBWellKnownTypes.m | 189 |
1 files changed, 172 insertions, 17 deletions
diff --git a/objectivec/GPBWellKnownTypes.m b/objectivec/GPBWellKnownTypes.m index fe02f5de..2808afeb 100644 --- a/objectivec/GPBWellKnownTypes.m +++ b/objectivec/GPBWellKnownTypes.m @@ -32,24 +32,63 @@ // the static library. If these were compiled separately, the category methods // below would be stripped by the linker. -#import "google/protobuf/Timestamp.pbobjc.m" -#import "google/protobuf/Duration.pbobjc.m" #import "GPBWellKnownTypes.h" -static NSTimeInterval TimeIntervalSince1970FromSecondsAndNanos(int64_t seconds, - int32_t nanos) { +#import "GPBUtilities_PackagePrivate.h" + +NSString *const GPBWellKnownTypesErrorDomain = + GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain); + +static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/"; + +static NSTimeInterval TimeIntervalFromSecondsAndNanos(int64_t seconds, + int32_t nanos) { return seconds + (NSTimeInterval)nanos / 1e9; } -static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time, - int64_t *outSeconds) { +static int32_t SecondsAndNanosFromTimeInterval(NSTimeInterval time, + int64_t *outSeconds, + BOOL nanosMustBePositive) { NSTimeInterval seconds; NSTimeInterval nanos = modf(time, &seconds); + + if (nanosMustBePositive && (nanos < 0)) { + // Per Timestamp.proto, nanos is non-negative and "Negative second values with + // fractions must still have non-negative nanos values that count forward in + // time. Must be from 0 to 999,999,999 inclusive." + --seconds; + nanos = 1.0 + nanos; + } + nanos *= 1e9; *outSeconds = (int64_t)seconds; return (int32_t)nanos; } +static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) { + if (typeURLPrefix.length == 0) { + return fullName; + } + + if ([typeURLPrefix hasSuffix:@"/"]) { + return [typeURLPrefix stringByAppendingString:fullName]; + } + + return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName]; +} + +static NSString *ParseTypeFromURL(NSString *typeURLString) { + NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch]; + if ((range.location == NSNotFound) || + (NSMaxRange(range) == typeURLString.length)) { + return nil; + } + NSString *result = [typeURLString substringFromIndex:range.location + 1]; + return result; +} + +#pragma mark - GPBTimestamp + @implementation GPBTimestamp (GBPWellKnownTypes) - (instancetype)initWithDate:(NSDate *)date { @@ -59,8 +98,8 @@ static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time, - (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { if ((self = [super init])) { int64_t seconds; - int32_t nanos = SecondsAndNanosFromTimeIntervalSince1970( - timeIntervalSince1970, &seconds); + int32_t nanos = SecondsAndNanosFromTimeInterval( + timeIntervalSince1970, &seconds, YES); self.seconds = seconds; self.nanos = nanos; } @@ -76,42 +115,158 @@ static int32_t SecondsAndNanosFromTimeIntervalSince1970(NSTimeInterval time, } - (NSTimeInterval)timeIntervalSince1970 { - return TimeIntervalSince1970FromSecondsAndNanos(self.seconds, self.nanos); + return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); } - (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { int64_t seconds; int32_t nanos = - SecondsAndNanosFromTimeIntervalSince1970(timeIntervalSince1970, &seconds); + SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES); self.seconds = seconds; self.nanos = nanos; } @end +#pragma mark - GPBDuration + @implementation GPBDuration (GBPWellKnownTypes) -- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { +- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval { if ((self = [super init])) { int64_t seconds; - int32_t nanos = SecondsAndNanosFromTimeIntervalSince1970( - timeIntervalSince1970, &seconds); + int32_t nanos = SecondsAndNanosFromTimeInterval( + timeInterval, &seconds, NO); self.seconds = seconds; self.nanos = nanos; } return self; } -- (NSTimeInterval)timeIntervalSince1970 { - return TimeIntervalSince1970FromSecondsAndNanos(self.seconds, self.nanos); +- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { + return [self initWithTimeInterval:timeIntervalSince1970]; } -- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { +- (NSTimeInterval)timeInterval { + return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); +} + +- (void)setTimeInterval:(NSTimeInterval)timeInterval { int64_t seconds; int32_t nanos = - SecondsAndNanosFromTimeIntervalSince1970(timeIntervalSince1970, &seconds); + SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO); self.seconds = seconds; self.nanos = nanos; } +- (NSTimeInterval)timeIntervalSince1970 { + return self.timeInterval; +} + +- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { + self.timeInterval = timeIntervalSince1970; +} + +@end + +#pragma mark - GPBAny + +@implementation GPBAny (GBPWellKnownTypes) + ++ (instancetype)anyWithMessage:(GPBMessage *)message + error:(NSError **)errorPtr { + return [self anyWithMessage:message + typeURLPrefix:kTypePrefixGoogleApisCom + error:errorPtr]; +} + ++ (instancetype)anyWithMessage:(GPBMessage *)message + typeURLPrefix:(NSString *)typeURLPrefix + error:(NSError **)errorPtr { + return [[[self alloc] initWithMessage:message + typeURLPrefix:typeURLPrefix + error:errorPtr] autorelease]; +} + +- (instancetype)initWithMessage:(GPBMessage *)message + error:(NSError **)errorPtr { + return [self initWithMessage:message + typeURLPrefix:kTypePrefixGoogleApisCom + error:errorPtr]; +} + +- (instancetype)initWithMessage:(GPBMessage *)message + typeURLPrefix:(NSString *)typeURLPrefix + error:(NSError **)errorPtr { + self = [self init]; + if (self) { + if (![self packWithMessage:message + typeURLPrefix:typeURLPrefix + error:errorPtr]) { + [self release]; + self = nil; + } + } + return self; +} + +- (BOOL)packWithMessage:(GPBMessage *)message + error:(NSError **)errorPtr { + return [self packWithMessage:message + typeURLPrefix:kTypePrefixGoogleApisCom + error:errorPtr]; +} + +- (BOOL)packWithMessage:(GPBMessage *)message + typeURLPrefix:(NSString *)typeURLPrefix + error:(NSError **)errorPtr { + NSString *fullName = [message descriptor].fullName; + if (fullName.length == 0) { + if (errorPtr) { + *errorPtr = + [NSError errorWithDomain:GPBWellKnownTypesErrorDomain + code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL + userInfo:nil]; + } + return NO; + } + if (errorPtr) { + *errorPtr = nil; + } + self.typeURL = BuildTypeURL(typeURLPrefix, fullName); + self.value = message.data; + return YES; +} + +- (GPBMessage *)unpackMessageClass:(Class)messageClass + error:(NSError **)errorPtr { + NSString *fullName = [messageClass descriptor].fullName; + if (fullName.length == 0) { + if (errorPtr) { + *errorPtr = + [NSError errorWithDomain:GPBWellKnownTypesErrorDomain + code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL + userInfo:nil]; + } + return nil; + } + + NSString *expectedFullName = ParseTypeFromURL(self.typeURL); + if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) { + if (errorPtr) { + *errorPtr = + [NSError errorWithDomain:GPBWellKnownTypesErrorDomain + code:GPBWellKnownTypesErrorCodeTypeURLMismatch + userInfo:nil]; + } + return nil; + } + + // Any is proto3, which means no extensions, so this assumes anything put + // within an any also won't need extensions. A second helper could be added + // if needed. + return [messageClass parseFromData:self.value + error:errorPtr]; +} + @end |