• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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