RLMUtil.hpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2014 Realm Inc.
  4. //
  5. // Licensed under the Apache License, Version 2.0 (the "License");
  6. // you may not use this file except in compliance with the License.
  7. // You may obtain a copy of the License at
  8. //
  9. // http://www.apache.org/licenses/LICENSE-2.0
  10. //
  11. // Unless required by applicable law or agreed to in writing, software
  12. // distributed under the License is distributed on an "AS IS" BASIS,
  13. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. // See the License for the specific language governing permissions and
  15. // limitations under the License.
  16. //
  17. ////////////////////////////////////////////////////////////////////////////
  18. #import <Realm/RLMConstants.h>
  19. #import <Realm/RLMOptionalBase.h>
  20. #import <objc/runtime.h>
  21. #import <realm/array.hpp>
  22. #import <realm/binary_data.hpp>
  23. #import <realm/string_data.hpp>
  24. #import <realm/timestamp.hpp>
  25. #import <realm/util/file.hpp>
  26. namespace realm {
  27. class Decimal128;
  28. class Mixed;
  29. class RealmFileException;
  30. }
  31. @class RLMObjectSchema;
  32. @class RLMProperty;
  33. __attribute__((format(NSString, 1, 2)))
  34. NSException *RLMException(NSString *fmt, ...);
  35. NSException *RLMException(std::exception const& exception);
  36. NSError *RLMMakeError(RLMError code, std::exception const& exception);
  37. NSError *RLMMakeError(RLMError code, const realm::util::File::AccessError&);
  38. NSError *RLMMakeError(RLMError code, const realm::RealmFileException&);
  39. NSError *RLMMakeError(std::system_error const& exception);
  40. void RLMSetErrorOrThrow(NSError *error, NSError **outError);
  41. // returns if the object can be inserted as the given type
  42. BOOL RLMIsObjectValidForProperty(id obj, RLMProperty *prop);
  43. // throw an exception if the object is not a valid value for the property
  44. void RLMValidateValueForProperty(id obj, RLMObjectSchema *objectSchema,
  45. RLMProperty *prop, bool validateObjects=false);
  46. BOOL RLMValidateValue(id value, RLMPropertyType type, bool optional, bool array,
  47. NSString *objectClassName);
  48. void RLMThrowTypeError(id obj, RLMObjectSchema *objectSchema, RLMProperty *prop);
  49. // gets default values for the given schema (+defaultPropertyValues)
  50. // merges with native property defaults if Swift class
  51. NSDictionary *RLMDefaultValuesForObjectSchema(RLMObjectSchema *objectSchema);
  52. BOOL RLMIsDebuggerAttached();
  53. BOOL RLMIsRunningInPlayground();
  54. // C version of isKindOfClass
  55. static inline BOOL RLMIsKindOfClass(Class class1, Class class2) {
  56. while (class1) {
  57. if (class1 == class2) return YES;
  58. class1 = class_getSuperclass(class1);
  59. }
  60. return NO;
  61. }
  62. template<typename T>
  63. static inline T *RLMDynamicCast(__unsafe_unretained id obj) {
  64. if ([obj isKindOfClass:[T class]]) {
  65. return obj;
  66. }
  67. return nil;
  68. }
  69. static inline id RLMCoerceToNil(__unsafe_unretained id obj) {
  70. if (static_cast<id>(obj) == NSNull.null) {
  71. return nil;
  72. }
  73. else if (__unsafe_unretained auto optional = RLMDynamicCast<RLMOptionalBase>(obj)) {
  74. return RLMCoerceToNil(RLMGetOptional(optional));
  75. }
  76. return obj;
  77. }
  78. template<typename T>
  79. static inline T RLMCoerceToNil(__unsafe_unretained T obj) {
  80. return RLMCoerceToNil(static_cast<id>(obj));
  81. }
  82. id<NSFastEnumeration> RLMAsFastEnumeration(id obj);
  83. bool RLMIsSwiftObjectClass(Class cls);
  84. // String conversion utilities
  85. static inline NSString * RLMStringDataToNSString(realm::StringData stringData) {
  86. static_assert(sizeof(NSUInteger) >= sizeof(size_t),
  87. "Need runtime overflow check for size_t to NSUInteger conversion");
  88. if (stringData.is_null()) {
  89. return nil;
  90. }
  91. else {
  92. return [[NSString alloc] initWithBytes:stringData.data()
  93. length:stringData.size()
  94. encoding:NSUTF8StringEncoding];
  95. }
  96. }
  97. static inline realm::StringData RLMStringDataWithNSString(__unsafe_unretained NSString *const string) {
  98. static_assert(sizeof(size_t) >= sizeof(NSUInteger),
  99. "Need runtime overflow check for NSUInteger to size_t conversion");
  100. return realm::StringData(string.UTF8String,
  101. [string lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
  102. }
  103. // Binary conversion utilities
  104. static inline NSData *RLMBinaryDataToNSData(realm::BinaryData binaryData) {
  105. return binaryData ? [NSData dataWithBytes:binaryData.data() length:binaryData.size()] : nil;
  106. }
  107. static inline realm::BinaryData RLMBinaryDataForNSData(__unsafe_unretained NSData *const data) {
  108. // this is necessary to ensure that the empty NSData isn't treated by core as the null realm::BinaryData
  109. // because data.bytes == 0 when data.length == 0
  110. // the casting bit ensures that we create a data with a non-null pointer
  111. auto bytes = static_cast<const char *>(data.bytes) ?: static_cast<char *>((__bridge void *)data);
  112. return realm::BinaryData(bytes, data.length);
  113. }
  114. // Date conversion utilities
  115. // These use the reference date and shift the seconds rather than just getting
  116. // the time interval since the epoch directly to avoid losing sub-second precision
  117. static inline NSDate *RLMTimestampToNSDate(realm::Timestamp ts) NS_RETURNS_RETAINED {
  118. if (ts.is_null())
  119. return nil;
  120. auto timeInterval = ts.get_seconds() - NSTimeIntervalSince1970 + ts.get_nanoseconds() / 1'000'000'000.0;
  121. return [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:timeInterval];
  122. }
  123. static inline realm::Timestamp RLMTimestampForNSDate(__unsafe_unretained NSDate *const date) {
  124. if (!date)
  125. return {};
  126. auto timeInterval = date.timeIntervalSinceReferenceDate;
  127. if (isnan(timeInterval))
  128. return {0, 0}; // Arbitrary choice
  129. // Clamp dates that we can't represent as a Timestamp to the maximum value
  130. if (timeInterval >= std::numeric_limits<int64_t>::max() - NSTimeIntervalSince1970)
  131. return {std::numeric_limits<int64_t>::max(), 1'000'000'000 - 1};
  132. if (timeInterval - NSTimeIntervalSince1970 < std::numeric_limits<int64_t>::min())
  133. return {std::numeric_limits<int64_t>::min(), -1'000'000'000 + 1};
  134. auto seconds = static_cast<int64_t>(timeInterval);
  135. auto nanoseconds = static_cast<int32_t>((timeInterval - seconds) * 1'000'000'000.0);
  136. seconds += static_cast<int64_t>(NSTimeIntervalSince1970);
  137. // Seconds and nanoseconds have to have the same sign
  138. if (nanoseconds < 0 && seconds > 0) {
  139. nanoseconds += 1'000'000'000;
  140. --seconds;
  141. }
  142. return {seconds, nanoseconds};
  143. }
  144. static inline NSUInteger RLMConvertNotFound(size_t index) {
  145. return index == realm::not_found ? NSNotFound : index;
  146. }
  147. static inline void RLMNSStringToStdString(std::string &out, NSString *in) {
  148. if (!in)
  149. return;
  150. out.resize([in maximumLengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
  151. if (out.empty()) {
  152. return;
  153. }
  154. NSUInteger size = out.size();
  155. [in getBytes:&out[0]
  156. maxLength:size
  157. usedLength:&size
  158. encoding:NSUTF8StringEncoding
  159. options:0 range:{0, in.length} remainingRange:nullptr];
  160. out.resize(size);
  161. }
  162. id RLMMixedToObjc(realm::Mixed const& value);
  163. realm::Decimal128 RLMObjcToDecimal128(id value);
  164. // Given a bundle identifier, return the base directory on the disk within which Realm database and support files should
  165. // be stored.
  166. NSString *RLMDefaultDirectoryForBundleIdentifier(NSString *bundleIdentifier);
  167. // Get a NSDateFormatter for ISO8601-formatted strings
  168. NSDateFormatter *RLMISO8601Formatter();
  169. template<typename Fn>
  170. static auto RLMTranslateError(Fn&& fn) {
  171. try {
  172. return fn();
  173. }
  174. catch (std::exception const& e) {
  175. @throw RLMException(e);
  176. }
  177. }