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