1// Protocol Buffers - Google's data interchange format 2// Copyright 2015 Google Inc. All rights reserved. 3// https://developers.google.com/protocol-buffers/ 4// 5// Redistribution and use in source and binary forms, with or without 6// modification, are permitted provided that the following conditions are 7// met: 8// 9// * Redistributions of source code must retain the above copyright 10// notice, this list of conditions and the following disclaimer. 11// * Redistributions in binary form must reproduce the above 12// copyright notice, this list of conditions and the following disclaimer 13// in the documentation and/or other materials provided with the 14// distribution. 15// * Neither the name of Google Inc. nor the names of its 16// contributors may be used to endorse or promote products derived from 17// this software without specific prior written permission. 18// 19// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 31// Importing sources here to force the linker to include our category methods in 32// the static library. If these were compiled separately, the category methods 33// below would be stripped by the linker. 34 35#import "GPBWellKnownTypes.h" 36 37#import "GPBUtilities_PackagePrivate.h" 38 39NSString *const GPBWellKnownTypesErrorDomain = 40 GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain); 41 42static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/"; 43 44static NSTimeInterval TimeIntervalFromSecondsAndNanos(int64_t seconds, 45 int32_t nanos) { 46 return seconds + (NSTimeInterval)nanos / 1e9; 47} 48 49static int32_t SecondsAndNanosFromTimeInterval(NSTimeInterval time, 50 int64_t *outSeconds, 51 BOOL nanosMustBePositive) { 52 NSTimeInterval seconds; 53 NSTimeInterval nanos = modf(time, &seconds); 54 55 if (nanosMustBePositive && (nanos < 0)) { 56 // Per Timestamp.proto, nanos is non-negative and "Negative second values with 57 // fractions must still have non-negative nanos values that count forward in 58 // time. Must be from 0 to 999,999,999 inclusive." 59 --seconds; 60 nanos = 1.0 + nanos; 61 } 62 63 nanos *= 1e9; 64 *outSeconds = (int64_t)seconds; 65 return (int32_t)nanos; 66} 67 68static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) { 69 if (typeURLPrefix.length == 0) { 70 return fullName; 71 } 72 73 if ([typeURLPrefix hasSuffix:@"/"]) { 74 return [typeURLPrefix stringByAppendingString:fullName]; 75 } 76 77 return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName]; 78} 79 80static NSString *ParseTypeFromURL(NSString *typeURLString) { 81 NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch]; 82 if ((range.location == NSNotFound) || 83 (NSMaxRange(range) == typeURLString.length)) { 84 return nil; 85 } 86 NSString *result = [typeURLString substringFromIndex:range.location + 1]; 87 return result; 88} 89 90#pragma mark - GPBTimestamp 91 92@implementation GPBTimestamp (GBPWellKnownTypes) 93 94- (instancetype)initWithDate:(NSDate *)date { 95 return [self initWithTimeIntervalSince1970:date.timeIntervalSince1970]; 96} 97 98- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 99 if ((self = [super init])) { 100 int64_t seconds; 101 int32_t nanos = SecondsAndNanosFromTimeInterval( 102 timeIntervalSince1970, &seconds, YES); 103 self.seconds = seconds; 104 self.nanos = nanos; 105 } 106 return self; 107} 108 109- (NSDate *)date { 110 return [NSDate dateWithTimeIntervalSince1970:self.timeIntervalSince1970]; 111} 112 113- (void)setDate:(NSDate *)date { 114 self.timeIntervalSince1970 = date.timeIntervalSince1970; 115} 116 117- (NSTimeInterval)timeIntervalSince1970 { 118 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); 119} 120 121- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 122 int64_t seconds; 123 int32_t nanos = 124 SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES); 125 self.seconds = seconds; 126 self.nanos = nanos; 127} 128 129@end 130 131#pragma mark - GPBDuration 132 133@implementation GPBDuration (GBPWellKnownTypes) 134 135- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval { 136 if ((self = [super init])) { 137 int64_t seconds; 138 int32_t nanos = SecondsAndNanosFromTimeInterval( 139 timeInterval, &seconds, NO); 140 self.seconds = seconds; 141 self.nanos = nanos; 142 } 143 return self; 144} 145 146- (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 147 return [self initWithTimeInterval:timeIntervalSince1970]; 148} 149 150- (NSTimeInterval)timeInterval { 151 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos); 152} 153 154- (void)setTimeInterval:(NSTimeInterval)timeInterval { 155 int64_t seconds; 156 int32_t nanos = 157 SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO); 158 self.seconds = seconds; 159 self.nanos = nanos; 160} 161 162- (NSTimeInterval)timeIntervalSince1970 { 163 return self.timeInterval; 164} 165 166- (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 { 167 self.timeInterval = timeIntervalSince1970; 168} 169 170@end 171 172#pragma mark - GPBAny 173 174@implementation GPBAny (GBPWellKnownTypes) 175 176+ (instancetype)anyWithMessage:(GPBMessage *)message 177 error:(NSError **)errorPtr { 178 return [self anyWithMessage:message 179 typeURLPrefix:kTypePrefixGoogleApisCom 180 error:errorPtr]; 181} 182 183+ (instancetype)anyWithMessage:(GPBMessage *)message 184 typeURLPrefix:(NSString *)typeURLPrefix 185 error:(NSError **)errorPtr { 186 return [[[self alloc] initWithMessage:message 187 typeURLPrefix:typeURLPrefix 188 error:errorPtr] autorelease]; 189} 190 191- (instancetype)initWithMessage:(GPBMessage *)message 192 error:(NSError **)errorPtr { 193 return [self initWithMessage:message 194 typeURLPrefix:kTypePrefixGoogleApisCom 195 error:errorPtr]; 196} 197 198- (instancetype)initWithMessage:(GPBMessage *)message 199 typeURLPrefix:(NSString *)typeURLPrefix 200 error:(NSError **)errorPtr { 201 self = [self init]; 202 if (self) { 203 if (![self packWithMessage:message 204 typeURLPrefix:typeURLPrefix 205 error:errorPtr]) { 206 [self release]; 207 self = nil; 208 } 209 } 210 return self; 211} 212 213- (BOOL)packWithMessage:(GPBMessage *)message 214 error:(NSError **)errorPtr { 215 return [self packWithMessage:message 216 typeURLPrefix:kTypePrefixGoogleApisCom 217 error:errorPtr]; 218} 219 220- (BOOL)packWithMessage:(GPBMessage *)message 221 typeURLPrefix:(NSString *)typeURLPrefix 222 error:(NSError **)errorPtr { 223 NSString *fullName = [message descriptor].fullName; 224 if (fullName.length == 0) { 225 if (errorPtr) { 226 *errorPtr = 227 [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 228 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL 229 userInfo:nil]; 230 } 231 return NO; 232 } 233 if (errorPtr) { 234 *errorPtr = nil; 235 } 236 self.typeURL = BuildTypeURL(typeURLPrefix, fullName); 237 self.value = message.data; 238 return YES; 239} 240 241- (GPBMessage *)unpackMessageClass:(Class)messageClass 242 error:(NSError **)errorPtr { 243 NSString *fullName = [messageClass descriptor].fullName; 244 if (fullName.length == 0) { 245 if (errorPtr) { 246 *errorPtr = 247 [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 248 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL 249 userInfo:nil]; 250 } 251 return nil; 252 } 253 254 NSString *expectedFullName = ParseTypeFromURL(self.typeURL); 255 if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) { 256 if (errorPtr) { 257 *errorPtr = 258 [NSError errorWithDomain:GPBWellKnownTypesErrorDomain 259 code:GPBWellKnownTypesErrorCodeTypeURLMismatch 260 userInfo:nil]; 261 } 262 return nil; 263 } 264 265 // Any is proto3, which means no extensions, so this assumes anything put 266 // within an any also won't need extensions. A second helper could be added 267 // if needed. 268 return [messageClass parseFromData:self.value 269 error:errorPtr]; 270} 271 272@end 273