GTLRDuration.m 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /* Copyright (c) 2016 Google Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. //#import <GoogleAPIClientForREST/GTLRDuration.h>
  16. #import "GTLRDuration.h"
  17. #import "GTLRDefines.h"
  18. static const int32_t kNanosPerMillisecond = 1000000;
  19. static const int32_t kNanosPerMicrosecond = 1000;
  20. static const int32_t kNanosPerSecond = 1000000000;
  21. static int32_t IntPow10(int x) {
  22. int32_t result = 1;
  23. for (int i = 0; i < x; ++i) {
  24. result *= 10;
  25. }
  26. return result;
  27. }
  28. @implementation GTLRDuration
  29. @dynamic timeInterval;
  30. @synthesize seconds = _seconds,
  31. nanos = _nanos,
  32. jsonString = _jsonString;
  33. + (instancetype)durationWithSeconds:(int64_t)seconds nanos:(int32_t)nanos {
  34. if (seconds < 0) {
  35. if (nanos > 0) {
  36. // secs was -, nanos was +
  37. return nil;
  38. }
  39. } else if (seconds > 0) {
  40. if (nanos < 0) {
  41. // secs was +, nanos was -
  42. return nil;
  43. }
  44. }
  45. if ((nanos <= -kNanosPerSecond) || (nanos >= kNanosPerSecond)) {
  46. // more than a seconds worth
  47. return nil;
  48. }
  49. return [[self alloc] initWithSeconds:seconds nanos:nanos jsonString:NULL];
  50. }
  51. + (instancetype)durationWithJSONString:(NSString *)jsonString {
  52. // It has to end in "s", so it needs >1 character.
  53. if (jsonString.length <= 1) {
  54. return nil;
  55. }
  56. static NSCharacterSet *gNumberSet;
  57. static dispatch_once_t onceToken;
  58. dispatch_once(&onceToken, ^{
  59. gNumberSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789"];
  60. });
  61. NSScanner* scanner = [NSScanner scannerWithString:jsonString];
  62. // There should be no whitespace, so no skip characters.
  63. [scanner setCharactersToBeSkipped:nil];
  64. // Can start with a '-'.
  65. BOOL isNeg = [scanner scanString:@"-" intoString:NULL];
  66. int64_t seconds;
  67. if (![scanner scanLongLong:&seconds]) {
  68. return nil;
  69. }
  70. // Since the sign was manually scanned, seconds should be positive
  71. // (i.e. no "--#" in the put).
  72. if (seconds < 0) {
  73. return nil;
  74. }
  75. // See if it has a ".[nanos]". Spec seems to say it is required, but play
  76. // it safe and make it optional.
  77. int32_t nanos = 0;
  78. if ([scanner scanString:@"." intoString:NULL]) {
  79. NSString *nanosStr;
  80. if (![scanner scanCharactersFromSet:gNumberSet intoString:&nanosStr]) {
  81. return nil;
  82. }
  83. // Ensure not too many digits (also ensure it is within range).
  84. if (nanosStr.length > 9) {
  85. return nil;
  86. }
  87. // Can use NSString's intValue since the character set was controlled.
  88. nanos = [nanosStr intValue];
  89. // Scale based on length.
  90. nanos *= IntPow10(9 - (int)nanosStr.length);
  91. }
  92. // And must have the final 's'.
  93. if (![scanner scanString:@"s" intoString:NULL]) {
  94. return nil;
  95. }
  96. // Better be the end...
  97. if (![scanner isAtEnd]) {
  98. return nil;
  99. }
  100. if (isNeg) {
  101. seconds = -seconds;
  102. nanos = -nanos;
  103. }
  104. // Pass on the json string so it will be reflected back out as it came in
  105. // (incase it had a different number of digits, etc).
  106. return [[self alloc] initWithSeconds:seconds
  107. nanos:nanos
  108. jsonString:jsonString];
  109. }
  110. + (instancetype)durationWithTimeInterval:(NSTimeInterval)timeInterval {
  111. NSTimeInterval seconds;
  112. NSTimeInterval nanos = modf(timeInterval, &seconds);
  113. nanos *= (NSTimeInterval)kNanosPerSecond;
  114. return [[self alloc] initWithSeconds:(int64_t)seconds
  115. nanos:(int32_t)nanos
  116. jsonString:NULL];
  117. }
  118. - (instancetype)init {
  119. return [self initWithSeconds:0 nanos:0 jsonString:NULL];
  120. }
  121. - (instancetype)initWithSeconds:(int64_t)seconds
  122. nanos:(int32_t)nanos
  123. jsonString:(NSString *)jsonString {
  124. self = [super init];
  125. if (self) {
  126. // Sanity asserts, the class methods should make sure this doesn't happen.
  127. GTLR_DEBUG_ASSERT((((seconds <= 0) && (nanos <= 0)) ||
  128. ((seconds >= 0) && (nanos >= 0))),
  129. @"Seconds and nanos must have the same sign (%lld & %d)",
  130. seconds, nanos);
  131. GTLR_DEBUG_ASSERT(((nanos < kNanosPerSecond) &&
  132. (nanos > -kNanosPerSecond)),
  133. @"Nanos is a second or more (%d)", nanos);
  134. _seconds = seconds;
  135. _nanos = nanos;
  136. if (jsonString.length) {
  137. _jsonString = [jsonString copy];
  138. } else {
  139. // Based off the JSON serialization code in protocol buffers
  140. // ( https://github.com/google/protobuf/ ).
  141. NSString *sign = @"";
  142. if ((seconds < 0) || (nanos < 0)) {
  143. sign = @"-";
  144. seconds = -seconds;
  145. nanos = -nanos;
  146. }
  147. int nanoDigts;
  148. int32_t nanoDivider;
  149. if (nanos % kNanosPerMillisecond == 0) {
  150. nanoDigts = 3;
  151. nanoDivider = kNanosPerMillisecond;
  152. } else if (nanos % kNanosPerMicrosecond == 0) {
  153. nanoDigts = 6;
  154. nanoDivider = kNanosPerMicrosecond;
  155. } else {
  156. nanoDigts = 9;
  157. nanoDivider = 1;
  158. }
  159. _jsonString = [NSString stringWithFormat:@"%@%lld.%0*ds",
  160. sign, seconds, nanoDigts, (nanos / nanoDivider)];
  161. }
  162. }
  163. return self;
  164. }
  165. - (NSTimeInterval)timeInterval {
  166. NSTimeInterval result = self.seconds;
  167. result += (NSTimeInterval)self.nanos / (NSTimeInterval)kNanosPerSecond;
  168. return result;
  169. }
  170. - (id)copyWithZone:(NSZone *)zone {
  171. // Object is immutable
  172. return self;
  173. }
  174. - (BOOL)isEqual:(GTLRDuration *)other {
  175. if (self == other) return YES;
  176. if (![other isKindOfClass:[GTLRDuration class]]) return NO;
  177. BOOL result = ((self.seconds == other.seconds) &&
  178. (self.nanos == other.nanos));
  179. return result;
  180. }
  181. - (NSUInteger)hash {
  182. NSUInteger result = (NSUInteger)((self.seconds * 13) + self.nanos);
  183. return result;
  184. }
  185. - (NSString *)description {
  186. return [NSString stringWithFormat:@"%@ %p: {%@}",
  187. [self class], self, self.jsonString];
  188. }
  189. @end