1// Protocol Buffers - Google's data interchange format 2// Copyright 2015 Google Inc. All rights reserved. 3// 4// Use of this source code is governed by a BSD-style 5// license that can be found in the LICENSE file or at 6// https://developers.google.com/open-source/licenses/bsd 7 8// Importing sources here to force the linker to include our category methods in 9// the static library. If these were compiled separately, the category methods 10// below would be stripped by the linker. 11 12#import "GPBWellKnownTypes.h" 13 14#import "GPBUtilities.h" 15#import "GPBUtilities_PackagePrivate.h" 16 17NSString *const GPBWellKnownTypesErrorDomain = GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain); 18 19static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/"; 20 21static NSTimeInterval TimeIntervalFromSecondsAndNanos(int64_t seconds, int32_t nanos) { 22 return seconds + (NSTimeInterval)nanos / 1e9; 23} 24 25static int32_t SecondsAndNanosFromTimeInterval(NSTimeInterval time, int64_t *outSeconds, 26 BOOL nanosMustBePositive) { 27 NSTimeInterval seconds; 28 NSTimeInterval nanos = modf(time, &seconds); 29 30 if (nanosMustBePositive && (nanos < 0)) { 31 // Per Timestamp.proto, nanos is non-negative and "Negative second values with 32 // fractions must still have non-negative nanos values that count forward in 33 // time. Must be from 0 to 999,999,999 inclusive." 34 --seconds; 35 nanos = 1.0 + nanos; 36 } 37 38 nanos *= 1e9; 39 *outSeconds = (int64_t)seconds; 40 return (int32_t)nanos; 41} 42 43static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) { 44 if (typeURLPrefix.length == 0) { 45 return fullName; 46 } 47 48 if ([typeURLPrefix hasSuffix:@"/"]) { 49 return [typeURLPrefix stringByAppendingString:fullName]; 50 } 51 52 return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName]; 53} 54 55static NSString *ParseTypeFromURL(NSString *typeURLString) { 56 NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch]; 57 if ((range.location == NSNotFound) || (NSMaxRange(range) == typeURLString.length)) { 58 return nil; 59 } 60 NSString *result = [typeURLString substringFromIndex:range.location + 1]; 61 return result; 62} 63 64#pragma mark - GPBTimestamp 65 66@implementation GPBTimestamp (GBPWellKnownTypes) 67 68- (instancetype)initWithDate:(NSDate *)date { 69 return [self initWithTimeIntervalSince1970:date.timeIntervalSince1970]; 70} 71 72- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 73 if ((self = [super init])) { 74 int64_t seconds; 75 int32_t nanos = SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES); 76 self.seconds = seconds; 77 self.nanos = nanos; 78 } 79 return self; 80} 81 82- (NSDate *)date { 83 return [NSDate dateWithTimeIntervalSince1970:self.timeIntervalSince1970]; 84} 85 86- (void)setDate:(NSDate *)date { 87 self.timeIntervalSince1970 = date.timeIntervalSince1970; 88} 89 90- (NSTimeInterval)timeIntervalSince1970 { 91 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); 92} 93 94- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 95 int64_t seconds; 96 int32_t nanos = SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES); 97 self.seconds = seconds; 98 self.nanos = nanos; 99} 100 101@end 102 103#pragma mark - GPBDuration 104 105@implementation GPBDuration (GBPWellKnownTypes) 106 107- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval { 108 if ((self = [super init])) { 109 int64_t seconds; 110 int32_t nanos = SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO); 111 self.seconds = seconds; 112 self.nanos = nanos; 113 } 114 return self; 115} 116 117- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 118 return [self initWithTimeInterval:timeIntervalSince1970]; 119} 120 121- (NSTimeInterval)timeInterval { 122 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); 123} 124 125- (void)setTimeInterval:(NSTimeInterval)timeInterval { 126 int64_t seconds; 127 int32_t nanos = SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO); 128 self.seconds = seconds; 129 self.nanos = nanos; 130} 131 132- (NSTimeInterval)timeIntervalSince1970 { 133 return self.timeInterval; 134} 135 136- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 137 self.timeInterval = timeIntervalSince1970; 138} 139 140@end 141 142#pragma mark - GPBAny 143 144@implementation GPBAny (GBPWellKnownTypes) 145 146+ (instancetype)anyWithMessage:(GPBMessage *)message error:(NSError **)errorPtr { 147 return [self anyWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr]; 148} 149 150+ (instancetype)anyWithMessage:(GPBMessage *)message 151 typeURLPrefix:(NSString *)typeURLPrefix 152 error:(NSError **)errorPtr { 153 return [[[self alloc] initWithMessage:message typeURLPrefix:typeURLPrefix 154 error:errorPtr] autorelease]; 155} 156 157- (instancetype)initWithMessage:(GPBMessage *)message error:(NSError **)errorPtr { 158 return [self initWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr]; 159} 160 161- (instancetype)initWithMessage:(GPBMessage *)message 162 typeURLPrefix:(NSString *)typeURLPrefix 163 error:(NSError **)errorPtr { 164 self = [self init]; 165 if (self) { 166 if (![self packWithMessage:message typeURLPrefix:typeURLPrefix error:errorPtr]) { 167 [self release]; 168 self = nil; 169 } 170 } 171 return self; 172} 173 174- (BOOL)packWithMessage:(GPBMessage *)message error:(NSError **)errorPtr { 175 return [self packWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr]; 176} 177 178- (BOOL)packWithMessage:(GPBMessage *)message 179 typeURLPrefix:(NSString *)typeURLPrefix 180 error:(NSError **)errorPtr { 181 NSString *fullName = [message descriptor].fullName; 182 if (fullName.length == 0) { 183 if (errorPtr) { 184 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 185 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL 186 userInfo:nil]; 187 } 188 return NO; 189 } 190 if (errorPtr) { 191 *errorPtr = nil; 192 } 193 self.typeURL = BuildTypeURL(typeURLPrefix, fullName); 194 self.value = message.data; 195 return YES; 196} 197 198- (GPBMessage *)unpackMessageClass:(Class)messageClass error:(NSError **)errorPtr { 199 return [self unpackMessageClass:messageClass extensionRegistry:nil error:errorPtr]; 200} 201 202- (nullable GPBMessage *)unpackMessageClass:(Class)messageClass 203 extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry 204 error:(NSError **)errorPtr { 205 NSString *fullName = [messageClass descriptor].fullName; 206 if (fullName.length == 0) { 207 if (errorPtr) { 208 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 209 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL 210 userInfo:nil]; 211 } 212 return nil; 213 } 214 215 NSString *expectedFullName = ParseTypeFromURL(self.typeURL); 216 if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) { 217 if (errorPtr) { 218 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 219 code:GPBWellKnownTypesErrorCodeTypeURLMismatch 220 userInfo:nil]; 221 } 222 return nil; 223 } 224 225 return [messageClass parseFromData:self.value extensionRegistry:extensionRegistry error:errorPtr]; 226} 227 228@end 229