1 /* GENERATED SOURCE. DO NOT MODIFY. */ 2 // © 2017 and later: Unicode, Inc. and others. 3 // License & terms of use: http://www.unicode.org/copyright.html#License 4 package ohos.global.icu.impl.number; 5 6 import java.math.BigDecimal; 7 import java.math.BigInteger; 8 9 /** 10 * A DecimalQuantity with internal storage as a 64-bit BCD, with fallback to a byte array for numbers 11 * that don't fit into the standard BCD. 12 * @hide exposed on OHOS 13 */ 14 public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_AbstractBCD { 15 16 /** 17 * The BCD of the 16 digits of the number represented by this object. Every 4 bits of the long map to 18 * one digit. For example, the number "12345" in BCD is "0x12345". 19 * 20 * <p> 21 * Whenever bcd changes internally, {@link #compact()} must be called, except in special cases like 22 * setting the digit to zero. 23 */ 24 private byte[] bcdBytes; 25 26 private long bcdLong = 0L; 27 28 private boolean usingBytes = false; 29 30 @Override maxRepresentableDigits()31 public int maxRepresentableDigits() { 32 return Integer.MAX_VALUE; 33 } 34 DecimalQuantity_DualStorageBCD()35 public DecimalQuantity_DualStorageBCD() { 36 setBcdToZero(); 37 flags = 0; 38 } 39 DecimalQuantity_DualStorageBCD(long input)40 public DecimalQuantity_DualStorageBCD(long input) { 41 setToLong(input); 42 } 43 DecimalQuantity_DualStorageBCD(int input)44 public DecimalQuantity_DualStorageBCD(int input) { 45 setToInt(input); 46 } 47 DecimalQuantity_DualStorageBCD(double input)48 public DecimalQuantity_DualStorageBCD(double input) { 49 setToDouble(input); 50 } 51 DecimalQuantity_DualStorageBCD(BigInteger input)52 public DecimalQuantity_DualStorageBCD(BigInteger input) { 53 setToBigInteger(input); 54 } 55 DecimalQuantity_DualStorageBCD(BigDecimal input)56 public DecimalQuantity_DualStorageBCD(BigDecimal input) { 57 setToBigDecimal(input); 58 } 59 DecimalQuantity_DualStorageBCD(DecimalQuantity_DualStorageBCD other)60 public DecimalQuantity_DualStorageBCD(DecimalQuantity_DualStorageBCD other) { 61 copyFrom(other); 62 } 63 DecimalQuantity_DualStorageBCD(Number number)64 public DecimalQuantity_DualStorageBCD(Number number) { 65 // NOTE: Number type expansion happens both here 66 // and in NumberFormat.java 67 if (number instanceof Long) { 68 setToLong(number.longValue()); 69 } else if (number instanceof Integer) { 70 setToInt(number.intValue()); 71 } else if (number instanceof Float) { 72 setToDouble(number.doubleValue()); 73 } else if (number instanceof Double) { 74 setToDouble(number.doubleValue()); 75 } else if (number instanceof BigInteger) { 76 setToBigInteger((BigInteger) number); 77 } else if (number instanceof BigDecimal) { 78 setToBigDecimal((BigDecimal) number); 79 } else if (number instanceof ohos.global.icu.math.BigDecimal) { 80 setToBigDecimal(((ohos.global.icu.math.BigDecimal) number).toBigDecimal()); 81 } else { 82 throw new IllegalArgumentException( 83 "Number is of an unsupported type: " + number.getClass().getName()); 84 } 85 } 86 87 @Override createCopy()88 public DecimalQuantity createCopy() { 89 return new DecimalQuantity_DualStorageBCD(this); 90 } 91 92 @Override getDigitPos(int position)93 protected byte getDigitPos(int position) { 94 if (usingBytes) { 95 if (position < 0 || position >= precision) 96 return 0; 97 return bcdBytes[position]; 98 } else { 99 if (position < 0 || position >= 16) 100 return 0; 101 return (byte) ((bcdLong >>> (position * 4)) & 0xf); 102 } 103 } 104 105 @Override setDigitPos(int position, byte value)106 protected void setDigitPos(int position, byte value) { 107 assert position >= 0; 108 if (usingBytes) { 109 ensureCapacity(position + 1); 110 bcdBytes[position] = value; 111 } else if (position >= 16) { 112 switchStorage(); 113 ensureCapacity(position + 1); 114 bcdBytes[position] = value; 115 } else { 116 int shift = position * 4; 117 bcdLong = bcdLong & ~(0xfL << shift) | ((long) value << shift); 118 } 119 } 120 121 @Override shiftLeft(int numDigits)122 protected void shiftLeft(int numDigits) { 123 if (!usingBytes && precision + numDigits > 16) { 124 switchStorage(); 125 } 126 if (usingBytes) { 127 ensureCapacity(precision + numDigits); 128 int i = precision + numDigits - 1; 129 for (; i >= numDigits; i--) { 130 bcdBytes[i] = bcdBytes[i - numDigits]; 131 } 132 for (; i >= 0; i--) { 133 bcdBytes[i] = 0; 134 } 135 } else { 136 bcdLong <<= (numDigits * 4); 137 } 138 scale -= numDigits; 139 precision += numDigits; 140 } 141 142 @Override shiftRight(int numDigits)143 protected void shiftRight(int numDigits) { 144 if (usingBytes) { 145 int i = 0; 146 for (; i < precision - numDigits; i++) { 147 bcdBytes[i] = bcdBytes[i + numDigits]; 148 } 149 for (; i < precision; i++) { 150 bcdBytes[i] = 0; 151 } 152 } else { 153 bcdLong >>>= (numDigits * 4); 154 } 155 scale += numDigits; 156 precision -= numDigits; 157 } 158 159 @Override popFromLeft(int numDigits)160 protected void popFromLeft(int numDigits) { 161 assert numDigits <= precision; 162 if (usingBytes) { 163 int i = precision - 1; 164 for (; i >= precision - numDigits; i--) { 165 bcdBytes[i] = 0; 166 } 167 } else { 168 bcdLong &= (1L << ((precision - numDigits) * 4)) - 1; 169 } 170 precision -= numDigits; 171 } 172 173 @Override setBcdToZero()174 protected void setBcdToZero() { 175 if (usingBytes) { 176 bcdBytes = null; 177 usingBytes = false; 178 } 179 bcdLong = 0L; 180 scale = 0; 181 precision = 0; 182 isApproximate = false; 183 origDouble = 0; 184 origDelta = 0; 185 exponent = 0; 186 } 187 188 @Override readIntToBcd(int n)189 protected void readIntToBcd(int n) { 190 assert n != 0; 191 // ints always fit inside the long implementation. 192 long result = 0L; 193 int i = 16; 194 for (; n != 0; n /= 10, i--) { 195 result = (result >>> 4) + (((long) n % 10) << 60); 196 } 197 assert !usingBytes; 198 bcdLong = result >>> (i * 4); 199 scale = 0; 200 precision = 16 - i; 201 } 202 203 @Override readLongToBcd(long n)204 protected void readLongToBcd(long n) { 205 assert n != 0; 206 if (n >= 10000000000000000L) { 207 ensureCapacity(); 208 int i = 0; 209 for (; n != 0L; n /= 10L, i++) { 210 bcdBytes[i] = (byte) (n % 10); 211 } 212 assert usingBytes; 213 scale = 0; 214 precision = i; 215 } else { 216 long result = 0L; 217 int i = 16; 218 for (; n != 0L; n /= 10L, i--) { 219 result = (result >>> 4) + ((n % 10) << 60); 220 } 221 assert i >= 0; 222 assert !usingBytes; 223 bcdLong = result >>> (i * 4); 224 scale = 0; 225 precision = 16 - i; 226 } 227 } 228 229 @Override readBigIntegerToBcd(BigInteger n)230 protected void readBigIntegerToBcd(BigInteger n) { 231 assert n.signum() != 0; 232 ensureCapacity(); // allocate initial byte array 233 int i = 0; 234 for (; n.signum() != 0; i++) { 235 BigInteger[] temp = n.divideAndRemainder(BigInteger.TEN); 236 ensureCapacity(i + 1); 237 bcdBytes[i] = temp[1].byteValue(); 238 n = temp[0]; 239 } 240 scale = 0; 241 precision = i; 242 } 243 244 @Override bcdToBigDecimal()245 protected BigDecimal bcdToBigDecimal() { 246 if (usingBytes) { 247 // Converting to a string here is faster than doing BigInteger/BigDecimal arithmetic. 248 BigDecimal result = new BigDecimal(toNumberString()); 249 if (isNegative()) { 250 result = result.negate(); 251 } 252 return result; 253 } else { 254 long tempLong = 0L; 255 for (int shift = (precision - 1); shift >= 0; shift--) { 256 tempLong = tempLong * 10 + getDigitPos(shift); 257 } 258 BigDecimal result = BigDecimal.valueOf(tempLong); 259 // Test that the new scale fits inside the BigDecimal 260 long newScale = result.scale() + scale + exponent; 261 if (newScale <= Integer.MIN_VALUE) { 262 result = BigDecimal.ZERO; 263 } else { 264 result = result.scaleByPowerOfTen(scale + exponent); 265 } 266 if (isNegative()) { 267 result = result.negate(); 268 } 269 return result; 270 } 271 } 272 273 @Override compact()274 protected void compact() { 275 if (usingBytes) { 276 int delta = 0; 277 for (; delta < precision && bcdBytes[delta] == 0; delta++) 278 ; 279 if (delta == precision) { 280 // Number is zero 281 setBcdToZero(); 282 return; 283 } else { 284 // Remove trailing zeros 285 shiftRight(delta); 286 } 287 288 // Compute precision 289 int leading = precision - 1; 290 for (; leading >= 0 && bcdBytes[leading] == 0; leading--) 291 ; 292 precision = leading + 1; 293 294 // Switch storage mechanism if possible 295 if (precision <= 16) { 296 switchStorage(); 297 } 298 299 } else { 300 if (bcdLong == 0L) { 301 // Number is zero 302 setBcdToZero(); 303 return; 304 } 305 306 // Compact the number (remove trailing zeros) 307 int delta = Long.numberOfTrailingZeros(bcdLong) / 4; 308 bcdLong >>>= delta * 4; 309 scale += delta; 310 311 // Compute precision 312 precision = 16 - (Long.numberOfLeadingZeros(bcdLong) / 4); 313 } 314 } 315 316 /** Ensure that a byte array of at least 40 digits is allocated. */ ensureCapacity()317 private void ensureCapacity() { 318 ensureCapacity(40); 319 } 320 ensureCapacity(int capacity)321 private void ensureCapacity(int capacity) { 322 if (capacity == 0) 323 return; 324 int oldCapacity = usingBytes ? bcdBytes.length : 0; 325 if (!usingBytes) { 326 bcdBytes = new byte[capacity]; 327 } else if (oldCapacity < capacity) { 328 byte[] bcd1 = new byte[capacity * 2]; 329 System.arraycopy(bcdBytes, 0, bcd1, 0, oldCapacity); 330 bcdBytes = bcd1; 331 } 332 usingBytes = true; 333 } 334 335 /** Switches the internal storage mechanism between the 64-bit long and the byte array. */ switchStorage()336 private void switchStorage() { 337 if (usingBytes) { 338 // Change from bytes to long 339 bcdLong = 0L; 340 for (int i = precision - 1; i >= 0; i--) { 341 bcdLong <<= 4; 342 bcdLong |= bcdBytes[i]; 343 } 344 bcdBytes = null; 345 usingBytes = false; 346 } else { 347 // Change from long to bytes 348 ensureCapacity(); 349 for (int i = 0; i < precision; i++) { 350 bcdBytes[i] = (byte) (bcdLong & 0xf); 351 bcdLong >>>= 4; 352 } 353 assert usingBytes; 354 } 355 } 356 357 @Override copyBcdFrom(DecimalQuantity _other)358 protected void copyBcdFrom(DecimalQuantity _other) { 359 DecimalQuantity_DualStorageBCD other = (DecimalQuantity_DualStorageBCD) _other; 360 setBcdToZero(); 361 if (other.usingBytes) { 362 ensureCapacity(other.precision); 363 System.arraycopy(other.bcdBytes, 0, bcdBytes, 0, other.precision); 364 } else { 365 bcdLong = other.bcdLong; 366 } 367 } 368 369 /** 370 * Checks whether the bytes stored in this instance are all valid. For internal unit testing only. 371 * 372 * @return An error message if this instance is invalid, or null if this instance is healthy. 373 * @deprecated This API is for ICU internal use only. 374 * @hide draft / provisional / internal are hidden on OHOS 375 */ 376 @Deprecated checkHealth()377 public String checkHealth() { 378 if (usingBytes) { 379 if (bcdLong != 0) 380 return "Value in bcdLong but we are in byte mode"; 381 if (precision == 0) 382 return "Zero precision but we are in byte mode"; 383 if (precision > bcdBytes.length) 384 return "Precision exceeds length of byte array"; 385 if (getDigitPos(precision - 1) == 0) 386 return "Most significant digit is zero in byte mode"; 387 if (getDigitPos(0) == 0) 388 return "Least significant digit is zero in long mode"; 389 for (int i = 0; i < precision; i++) { 390 if (getDigitPos(i) >= 10) 391 return "Digit exceeding 10 in byte array"; 392 if (getDigitPos(i) < 0) 393 return "Digit below 0 in byte array"; 394 } 395 for (int i = precision; i < bcdBytes.length; i++) { 396 if (getDigitPos(i) != 0) 397 return "Nonzero digits outside of range in byte array"; 398 } 399 } else { 400 if (bcdBytes != null) { 401 for (int i = 0; i < bcdBytes.length; i++) { 402 if (bcdBytes[i] != 0) 403 return "Nonzero digits in byte array but we are in long mode"; 404 } 405 } 406 if (precision == 0 && bcdLong != 0) 407 return "Value in bcdLong even though precision is zero"; 408 if (precision > 16) 409 return "Precision exceeds length of long"; 410 if (precision != 0 && getDigitPos(precision - 1) == 0) 411 return "Most significant digit is zero in long mode"; 412 if (precision != 0 && getDigitPos(0) == 0) 413 return "Least significant digit is zero in long mode"; 414 for (int i = 0; i < precision; i++) { 415 if (getDigitPos(i) >= 10) 416 return "Digit exceeding 10 in long"; 417 if (getDigitPos(i) < 0) 418 return "Digit below 0 in long (?!)"; 419 } 420 for (int i = precision; i < 16; i++) { 421 if (getDigitPos(i) != 0) 422 return "Nonzero digits outside of range in long"; 423 } 424 } 425 426 return null; 427 } 428 429 /** 430 * Checks whether this {@link DecimalQuantity_DualStorageBCD} is using its internal byte array 431 * storage mechanism. 432 * 433 * @return true if an internal byte array is being used; false if a long is being used. 434 * @deprecated This API is ICU internal only. 435 * @hide draft / provisional / internal are hidden on OHOS 436 */ 437 @Deprecated isUsingBytes()438 public boolean isUsingBytes() { 439 return usingBytes; 440 } 441 442 @Override toString()443 public String toString() { 444 return String.format("<DecimalQuantity %d:%d %s %s%s>", 445 lReqPos, 446 rReqPos, 447 (usingBytes ? "bytes" : "long"), 448 (isNegative() ? "-" : ""), 449 toNumberString()); 450 } 451 toNumberString()452 private String toNumberString() { 453 StringBuilder sb = new StringBuilder(); 454 if (usingBytes) { 455 if (precision == 0) { 456 sb.append('0'); 457 } 458 for (int i = precision - 1; i >= 0; i--) { 459 sb.append(bcdBytes[i]); 460 } 461 } else { 462 sb.append(Long.toHexString(bcdLong)); 463 } 464 sb.append("E"); 465 sb.append(scale); 466 return sb.toString(); 467 } 468 } 469