1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2015 Google Inc. All rights reserved.
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
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.
12 #import "GPBWellKnownTypes.h"
14 #import "GPBUtilities.h"
15 #import "GPBUtilities_PackagePrivate.h"
17 NSString *const GPBWellKnownTypesErrorDomain = GPBNSStringifySymbol(GPBWellKnownTypesErrorDomain);
19 static NSString *kTypePrefixGoogleApisCom = @"type.googleapis.com/";
21 static NSTimeInterval TimeIntervalFromSecondsAndNanos(int64_t seconds, int32_t nanos) {
22 return seconds + (NSTimeInterval)nanos / 1e9;
25 static int32_t SecondsAndNanosFromTimeInterval(NSTimeInterval time, int64_t *outSeconds,
26 BOOL nanosMustBePositive) {
27 NSTimeInterval seconds;
28 NSTimeInterval nanos = modf(time, &seconds);
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."
39 *outSeconds = (int64_t)seconds;
40 return (int32_t)nanos;
43 static NSString *BuildTypeURL(NSString *typeURLPrefix, NSString *fullName) {
44 if (typeURLPrefix.length == 0) {
48 if ([typeURLPrefix hasSuffix:@"/"]) {
49 return [typeURLPrefix stringByAppendingString:fullName];
52 return [NSString stringWithFormat:@"%@/%@", typeURLPrefix, fullName];
55 static NSString *ParseTypeFromURL(NSString *typeURLString) {
56 NSRange range = [typeURLString rangeOfString:@"/" options:NSBackwardsSearch];
57 if ((range.location == NSNotFound) || (NSMaxRange(range) == typeURLString.length)) {
60 NSString *result = [typeURLString substringFromIndex:range.location + 1];
64 #pragma mark - GPBTimestamp
66 @implementation GPBTimestamp (GBPWellKnownTypes)
68 - (instancetype)initWithDate:(NSDate *)date {
69 return [self initWithTimeIntervalSince1970:date.timeIntervalSince1970];
72 - (instancetype)initWithTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
73 if ((self = [super init])) {
75 int32_t nanos = SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES);
76 self.seconds = seconds;
83 return [NSDate dateWithTimeIntervalSince1970:self.timeIntervalSince1970];
86 - (void)setDate:(NSDate *)date {
87 self.timeIntervalSince1970 = date.timeIntervalSince1970;
90 - (NSTimeInterval)timeIntervalSince1970 {
91 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos);
94 - (void)setTimeIntervalSince1970:(NSTimeInterval)timeIntervalSince1970 {
96 int32_t nanos = SecondsAndNanosFromTimeInterval(timeIntervalSince1970, &seconds, YES);
97 self.seconds = seconds;
103 #pragma mark - GPBDuration
105 @implementation GPBDuration (GBPWellKnownTypes)
107 - (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval {
108 if ((self = [super init])) {
110 int32_t nanos = SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO);
111 self.seconds = seconds;
117 - (NSTimeInterval)timeInterval {
118 return TimeIntervalFromSecondsAndNanos(self.seconds, self.nanos);
121 - (void)setTimeInterval:(NSTimeInterval)timeInterval {
123 int32_t nanos = SecondsAndNanosFromTimeInterval(timeInterval, &seconds, NO);
124 self.seconds = seconds;
130 #pragma mark - GPBAny
132 @implementation GPBAny (GBPWellKnownTypes)
134 + (instancetype)anyWithMessage:(GPBMessage *)message error:(NSError **)errorPtr {
135 return [self anyWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr];
138 + (instancetype)anyWithMessage:(GPBMessage *)message
139 typeURLPrefix:(NSString *)typeURLPrefix
140 error:(NSError **)errorPtr {
141 return [[[self alloc] initWithMessage:message typeURLPrefix:typeURLPrefix
142 error:errorPtr] autorelease];
145 - (instancetype)initWithMessage:(GPBMessage *)message error:(NSError **)errorPtr {
146 return [self initWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr];
149 - (instancetype)initWithMessage:(GPBMessage *)message
150 typeURLPrefix:(NSString *)typeURLPrefix
151 error:(NSError **)errorPtr {
154 if (![self packWithMessage:message typeURLPrefix:typeURLPrefix error:errorPtr]) {
162 - (BOOL)packWithMessage:(GPBMessage *)message error:(NSError **)errorPtr {
163 return [self packWithMessage:message typeURLPrefix:kTypePrefixGoogleApisCom error:errorPtr];
166 - (BOOL)packWithMessage:(GPBMessage *)message
167 typeURLPrefix:(NSString *)typeURLPrefix
168 error:(NSError **)errorPtr {
169 NSString *fullName = [message descriptor].fullName;
170 if (fullName.length == 0) {
172 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
173 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
181 self.typeURL = BuildTypeURL(typeURLPrefix, fullName);
182 self.value = message.data;
186 - (GPBMessage *)unpackMessageClass:(Class)messageClass error:(NSError **)errorPtr {
187 return [self unpackMessageClass:messageClass extensionRegistry:nil error:errorPtr];
190 - (nullable GPBMessage *)unpackMessageClass:(Class)messageClass
191 extensionRegistry:(nullable id<GPBExtensionRegistry>)extensionRegistry
192 error:(NSError **)errorPtr {
193 NSString *fullName = [messageClass descriptor].fullName;
194 if (fullName.length == 0) {
196 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
197 code:GPBWellKnownTypesErrorCodeFailedToComputeTypeURL
203 NSString *expectedFullName = ParseTypeFromURL(self.typeURL);
204 if ((expectedFullName == nil) || ![expectedFullName isEqual:fullName]) {
206 *errorPtr = [NSError errorWithDomain:GPBWellKnownTypesErrorDomain
207 code:GPBWellKnownTypesErrorCodeTypeURLMismatch
213 return [messageClass parseFromData:self.value extensionRegistry:extensionRegistry error:errorPtr];