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