RLMDecimal128.mm 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. ////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2020 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 "RLMDecimal128_Private.hpp"
  19. #import "RLMUtil.hpp"
  20. #import <realm/decimal128.hpp>
  21. // Swift's obj-c bridging does not support making an obj-c defined class conform
  22. // to Decodable, so we need a Swift-defined subclass for that. This means that
  23. // when Realm Swift is being used, we need to produce objects of that type rather
  24. // than our obj-c defined type. objc_runtime_visible marks the type as being
  25. // visbile only to the obj-c runtime and not the linker, which means that it'll
  26. // be `nil` at runtime rather than being a linker error if it's not defined, and
  27. // valid if it happens to be defined by some other library (i.e. Realm Swift).
  28. //
  29. // At the point where the objects are being allocated we generally don't have
  30. // any good way of knowing whether or not it's going to end up being used by
  31. // Swift, so we just switch to the subclass unconditionally if the subclass
  32. // exists. This shouldn't have any impact on obj-c code other than a small
  33. // performance hit.
  34. [[clang::objc_runtime_visible]]
  35. @interface RealmSwiftDecimal128 : RLMDecimal128
  36. @end
  37. @implementation RLMDecimal128 {
  38. realm::Decimal128 _value;
  39. }
  40. - (instancetype)init {
  41. if (self = [super init]) {
  42. if (auto cls = [RealmSwiftDecimal128 class]; cls && cls != self.class) {
  43. object_setClass(self, cls);
  44. }
  45. }
  46. return self;
  47. }
  48. - (instancetype)initWithDecimal128:(realm::Decimal128)value {
  49. if ((self = [self init])) {
  50. _value = value;
  51. }
  52. return self;
  53. }
  54. - (instancetype)initWithValue:(id)value {
  55. if ((self = [self init])) {
  56. _value = RLMObjcToDecimal128(value);
  57. }
  58. return self;
  59. }
  60. - (instancetype)initWithNumber:(NSNumber *)number {
  61. if ((self = [self init])) {
  62. _value = RLMObjcToDecimal128(number);
  63. }
  64. return self;
  65. }
  66. - (instancetype)initWithString:(NSString *)string error:(__unused NSError **)error {
  67. if ((self = [self init])) {
  68. _value = realm::Decimal128(string.UTF8String);
  69. }
  70. return self;
  71. }
  72. + (instancetype)decimalWithNumber:(NSNumber *)number {
  73. return [[self alloc] initWithNumber:number];
  74. }
  75. + (instancetype)decimalWithNSDecimal:(NSDecimalNumber *)number {
  76. return [[self alloc] initWithString:number.stringValue error:nil];
  77. }
  78. - (id)copyWithZone:(NSZone *)zone {
  79. RLMDecimal128 *copy = [[self.class allocWithZone:zone] init];
  80. copy->_value = _value;
  81. return copy;
  82. }
  83. - (realm::Decimal128)decimal128Value {
  84. return _value;
  85. }
  86. - (BOOL)isEqual:(id)object {
  87. if (auto decimal128 = RLMDynamicCast<RLMDecimal128>(object)) {
  88. return _value == decimal128->_value;
  89. }
  90. if (auto number = RLMDynamicCast<NSNumber>(object)) {
  91. return _value == RLMObjcToDecimal128(number);
  92. }
  93. return NO;
  94. }
  95. - (NSUInteger)hash {
  96. return @(self.doubleValue).hash;
  97. }
  98. - (NSString *)description {
  99. return self.stringValue;
  100. }
  101. - (NSComparisonResult)compare:(RLMDecimal128 *)other {
  102. return static_cast<NSComparisonResult>(_value.compare(other->_value));
  103. }
  104. - (double)doubleValue {
  105. return [NSDecimalNumber decimalNumberWithDecimal:self.decimalValue].doubleValue;
  106. }
  107. - (NSDecimal)decimalValue {
  108. NSDecimal ret;
  109. [[[NSScanner alloc] initWithString:@(_value.to_string().c_str())] scanDecimal:&ret];
  110. return ret;
  111. }
  112. - (NSString *)stringValue {
  113. auto str = _value.to_string();
  114. // If there's a decimal point, trim trailing zeroes
  115. auto decimal_pos = str.find('.');
  116. if (decimal_pos != std::string::npos) {
  117. // Look specifically at the range between the decimal point and the E
  118. // if it's present, and the rest of the string if not
  119. std::string_view sv = str;
  120. auto e_pos = str.find('E', decimal_pos);
  121. if (e_pos != std::string::npos) {
  122. sv = sv.substr(0, e_pos);
  123. }
  124. // Remove everything between the character after the final non-zero
  125. // and the end of the string (or the E)
  126. auto final_non_zero = sv.find_last_not_of('0');
  127. REALM_ASSERT(final_non_zero != std::string::npos);
  128. if (final_non_zero == decimal_pos) {
  129. // Also drop the decimal if there's no non-zero digits after it
  130. --final_non_zero;
  131. }
  132. str.erase(final_non_zero + 1, sv.size() - final_non_zero - 1);
  133. }
  134. return @(str.c_str());
  135. }
  136. - (BOOL)isNaN {
  137. return _value.is_nan();
  138. }
  139. - (RLMDecimal128 *)magnitude {
  140. auto result = realm::Decimal128(abs(self.doubleValue));
  141. return [[RLMDecimal128 alloc] initWithDecimal128:result];
  142. }
  143. - (void)negate {
  144. _value = realm::Decimal128(-self.doubleValue);
  145. }
  146. + (RLMDecimal128 *)minimumDecimalNumber {
  147. return [[RLMDecimal128 alloc] initWithDecimal128:std::numeric_limits<realm::Decimal128>::lowest()];
  148. }
  149. + (RLMDecimal128 *)maximumDecimalNumber {
  150. return [[RLMDecimal128 alloc] initWithDecimal128:std::numeric_limits<realm::Decimal128>::max()];
  151. }
  152. - (RLMDecimal128 *)decimalNumberByAdding:(RLMDecimal128 *)decimalNumber {
  153. auto rhs = RLMObjcToDecimal128(decimalNumber);
  154. return [[RLMDecimal128 alloc] initWithDecimal128:_value+rhs];
  155. }
  156. - (RLMDecimal128 *)decimalNumberByDividingBy:(RLMDecimal128 *)decimalNumber {
  157. auto rhs = RLMObjcToDecimal128(decimalNumber);
  158. return [[RLMDecimal128 alloc] initWithDecimal128:_value/rhs];
  159. }
  160. - (RLMDecimal128 *)decimalNumberBySubtracting:(RLMDecimal128 *)decimalNumber {
  161. auto rhs = RLMObjcToDecimal128(decimalNumber);
  162. return [[RLMDecimal128 alloc] initWithDecimal128:_value-rhs];
  163. }
  164. - (RLMDecimal128 *)decimalNumberByMultiplyingBy:(RLMDecimal128 *)decimalNumber {
  165. auto rhs = RLMObjcToDecimal128(decimalNumber);
  166. return [[RLMDecimal128 alloc] initWithDecimal128:_value*rhs];
  167. }
  168. - (BOOL)isGreaterThan:(RLMDecimal128 *)decimalNumber {
  169. auto rhs = RLMObjcToDecimal128(decimalNumber);
  170. return _value > rhs;
  171. }
  172. - (BOOL)isGreaterThanOrEqualTo:(RLMDecimal128 *)decimalNumber {
  173. auto rhs = RLMObjcToDecimal128(decimalNumber);
  174. return _value >= rhs;
  175. }
  176. - (BOOL)isLessThan:(RLMDecimal128 *)decimalNumber {
  177. auto rhs = RLMObjcToDecimal128(decimalNumber);
  178. return _value < rhs;
  179. }
  180. - (BOOL)isLessThanOrEqualTo:(RLMDecimal128 *)decimalNumber {
  181. auto rhs = RLMObjcToDecimal128(decimalNumber);
  182. return _value <= rhs;
  183. }
  184. @end