Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/DecimalData.java
12558 views
1
/*[INCLUDE-IF DAA]*/
2
/*******************************************************************************
3
* Copyright (c) 2013, 2021 IBM Corp. and others
4
*
5
* This program and the accompanying materials are made available under
6
* the terms of the Eclipse Public License 2.0 which accompanies this
7
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
8
* or the Apache License, Version 2.0 which accompanies this distribution and
9
* is available at https://www.apache.org/licenses/LICENSE-2.0.
10
*
11
* This Source Code may also be made available under the following
12
* Secondary Licenses when the conditions for such availability set
13
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
14
* General Public License, version 2 with the GNU Classpath
15
* Exception [1] and GNU General Public License, version 2 with the
16
* OpenJDK Assembly Exception [2].
17
*
18
* [1] https://www.gnu.org/software/classpath/license.html
19
* [2] http://openjdk.java.net/legal/assembly-exception.html
20
*
21
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
22
*******************************************************************************/
23
package com.ibm.dataaccess;
24
25
import java.math.BigDecimal;
26
import java.math.BigInteger;
27
import java.util.Arrays;
28
29
import com.ibm.dataaccess.ByteArrayMarshaller;
30
import com.ibm.dataaccess.ByteArrayUnmarshaller;
31
import com.ibm.dataaccess.CommonData;
32
import com.ibm.dataaccess.PackedDecimal;
33
34
/**
35
* Routines to convert between decimal data types stored in byte arrays and Java binary types.
36
*
37
* <p>
38
* All the converter routines require the precision of the decimal value to convert, which represents the number of
39
* decimal digits in the decimal value, not including the sign.
40
* </p>
41
*
42
* <p>
43
* Unicode Decimal values can be represented as either a char array or as a byte array where every Unicode character is
44
* represented by a pair of adjacent bytes.
45
* </p>
46
*
47
* <p>
48
* For embedded sign nibbles (4 bit integers representing values between <code>0x0</code> and <code>0xF</code>
49
* inclusive) in External Decimal or Packed Decimal data, <code>0xB</code> and <code>0xD</code> represent a negative
50
* sign. All other sign nibble values represent a positive sign. For operations that produce an External Decimal or
51
* Packed Decimal result, the Data Access Accelerator library inserts the preferred positive sign code of
52
* <code>0xC</code> if the result is positive, and a preferred negative sign code of <code>0xD</code> if the result is
53
* negative. All values between <code>0x0</code> and <code>0xF</code> inclusive are interpreted as valid sign codes.
54
* </p>
55
*
56
* <p>
57
* This library has full support for signed integers but only limited support for scale points (decimals). Scale points
58
* and other unrecognized characters found in input External, Unicode, or Packed Decimal byte arrays are not supported,
59
* and may cause IllegalArgumentExceptions or undefined results. BigDecimal <i>inputs</i> will have scale ignored (i.e.
60
* -1.23 will be interpreted as -123). When converting to BigDecimal (as <i>output</i>), a scale value may be explicitly
61
* specified as a separate parameter.
62
* </p>
63
*
64
* @author IBM
65
* @version $Revision$ on $Date$
66
*/
67
public final class DecimalData
68
{
69
/**
70
* External Decimal data format where each byte is an EBCDIC character representing a decimal digit, the sign is
71
* encoded in the top nibble of the last byte.
72
*/
73
public static final int EBCDIC_SIGN_EMBEDDED_TRAILING = 1;
74
75
/**
76
* External Decimal data format where each byte is an EBCDIC character representing a decimal digit, the sign is
77
* encoded in the top nibble of the first byte.
78
*/
79
public static final int EBCDIC_SIGN_EMBEDDED_LEADING = 2;
80
81
/**
82
* External Decimal data format where each byte is an EBCDIC character representing a decimal digit, the sign is
83
* encoded in a separate byte that comes after the last byte of the number.
84
*/
85
public static final int EBCDIC_SIGN_SEPARATE_TRAILING = 3;
86
87
/**
88
* External Decimal data format where each byte is an EBCDIC character representing a decimal digit, the sign is
89
* encoded in a separate byte that comes before the first byte of the number.
90
*/
91
public static final int EBCDIC_SIGN_SEPARATE_LEADING = 4;
92
93
/**
94
* Unicode Decimal data format where each digit is a Unicode character, there is no sign.
95
*/
96
public static final int UNICODE_UNSIGNED = 5;
97
98
/**
99
* Unicode Decimal data format where each digit is a Unicode character, the sign is stored in the first character.
100
*/
101
public static final int UNICODE_SIGN_SEPARATE_LEADING = 6;
102
103
/**
104
* Unicode Decimal data format where each digit is a Unicode character, the sign is stored in the last character.
105
*/
106
public static final int UNICODE_SIGN_SEPARATE_TRAILING = 7;
107
108
/**
109
* External Decimal format for positive separate sign
110
*/
111
private static final byte EBCDIC_SIGN_POSITIVE = 0x4E;
112
113
/**
114
* External Decimal format for negative separate sign
115
*/
116
private static final byte EBCDIC_SIGN_NEGATIVE = 0x60;
117
118
/**
119
* External Decimal High Nibble Mask
120
*/
121
private static final byte EXTERNAL_HIGH_MASK = (byte) 0xF0;
122
123
private static final byte UNICODE_HIGH_MASK = (byte) 0x30;
124
125
static final int EXTERNAL_DECIMAL_MIN = 1;
126
127
static final int EXTERNAL_DECIMAL_MAX = 4;
128
129
private static final int EBCDIC_MIN = 1;
130
131
private static final int EBCDIC_MAX = 4;
132
133
private static final int UNICODE_MIN = 5;
134
135
private static final int UNICODE_MAX = 7;
136
137
private static byte[] PD2EDTranslationTable;
138
private static byte[] ED2UDTranslationTable;
139
140
private static char UNICODE_SIGN_MINUS = '-';
141
private static char UNICODE_SIGN_PLUS = '+';
142
private static char UNICODE_ZERO = '0';
143
144
private static final boolean JIT_INTRINSICS_ENABLED = false;
145
146
static {
147
PD2EDTranslationTable = new byte[512];
148
ED2UDTranslationTable = new byte[256];
149
150
Arrays.fill(PD2EDTranslationTable, (byte) 0);
151
Arrays.fill(ED2UDTranslationTable, (byte) 0);
152
153
for (int tenDigit = 0; tenDigit < 10; ++tenDigit)
154
for (int oneDigit = 0; oneDigit < 10; ++oneDigit)
155
{
156
int pdValue = tenDigit * 16 + oneDigit;
157
// ED tenDigit
158
PD2EDTranslationTable[pdValue*2 ] = (byte)(0xF0 | tenDigit);
159
PD2EDTranslationTable[pdValue*2+1] = (byte)(0xF0 | oneDigit);
160
}
161
162
for (int digit = 0; digit < 10; ++digit)
163
{
164
int edValue = (0xF0 | digit);
165
ED2UDTranslationTable[edValue + 1] = (byte)(0x30 | digit);
166
}
167
168
BigDecimal dummy = new BigDecimal("0");
169
170
171
}
172
173
// Private constructor, class contains only static methods.
174
private DecimalData() {
175
}
176
177
private static final int PACKED_BYTES_LENGTH = 33;
178
// Pre-compute all of the possible values for one byte of a Packed Decimal
179
private static final byte[] PACKED_BYTES = new byte[200];
180
static {
181
int i = 100;
182
for (int j = 0; j < 100; j++, i--) {
183
int low = i % 10;
184
int high = (i / 10) % 10;
185
PACKED_BYTES[j] = (byte) ((high << 4) + low);
186
}
187
i = 0;
188
for (int j = 100; j < 200; j++, i++) {
189
int low = i % 10;
190
int high = (i / 10) % 10;
191
PACKED_BYTES[j] = (byte) ((high << 4) + low);
192
}
193
}
194
195
/**
196
* This method is recognized by the JIT. The ILGenerator and Walker will replace this method with an appropriate
197
* iconst bytecode value corresponding to whether or not the architecture supports DAA JIT intrinsics. Currently
198
* the JIT will generate an iconst 1 if and only if we are executing under zOS.
199
*
200
* @return true if DAA JIT intrinsics are enabled on the current platform, false otherwise
201
*
202
*/
203
private final static boolean JITIntrinsicsEnabled()
204
{
205
return JIT_INTRINSICS_ENABLED;
206
}
207
208
// Binary search on the number of digits in value
209
private static int numDigits(int value)
210
{
211
value = (value == Integer.MIN_VALUE) ? Integer.MAX_VALUE : Math.abs(value);
212
213
return (value >= 10000) ?
214
(value >= 10000000) ?
215
(value >= 100000000) ?
216
(value >= 1000000000) ? 10 : 9 : 8 :
217
(value >= 100000) ?
218
(value >= 1000000) ? 7 : 6 : 5 :
219
(value >= 100) ?
220
(value >= 1000) ? 4 : 3 :
221
(value >= 10) ? 2 : 1;
222
}
223
224
// Binary search on the number of digits in value
225
private static int numDigits(long value)
226
{
227
value = value == Long.MIN_VALUE ? Long.MAX_VALUE : Math.abs(value);
228
229
return (value >= 1000000000L) ?
230
(value >= 100000000000000L) ?
231
(value >= 10000000000000000L) ?
232
(value >= 100000000000000000L) ?
233
(value >= 1000000000000000000L) ? 19 : 18 : 17 :
234
(value >= 1000000000000000L) ? 16 : 15 :
235
(value >= 100000000000L) ?
236
(value >= 1000000000000L) ?
237
(value >= 10000000000000L) ? 14 : 13 : 12 :
238
(value >= 10000000000L) ? 11 : 10 :
239
(value >= 10000L) ?
240
(value >= 1000000L) ?
241
(value >= 10000000L) ?
242
(value >= 100000000L) ? 9 : 8 : 7 :
243
(value >= 100000L) ? 6 : 5 :
244
(value >= 100L) ?
245
(value >= 1000L) ? 4 : 3 :
246
(value >= 10L) ? 2 : 1;
247
}
248
249
/**
250
* Converts a binary integer value into a signed Packed Decimal format. The Packed Decimal will be padded with zeros
251
* on the left if necessary.
252
*
253
* Overflow can happen if the resulting Packed Decimal does not fit into the result byte array, given the offset and
254
* precision. In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown,
255
* when false a truncated or invalid result is returned.
256
*
257
* @param integerValue
258
* the binary integer value to convert
259
* @param packedDecimal
260
* byte array that will store the resulting Packed Decimal value
261
* @param offset
262
* offset of the first byte of the Packed Decimal in <code>packedDecimal</code>
263
* @param precision
264
* number of Packed Decimal digits. Maximum valid precision is 253
265
* @param checkOverflow
266
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
267
* specified precision (overflow)
268
*
269
* @throws ArrayIndexOutOfBoundsException
270
* if an invalid array access occurs
271
* @throws NullPointerException
272
* if <code>packedDecimal</code> is null
273
* @throws ArithmeticException
274
* if the <code>checkOverflow</code> parameter is true and overflow occurs
275
*/
276
public static void convertIntegerToPackedDecimal(int integerValue,
277
byte[] packedDecimal, int offset, int precision,
278
boolean checkOverflow) {
279
if ((offset + ((precision/ 2) + 1) > packedDecimal.length) || (offset < 0))
280
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
281
"convertIntegerToPackedDecimal is trying to access packedDecimal[" + offset + "] to packedDecimal[" + (offset + (precision/ 2)) + "], " +
282
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
283
284
convertIntegerToPackedDecimal_(integerValue, packedDecimal, offset, precision, checkOverflow);
285
}
286
287
private static void convertIntegerToPackedDecimal_(int integerValue,
288
byte[] packedDecimal, int offset, int precision,
289
boolean checkOverflow) {
290
int value;
291
int bytes = CommonData.getPackedByteCount(precision);
292
int last = offset + bytes - 1;
293
int i;
294
boolean evenPrecision = (precision % 2 == 0) ? true : false;
295
296
// avoid invalid precision
297
if (checkOverflow) {
298
if (precision < 1)
299
throw new ArithmeticException(
300
"Decimal overflow - Packed Decimal precision lesser than 1");
301
302
if (numDigits(integerValue) > precision)
303
throw new ArithmeticException(
304
"Decimal overflow - Packed Decimal precision insufficient");
305
}
306
if (integerValue < 0) {
307
packedDecimal[last] = (byte) ((Math.abs(integerValue) % 10) << 4 | CommonData.PACKED_MINUS);
308
value = Math.abs(integerValue / 10);
309
} else {
310
value = integerValue;
311
packedDecimal[last] = (byte) ((value % 10) << 4 | CommonData.PACKED_PLUS);
312
value = value / 10;
313
}
314
315
// fill in high/low nibble pairs from next-to-last up to first
316
for (i = last - 1; i > offset && value != 0; i--) {
317
packedDecimal[i] = CommonData.getBinaryToPackedValues(value % 100);
318
value = value / 100;
319
}
320
321
if (i == offset && value != 0) {
322
if (evenPrecision)
323
packedDecimal[i] = (byte) (CommonData.getBinaryToPackedValues(value % 100) & CommonData.LOWER_NIBBLE_MASK);
324
else
325
packedDecimal[i] = CommonData.getBinaryToPackedValues(value % 100);
326
value = value / 100;
327
i--;
328
}
329
330
if (checkOverflow && value != 0) {
331
throw new ArithmeticException(
332
"Decimal overflow - Packed Decimal precision insufficient");
333
}
334
if (i >= offset) {
335
for (int j = 0; j < i - offset + 1; ++j)
336
packedDecimal[j+offset] = CommonData.PACKED_ZERO;
337
}
338
}
339
340
/**
341
* Converts an integer to an External Decimal in a byte array. The External Decimal will be padded with zeros on the
342
* left if necessary.
343
*
344
* Overflow can happen if the resulting External Decimal value does not fit into the byte array, given the precision
345
* and offset. In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown,
346
* when false a truncated or invalid result is returned.
347
*
348
* @param integerValue
349
* the value to convert
350
* @param externalDecimal
351
* the byte array which will hold the External Decimal on a successful return
352
* @param offset
353
* the offset in the byte array at which the External Decimal should be located
354
* @param precision
355
* the number of decimal digits. Maximum valid precision is 253
356
* @param checkOverflow
357
* if true an <code>ArithmeticException</code>will be thrown if the designated array cannot hold the
358
* External Decimal.
359
* @param decimalType
360
* constant value indicating the type of External Decimal
361
*
362
* @throws NullPointerException
363
* if <code>externalDecimal</code> is null
364
* @throws ArrayIndexOutOfBoundsException
365
* if an invalid array access occurs
366
* @throws ArithmeticException
367
* if the <code>checkOverflow</code> parameter is true and overflow occurs
368
* @throws IllegalArgumentException
369
* if <code>decimalType</code> or <code>precision</code> is invalid
370
*/
371
public static void convertIntegerToExternalDecimal(int integerValue,
372
byte[] externalDecimal, int offset, int precision,
373
boolean checkOverflow, int decimalType) {
374
if ((offset < 0)
375
|| (offset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length))
376
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. "
377
+ "convertIntegerToExternalDecimal is trying to access externalDecimal[" + offset
378
+ "] to externalDecimal[" + (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1)
379
+ "], " + " but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
380
if (JITIntrinsicsEnabled()) {
381
byte[] packedDecimal = new byte[precision / 2 + 1];
382
convertIntegerToPackedDecimal_(integerValue, packedDecimal, 0, precision, checkOverflow);
383
convertPackedDecimalToExternalDecimal_(packedDecimal, 0, externalDecimal, offset, precision, decimalType);
384
} else {
385
convertIntegerToExternalDecimal_(integerValue, externalDecimal, offset, precision, checkOverflow, decimalType);
386
}
387
}
388
389
private static void convertIntegerToExternalDecimal_(int integerValue, byte[] externalDecimal, int offset,
390
int precision, boolean checkOverflow, int decimalType) {
391
int i;
392
byte zoneVal = EXTERNAL_HIGH_MASK;
393
394
int externalSignOffset = offset;
395
if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING)
396
offset++;
397
int end = offset + precision - 1;
398
399
if (decimalType < EXTERNAL_DECIMAL_MIN || decimalType > EXTERNAL_DECIMAL_MAX)
400
throw new IllegalArgumentException("invalid decimalType");
401
402
if (checkOverflow) {
403
if (precision < 1)
404
throw new ArithmeticException("Decimal overflow - External Decimal precision lesser than 1");
405
406
if (numDigits(integerValue) > precision)
407
throw new ArithmeticException("Decimal overflow - External Decimal precision insufficient");
408
}
409
410
externalDecimal[end] = (byte) (zoneVal | Math.abs(integerValue % 10));
411
int value = Math.abs(integerValue / 10);
412
// fill in high/low nibble pairs from next-to-last up to first
413
for (i = end - 1; i >= offset && value != 0; i--) {
414
externalDecimal[i] = (byte) (zoneVal | (value % 10));
415
value = value / 10;
416
}
417
418
if (i >= offset) {
419
for (int j = offset; j <= i; j++) {
420
externalDecimal[j] = (byte) (zoneVal | CommonData.PACKED_ZERO);
421
}
422
}
423
424
switch (decimalType) {
425
case EBCDIC_SIGN_EMBEDDED_TRAILING:
426
case EBCDIC_SIGN_EMBEDDED_LEADING:
427
if (decimalType == EBCDIC_SIGN_EMBEDDED_TRAILING) {
428
externalSignOffset += precision - 1;
429
}
430
byte sign;
431
if (integerValue >= 0) {
432
sign = (byte) (CommonData.PACKED_PLUS << 4);
433
} else {
434
sign = (byte) (CommonData.PACKED_MINUS << 4);
435
}
436
externalDecimal[externalSignOffset] = (byte) ((externalDecimal[externalSignOffset] & CommonData.LOWER_NIBBLE_MASK) | sign);
437
break;
438
case EBCDIC_SIGN_SEPARATE_TRAILING:
439
case EBCDIC_SIGN_SEPARATE_LEADING:
440
if (decimalType == EBCDIC_SIGN_SEPARATE_TRAILING) {
441
externalSignOffset += precision;
442
}
443
if (integerValue >= 0)
444
externalDecimal[externalSignOffset] = EBCDIC_SIGN_POSITIVE;
445
else
446
externalDecimal[externalSignOffset] = EBCDIC_SIGN_NEGATIVE;
447
break;
448
}
449
}
450
451
/**
452
* Converts an integer to a Unicode Decimal in a char array
453
*
454
* Overflow can happen if the resulting External Decimal value does not fit into the char array, given the offset and
455
* precision. In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown,
456
* when false a truncated or invalid result is returned.
457
*
458
* @param integerValue
459
* the long value to convert
460
* @param unicodeDecimal
461
* the char array which will hold the Unicode Decimal on a successful return
462
* @param offset
463
* the offset in the char array where the Unicode Decimal would be located
464
* @param precision
465
* the number of decimal digits. Maximum valid precision is 253
466
* @param checkoverflow
467
* if true, when the designated an <code>ArithmeticException</code>
468
* @param unicodeType
469
* constant value indicating the type of Unicode Decimal
470
*
471
* @throws NullPointerException
472
* if <code>unicodeDecimal</code> is null
473
* @throws ArrayIndexOutOfBoundsException
474
* if an invalid array access occurs
475
*
476
* @throws ArithmeticException
477
* if the <code>checkOverflow</code> parameter is true and overflow occurs
478
* @throws IllegalArgumentException
479
* if the <code>decimalType</code> or <code>precision</code> is invalid
480
*/
481
public static void convertIntegerToUnicodeDecimal(int integerValue,
482
char[] unicodeDecimal, int offset, int precision,
483
boolean checkoverflow, int unicodeType) {
484
int size = unicodeType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
485
if ((offset + size > unicodeDecimal.length) || (offset < 0))
486
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
487
"convertIntegerToUnicodeDecimal is trying to access unicodeDecimal[" + offset + "] to unicodeDecimal[" + (offset + size - 1) + "], " +
488
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
489
490
if (JITIntrinsicsEnabled()) {
491
byte[] packedDecimal = new byte[precision / 2 + 1];
492
convertIntegerToPackedDecimal_(integerValue, packedDecimal, 0, precision, checkoverflow);
493
convertPackedDecimalToUnicodeDecimal_(packedDecimal, 0, unicodeDecimal, offset, precision, unicodeType);
494
} else {
495
convertIntegerToUnicodeDecimal_(integerValue, unicodeDecimal, offset, precision, checkoverflow, unicodeType);
496
}
497
}
498
499
private static void convertIntegerToUnicodeDecimal_(int integerValue, char[] unicodeDecimal, int offset, int precision, boolean checkOverflow, int unicodeType)
500
{
501
// Avoid invalid precisions
502
if (checkOverflow) {
503
if (precision < 1)
504
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision lesser than 1");
505
506
if (precision < numDigits(integerValue))
507
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision insufficient");
508
}
509
510
switch (unicodeType)
511
{
512
case UNICODE_UNSIGNED: break;
513
514
case UNICODE_SIGN_SEPARATE_LEADING:
515
unicodeDecimal[offset++] = integerValue < 0 ? UNICODE_SIGN_MINUS : UNICODE_SIGN_PLUS;
516
break;
517
518
case UNICODE_SIGN_SEPARATE_TRAILING:
519
unicodeDecimal[offset + precision] = integerValue < 0 ? UNICODE_SIGN_MINUS : UNICODE_SIGN_PLUS;
520
break;
521
522
default: throw new IllegalArgumentException("Invalid decimalType");
523
}
524
525
unicodeDecimal[offset + precision - 1] = (char) (UNICODE_HIGH_MASK | (Math.abs(integerValue) % 10));
526
527
// Normalize the value
528
integerValue = Math.abs(integerValue / 10);
529
530
int i;
531
532
// fill in high/low nibble pairs from next-to-last up to first
533
for (i = offset + precision - 2; i >= offset && integerValue != 0; i--) {
534
unicodeDecimal[i] = (char) (UNICODE_HIGH_MASK | (integerValue % 10));
535
536
integerValue = integerValue / 10;
537
}
538
539
if (checkOverflow && integerValue != 0) {
540
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision insufficient");
541
}
542
543
for (; i >= offset; i--) {
544
unicodeDecimal[i] = (char) UNICODE_HIGH_MASK;
545
}
546
}
547
548
/**
549
* Converts a binary long value into signed Packed Decimal format. The Packed Decimal will be padded with zeros on
550
* the left if necessary.
551
*
552
* Overflow can happen if the resulting Packed Decimal does not fit into the result byte array, given the offset and
553
* precision . In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown,
554
* when false a truncated or invalid result is returned.
555
*
556
* @param longValue
557
* the binary long value to convert
558
* @param packedDecimal
559
* byte array that will store the resulting Packed Decimal value
560
* @param offset
561
* offset of the first byte of the Packed Decimal in <code>packedDecimal</code>
562
* @param precision
563
* number of Packed Decimal digits. Maximum valid precision is 253
564
* @param checkOverflow
565
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
566
* specified precision (overflow), otherwise a truncated value is returned
567
*
568
* @throws ArrayIndexOutOfBoundsException
569
* if an invalid array access occurs
570
* @throws NullPointerException
571
* if <code>packedDecimal</code> is null
572
* @throws ArithmeticException
573
* the <code>checkOverflow</code> parameter is true and overflow occurs
574
*/
575
public static void convertLongToPackedDecimal(long longValue,
576
byte[] packedDecimal, int offset, int precision,
577
boolean checkOverflow) {
578
if ((offset + ((precision/ 2) + 1) > packedDecimal.length) || (offset < 0))
579
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
580
"convertLongToPackedDecimal is trying to access packedDecimal[" + offset + "] to packedDecimal[" + (offset + (precision/ 2)) + "], " +
581
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
582
583
convertLongToPackedDecimal_(longValue, packedDecimal, offset, precision, checkOverflow);
584
}
585
private static void convertLongToPackedDecimal_(long longValue,
586
byte[] packedDecimal, int offset, int precision,
587
boolean checkOverflow) {
588
long value;
589
int bytes = CommonData.getPackedByteCount(precision);
590
int last = offset + bytes - 1;
591
int i;
592
boolean evenPrecision = (precision % 2 == 0) ? true : false;
593
594
if (checkOverflow) {
595
if (precision < 1)
596
throw new ArithmeticException(
597
"Decimal overflow - Packed Decimal precision lesser than 1");
598
599
if (numDigits(longValue) > precision)
600
throw new ArithmeticException(
601
"Decimal overflow - Packed Decimal precision insufficient");
602
}
603
604
if (longValue < 0) {
605
packedDecimal[last] = (byte) ((Math.abs(longValue) % 10) << 4 | CommonData.PACKED_MINUS);
606
value = Math.abs(longValue / 10);
607
} else {
608
value = longValue;
609
packedDecimal[last] = (byte) ((value % 10) << 4 | CommonData.PACKED_PLUS);
610
value = value / 10;
611
}
612
613
// fill in high/low nibble pairs from next-to-last up to first
614
for (i = last - 1; i > offset && value != 0; i--) {
615
packedDecimal[i] = CommonData
616
.getBinaryToPackedValues((int) (value % 100));
617
value = value / 100;
618
}
619
620
if (i == offset && value != 0) {
621
if (evenPrecision)
622
packedDecimal[i] = (byte) (CommonData
623
.getBinaryToPackedValues((int) (value % 100)) & CommonData.LOWER_NIBBLE_MASK);
624
else
625
packedDecimal[i] = CommonData
626
.getBinaryToPackedValues((int) (value % 100));
627
value = value / 100;
628
i--;
629
}
630
631
if (checkOverflow && value != 0) {
632
throw new ArithmeticException(
633
"Decimal overflow - Packed Decimal precision insufficient");
634
}
635
if (i >= offset) {
636
for (int j = 0; j < i - offset + 1; ++j)
637
packedDecimal[j+offset] = CommonData.PACKED_ZERO;
638
}
639
}
640
641
/**
642
* Converts a long into an External Decimal in a byte array. The External Decimal will be padded with zeros on the
643
* left if necessary.
644
*
645
* Overflow can happen if the External Decimal value does not fit into the byte array, given its precision and offset.
646
* In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a
647
* truncated or invalid result is returned.
648
*
649
* @param longValue
650
* the value to convert
651
* @param externalDecimal
652
* the byte array which will hold the External Decimal on a successful return
653
* @param offset
654
* the offset into <code>externalDecimal</code> where External Decimal should be located
655
* @param precision
656
* the number of decimal digits to convert. Maximum valid precision is 253
657
* @param checkOverflow
658
* if true an <code>ArithmeticException</code> or <code>IllegalArgumentException</code> may be thrown
659
* @param decimalType
660
* constant value indicating the type of External Decimal
661
*
662
* @throws NullPointerException
663
* if <code>externalDecimal</code> is null
664
* @throws ArrayIndexOutOfBoundsException
665
* if an invalid array access occurs
666
* @throws ArithmeticException
667
* if the <code>checkOverflow</code> parameter is true and overflow occurs
668
* @throws IllegalArgumentException
669
* if the <code>decimalType</code> or <code>precision</code> is invalid
670
*/
671
public static void convertLongToExternalDecimal(long longValue, byte[] externalDecimal, int offset, int precision,
672
boolean checkOverflow, int decimalType) {
673
if ((offset < 0)
674
|| (offset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length))
675
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. "
676
+ "convertLongToExternalDecimal is trying to access externalDecimal[" + offset
677
+ "] to externalDecimal[" + (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1)
678
+ "], " + " but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
679
if (JITIntrinsicsEnabled()) {
680
byte[] packedDecimal = new byte[precision / 2 + 1];
681
convertLongToPackedDecimal_(longValue, packedDecimal, 0, precision, checkOverflow);
682
convertPackedDecimalToExternalDecimal_(packedDecimal, 0, externalDecimal, offset, precision, decimalType);
683
} else {
684
convertLongToExternalDecimal_(longValue, externalDecimal, offset, precision, checkOverflow, decimalType);
685
}
686
}
687
688
private static void convertLongToExternalDecimal_(long longValue, byte[] externalDecimal, int offset,
689
int precision, boolean checkOverflow, int decimalType) {
690
int i;
691
byte zoneVal = EXTERNAL_HIGH_MASK;
692
693
int externalSignOffset = offset;
694
if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING)
695
offset++;
696
int end = offset + precision - 1;
697
698
if (decimalType < EXTERNAL_DECIMAL_MIN || decimalType > EXTERNAL_DECIMAL_MAX)
699
throw new IllegalArgumentException("invalid decimalType");
700
701
if (checkOverflow) {
702
if (precision < 1)
703
throw new ArithmeticException("Decimal overflow - External Decimal precision lesser than 1");
704
705
if (numDigits(longValue) > precision)
706
throw new ArithmeticException("Decimal overflow - External Decimal precision insufficient");
707
}
708
709
externalDecimal[end] = (byte) (zoneVal | Math.abs(longValue % 10));
710
long value = Math.abs(longValue / 10);
711
// fill in high/low nibble pairs from next-to-last up to first
712
for (i = end - 1; i >= offset && value != 0; i--) {
713
externalDecimal[i] = (byte) (zoneVal | (value % 10));
714
value = value / 10;
715
}
716
717
if (i >= offset) {
718
for (int j = offset; j <= i; j++) {
719
externalDecimal[j] = (byte) (zoneVal | CommonData.PACKED_ZERO);
720
}
721
}
722
723
switch (decimalType) {
724
case EBCDIC_SIGN_EMBEDDED_TRAILING:
725
case EBCDIC_SIGN_EMBEDDED_LEADING:
726
if (decimalType == EBCDIC_SIGN_EMBEDDED_TRAILING) {
727
externalSignOffset += precision - 1;
728
}
729
byte sign;
730
if (longValue >= 0) {
731
sign = (byte) (CommonData.PACKED_PLUS << 4);
732
} else {
733
sign = (byte) (CommonData.PACKED_MINUS << 4);
734
}
735
externalDecimal[externalSignOffset] = (byte) ((externalDecimal[externalSignOffset] & CommonData.LOWER_NIBBLE_MASK) | sign);
736
break;
737
case EBCDIC_SIGN_SEPARATE_TRAILING:
738
case EBCDIC_SIGN_SEPARATE_LEADING:
739
if (decimalType == EBCDIC_SIGN_SEPARATE_TRAILING) {
740
externalSignOffset += precision;
741
}
742
if (longValue >= 0)
743
externalDecimal[externalSignOffset] = EBCDIC_SIGN_POSITIVE;
744
else
745
externalDecimal[externalSignOffset] = EBCDIC_SIGN_NEGATIVE;
746
break;
747
}
748
}
749
750
/**
751
* Converts a long to a Unicode Decimal in a char array
752
*
753
* Overflow can happen if the resulting Unicode Decimal value does not fit into the char array, given its precision
754
* and offset . In this case, when <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown,
755
* when false a truncated or invalid result is returned.
756
*
757
* @param longValue
758
* the long value to convert
759
* @param unicodeDecimal
760
* the char array which will hold the Unicode Decimal on a successful return
761
* @param offset
762
* the offset in the char array where the Unicode Decimal would be located
763
* @param precision
764
* the number of Unicode Decimal digits. Maximum valid precision is 253
765
* @param checkOverflow
766
* if true an <code>ArithmeticException</code> or <code>IllegalArgumentException</code> may be thrown
767
* @param decimalType
768
* constant value indicating the type of External Decimal
769
*
770
* @throws NullPointerException
771
* if <code>unicodeDecimal</code> is null
772
* @throws ArrayIndexOutOfBoundsException
773
* if an invalid array access occurs
774
* @throws ArithmeticException
775
* if the <code>checkOverflow</code> parameter is true and overflow occurs
776
* @throws IllegalArgumentException
777
* if <code>decimalType</code> or <code>precision</code> is invalid
778
*/
779
public static void convertLongToUnicodeDecimal(long longValue,
780
char[] unicodeDecimal, int offset, int precision,
781
boolean checkOverflow, int decimalType) {
782
int size = decimalType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
783
if ((offset + size > unicodeDecimal.length) || (offset < 0))
784
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
785
"convertIntegerToUnicodeDecimal is trying to access unicodeDecimal[" + offset + "] to unicodeDecimal[" + (offset + size - 1) + "], " +
786
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
787
788
if (JITIntrinsicsEnabled()) {
789
byte[] packedDecimal = new byte[precision / 2 + 1];
790
convertLongToPackedDecimal_(longValue, packedDecimal, 0, precision, checkOverflow);
791
convertPackedDecimalToUnicodeDecimal_(packedDecimal, 0, unicodeDecimal, offset, precision, decimalType);
792
} else {
793
convertLongToUnicodeDecimal_(longValue, unicodeDecimal, offset, precision, checkOverflow, decimalType);
794
}
795
}
796
797
private static void convertLongToUnicodeDecimal_(long longValue, char[] unicodeDecimal, int offset, int precision, boolean checkOverflow, int unicodeType)
798
{
799
// Avoid invalid precisions
800
if (checkOverflow) {
801
if (precision < 1)
802
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision lesser than 1");
803
804
if (precision < numDigits(longValue))
805
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision insufficient");
806
}
807
808
switch (unicodeType)
809
{
810
case UNICODE_UNSIGNED: break;
811
812
case UNICODE_SIGN_SEPARATE_LEADING:
813
unicodeDecimal[offset++] = longValue < 0 ? UNICODE_SIGN_MINUS : UNICODE_SIGN_PLUS;
814
break;
815
816
case UNICODE_SIGN_SEPARATE_TRAILING:
817
unicodeDecimal[offset + precision] = longValue < 0 ? UNICODE_SIGN_MINUS : UNICODE_SIGN_PLUS;
818
break;
819
820
default: throw new IllegalArgumentException("Invalid decimalType");
821
}
822
823
unicodeDecimal[offset + precision - 1] = (char) (UNICODE_HIGH_MASK | (Math.abs(longValue) % 10));
824
825
// Normalize the value
826
longValue = Math.abs(longValue / 10);
827
828
int i;
829
830
// fill in high/low nibble pairs from next-to-last up to first
831
for (i = offset + precision - 2; i >= offset && longValue != 0; i--) {
832
unicodeDecimal[i] = (char) (UNICODE_HIGH_MASK | (longValue % 10));
833
834
longValue = longValue / 10;
835
}
836
837
if (checkOverflow && longValue != 0) {
838
throw new ArithmeticException("Decimal overflow - Unicode Decimal precision insufficient");
839
}
840
841
for (; i >= offset; i--) {
842
unicodeDecimal[i] = (char) UNICODE_HIGH_MASK;
843
}
844
}
845
846
/**
847
* Converts a Packed Decimal value in a byte array into a binary integer. If the digital part of the input Packed
848
* Decimal is not valid then the digital part of the output will not be valid. The sign of the input Packed Decimal
849
* is assumed to be positive unless the sign nibble contains one of the negative sign codes, in which case the
850
* sign of the input Packed Decimal is interpreted as negative.
851
*
852
* Overflow can happen if the Packed Decimal value does not fit into a binary integer. When
853
* <code>checkOverflow</code> is true overflow results in an <code>ArithmeticException</code>, when false a
854
* truncated or invalid result is returned.
855
*
856
* @param packedDecimal
857
* byte array which contains the Packed Decimal value
858
* @param offset
859
* offset of the first byte of the Packed Decimal in <code>packedDecimal</code>
860
* @param precision
861
* number of Packed Decimal digits. Maximum valid precision is 253
862
* @param checkOverflow
863
* if true an <code>ArithmeticException</code> may be thrown
864
*
865
* @return int the resulting binary integer value
866
*
867
* @throws NullPointerException
868
* if <code>packedDecimal</code> is null
869
* @throws ArrayIndexOutOfBoundsException
870
* if an invalid array access occurs
871
* @throws ArithmeticException
872
* if <code>checkOverflow</code> is true and the result does not fit into an int (overflow)
873
*/
874
public static int convertPackedDecimalToInteger(byte[] packedDecimal,
875
int offset, int precision, boolean checkOverflow) {
876
if ((offset + ((precision/ 2) + 1) > packedDecimal.length) || (offset < 0))
877
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
878
"convertPackedDecimalToInteger is trying to access packedDecimal[" + offset + "] to packedDecimal[" + (offset + (precision/ 2)) + "], " +
879
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
880
881
return convertPackedDecimalToInteger_(packedDecimal, offset, precision, checkOverflow);
882
}
883
884
private static int convertPackedDecimalToInteger_(byte[] packedDecimal,
885
int offset, int precision, boolean checkOverflow) {
886
int bytes = CommonData.getPackedByteCount(precision);
887
int end = offset + bytes - 1;
888
long value = 0;// = (packedDecimal[end] & CommonData.INTEGER_MASK) >> 4;
889
890
byte sign = CommonData.getSign((byte) (packedDecimal[end] & CommonData.LOWER_NIBBLE_MASK));
891
892
// Skip the first byte if the precision is even and the low-order nibble is zero
893
if (precision % 2 == 0 && (packedDecimal[offset] & CommonData.LOWER_NIBBLE_MASK) == 0x00)
894
{
895
precision--;
896
offset++;
897
}
898
899
// Skip consecutive zero bytes
900
for (; offset < end && packedDecimal[offset] == CommonData.PACKED_ZERO; offset++)
901
{
902
precision -= 2;
903
}
904
905
if (checkOverflow)
906
{
907
// Skip high-order zero if and only if precision is odd
908
if (precision % 2 == 1 && (packedDecimal[offset] & CommonData.HIGHER_NIBBLE_MASK) == 0x00)
909
{
910
precision--;
911
}
912
913
// At this point we are guaranteed that the nibble pointed by a non-zero precision value is non-zero
914
if (precision > 10)
915
throw new ArithmeticException(
916
"Decimal overflow - Packed Decimal too large for an int");
917
}
918
919
// For checkOverflow == true at this point we are guaranteed that precision <= 10. The following loop
920
// will never overflow because the long value can always contain an integer of precision 10.
921
922
for (int pos = offset; pos <= end - 1; ++pos)
923
{
924
value = value * 100 + CommonData.getPackedToBinaryValues(packedDecimal[pos]);
925
}
926
927
value = value * 10 + ((packedDecimal[end] & CommonData.HIGHER_NIBBLE_MASK) >> 4);
928
929
if (sign == CommonData.PACKED_MINUS)
930
value = -1 * value;
931
932
if (checkOverflow && (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE))
933
{
934
throw new ArithmeticException(
935
"Decimal overflow - Packed Decimal too large for a int");
936
}
937
938
return (int)value;
939
}
940
941
/**
942
* Converts a Packed Decimal value in a byte array into a binary long. If the digital part of the input Packed
943
* Decimal is not valid then the digital part of the output will not be valid. The sign of the input Packed Decimal
944
* is assumed to be positive unless the sign nibble contains one of the negative sign codes, in which case the
945
* sign of the input Packed Decimal is interpreted as negative.
946
*
947
* Overflow can happen if the Packed Decimal value does not fit into a binary long. In this case, when
948
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
949
* invalid result is returned.
950
*
951
* @param packedDecimal
952
* byte array which contains the Packed Decimal value
953
* @param offset
954
* offset of the first byte of the Packed Decimal in <code>packedDecimal</code>
955
* @param precision
956
* number of decimal digits. Maximum valid precision is 253
957
* @param checkOverflow
958
* if true an <code>ArithmeticException</code> may be thrown
959
*
960
* @return long the resulting binary long value
961
*
962
* @throws NullPointerException
963
* if <code>packedDecimal</code> is null
964
* @throws ArrayIndexOutOfBoundsException
965
* if an invalid array access occurs
966
* @throws ArithmeticException
967
* if <code>checkOverflow</code> is true and the result does not fit into a long (overflow)
968
*/
969
public static long convertPackedDecimalToLong(byte[] packedDecimal,
970
int offset, int precision, boolean checkOverflow) {
971
if ((offset + ((precision/ 2) + 1) > packedDecimal.length) || (offset < 0))
972
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
973
"convertPackedDecimalToLong is trying to access packedDecimal[" + offset + "] to packedDecimal[" + (offset + (precision/ 2)) + "], " +
974
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
975
976
return convertPackedDecimalToLong_(packedDecimal, offset, precision, checkOverflow);
977
}
978
979
private static long convertPackedDecimalToLong_(byte[] packedDecimal,
980
int offset, int precision, boolean checkOverflow) {
981
long value = 0;
982
int bytes = CommonData.getPackedByteCount(precision);
983
int end = offset + bytes - 1;
984
int last = packedDecimal[end] & CommonData.INTEGER_MASK;
985
byte sign = CommonData.getSign((byte) (last & CommonData.LOWER_NIBBLE_MASK));
986
987
// Skip the first byte if the precision is even and the low-order nibble is zero
988
if (precision % 2 == 0 && (packedDecimal[offset] & CommonData.LOWER_NIBBLE_MASK) == 0x00)
989
{
990
precision--;
991
offset++;
992
}
993
994
// Skip consecutive zero bytes
995
for (; offset < end && packedDecimal[offset] == CommonData.PACKED_ZERO; offset++)
996
{
997
precision -= 2;
998
}
999
1000
if (checkOverflow)
1001
{
1002
// Skip high-order zero if and only if precision is odd
1003
if (precision % 2 == 1 && (packedDecimal[offset] & CommonData.HIGHER_NIBBLE_MASK) == 0x00)
1004
{
1005
precision--;
1006
}
1007
1008
// At this point we are guaranteed that the nibble pointed by a non-zero precision value is non-zero
1009
if (precision > 19)
1010
throw new ArithmeticException(
1011
"Decimal overflow - Packed Decimal too large for a long");
1012
}
1013
1014
// For checkOverflow == true at this point we are guaranteed that precision <= 19. The following loop
1015
// may cause the signed long value to overflow. Because the first digit of Long.MAX_VALUE is a 9 the
1016
// overflowed signed long value cannot overflow an unsigned long. This guarantees that if an overflow
1017
// occurs, value will be negative. We will use this fact along with the sign code calculated earlier
1018
// to determine whether overflow occurred.
1019
1020
for (int pos = offset; pos <= end - 1; ++pos)
1021
{
1022
value = value * 100 + CommonData.getPackedToBinaryValues(packedDecimal[pos]);
1023
}
1024
1025
value = value * 10 + ((last & CommonData.HIGHER_NIBBLE_MASK) >> 4);
1026
1027
1028
if (sign == CommonData.PACKED_MINUS)
1029
value = -value;
1030
1031
if (checkOverflow)
1032
{
1033
if (sign == CommonData.PACKED_PLUS && value < 0)
1034
throw new ArithmeticException(
1035
"Decimal overflow - Packed Decimal too large for a long");
1036
else if (sign == CommonData.PACKED_MINUS && value > 0)
1037
throw new ArithmeticException(
1038
"Decimal overflow - Packed Decimal too large for a long");
1039
}
1040
1041
return value;
1042
}
1043
1044
/**
1045
* Converts a Packed Decimal in a byte array into an External Decimal in another byte array. If the digital part of
1046
* the input Packed Decimal is not valid then the digital part of the output will not be valid. The sign of the
1047
* input Packed Decimal is assumed to be positive unless the sign nibble contains one of the negative sign codes,
1048
* in which case the sign of the input Packed Decimal is interpreted as negative.
1049
*
1050
* @param packedDecimal
1051
* byte array that holds the Packed Decimal to be converted
1052
* @param packedOffset
1053
* offset in <code>packedDecimal</code> where the Packed Decimal is located
1054
* @param externalDecimal
1055
* byte array that will hold the External Decimal on a successful return
1056
* @param externalOffset
1057
* offset in <code>externalOffset</code> where the External Decimal is expected to be located
1058
* @param precision
1059
* number of decimal digits
1060
* @param decimalType
1061
* constant indicating the type of the decimal
1062
*
1063
* @throws ArrayIndexOutOfBoundsException
1064
* if an invalid array access occurs
1065
* @throws NullPointerException
1066
* if <code>packedDecimal</code> or <code>externalDecimal</code> are null
1067
* @throws IllegalArgumentException
1068
* if <code>precision</code> or <code>decimalType</code> is invalid
1069
*/
1070
public static void convertPackedDecimalToExternalDecimal(
1071
byte[] packedDecimal, int packedOffset, byte[] externalDecimal,
1072
int externalOffset, int precision, int decimalType) {
1073
if ((packedOffset + ((precision/ 2) + 1) > packedDecimal.length) || (packedOffset < 0))
1074
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1075
"convertPackedDecimalToExternalDecimal is trying to access packedDecimal[" + packedOffset + "] to packedDecimal[" + (packedOffset + (precision/ 2)) + "], " +
1076
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
1077
if ((externalOffset < 0) || (externalOffset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length))
1078
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1079
"convertPackedDecimalToExternalDecimal is trying to access externalDecimal[" + externalOffset + "] to externalDecimal[" + (externalOffset + CommonData.getExternalByteCounts(precision, decimalType) - 1) + "], " +
1080
" but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
1081
1082
convertPackedDecimalToExternalDecimal_(packedDecimal, packedOffset, externalDecimal,
1083
externalOffset, precision, decimalType);
1084
}
1085
1086
private static void convertPackedDecimalToExternalDecimal_(
1087
byte[] packedDecimal, int packedOffset, byte[] externalDecimal,
1088
int externalOffset, int precision, int decimalType) {
1089
1090
if (decimalType < EXTERNAL_DECIMAL_MIN
1091
|| decimalType > EXTERNAL_DECIMAL_MAX)
1092
throw new IllegalArgumentException("invalid decimalType");
1093
if (precision <= 0)
1094
throw new IllegalArgumentException("negative precision");
1095
1096
int externalSignOffset = externalOffset;
1097
if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING)
1098
externalOffset++;
1099
1100
int end = packedOffset + precision / 2;
1101
int edEnd = externalOffset + precision - 1;
1102
1103
byte zoneVal = EXTERNAL_HIGH_MASK;
1104
1105
// handle even precision
1106
if (precision % 2 == 0) {
1107
externalDecimal[externalOffset++] = (byte) (zoneVal | (packedDecimal[packedOffset++] & CommonData.LOWER_NIBBLE_MASK));
1108
}
1109
1110
// compute all the intermediate digits
1111
for (int i = packedOffset; i < end; i++) {
1112
externalDecimal[externalOffset++] = (byte) (zoneVal | (((packedDecimal[i] & CommonData.HIGHER_NIBBLE_MASK) >> 4) & CommonData.LOWER_NIBBLE_MASK));
1113
externalDecimal[externalOffset++] = (byte) (zoneVal | (packedDecimal[i] & CommonData.LOWER_NIBBLE_MASK));
1114
}
1115
1116
1117
// deal with the last digit
1118
externalDecimal[edEnd] = (byte) (zoneVal | (((packedDecimal[end] & 0xF0) >> 4) & CommonData.LOWER_NIBBLE_MASK));
1119
1120
//byte sign = (byte)((packedDecimal[end] & CommonData.LOWER_NIBBLE_MASK) << 4);
1121
byte sign = (byte)(CommonData.getSign(packedDecimal[end] & CommonData.LOWER_NIBBLE_MASK) << 4);
1122
1123
switch (decimalType) {
1124
case EBCDIC_SIGN_SEPARATE_LEADING:
1125
if (sign == (byte) 0xC0)
1126
externalDecimal[externalSignOffset] = EBCDIC_SIGN_POSITIVE;
1127
else
1128
externalDecimal[externalSignOffset] = EBCDIC_SIGN_NEGATIVE;
1129
break;
1130
case EBCDIC_SIGN_EMBEDDED_LEADING:
1131
externalDecimal[externalSignOffset] = (byte) ((externalDecimal[externalSignOffset] & CommonData.LOWER_NIBBLE_MASK) | sign);
1132
break;
1133
case EBCDIC_SIGN_EMBEDDED_TRAILING:
1134
externalSignOffset += precision - 1;
1135
externalDecimal[externalSignOffset] = (byte) ((externalDecimal[externalSignOffset] & CommonData.LOWER_NIBBLE_MASK) | sign);
1136
break;
1137
case EBCDIC_SIGN_SEPARATE_TRAILING:
1138
externalSignOffset += precision;
1139
if (sign == (byte) 0xC0)
1140
externalDecimal[externalSignOffset] = EBCDIC_SIGN_POSITIVE;
1141
else
1142
externalDecimal[externalSignOffset] = EBCDIC_SIGN_NEGATIVE;
1143
break;
1144
default:
1145
//unreachable code
1146
//throw new IllegalArgumentException("invalid decimalType");
1147
}
1148
}
1149
1150
/**
1151
* Convert a Packed Decimal in a byte array to a Unicode Decimal in a char array. If the digital part of the input
1152
* Packed Decimal is not valid then the digital part of the output will not be valid. The sign of the input Packed
1153
* Decimal is assumed to be positive unless the sign nibble contains one of the negative sign codes, in which
1154
* case the sign of the input Packed Decimal is interpreted as negative.
1155
*
1156
* @param packedDecimal
1157
* byte array that holds a Packed Decimal to be converted
1158
* @param packedOffset
1159
* offset in <code>packedDecimal</code> where the Packed Decimal is located
1160
* @param unicodeDecimal
1161
* char array that will hold the Unicode Decimal on a successful return
1162
* @param unicodeOffset
1163
* offset in the byte array where the Unicode Decimal is expected to be located
1164
* @param precision
1165
* number of decimal digits
1166
* @param decimalType
1167
* constant value indicating the type of the External Decimal
1168
*
1169
* @throws ArrayIndexOutOfBoundsException
1170
* if an invalid array access occurs
1171
* @throws NullPointerException
1172
* if <code>packedDecimal</code> or <code>unicodeDecimal</code> are null
1173
* @throws IllegalArgumentException
1174
* if <code>precision</code> or <code>decimalType</code> is invalid
1175
*/
1176
public static void convertPackedDecimalToUnicodeDecimal(
1177
byte[] packedDecimal, int packedOffset, char[] unicodeDecimal,
1178
int unicodeOffset, int precision, int decimalType) {
1179
int size = decimalType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
1180
if ((unicodeOffset + size > unicodeDecimal.length) || (unicodeOffset < 0))
1181
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1182
"convertPackedDecimalToUnicodeDecimal is trying to access unicodeDecimal[" + unicodeOffset + "] to unicodeDecimal[" + (unicodeOffset + size - 1) + "], " +
1183
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
1184
if ((packedOffset < 0) || (packedOffset + ((precision/ 2) + 1) > packedDecimal.length))
1185
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1186
"convertPackedDecimalToUnicodeDecimal is trying to access packedDecimal[" + packedOffset + "] to packedDecimal[" + (packedOffset + (precision/ 2)) + "], " +
1187
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
1188
1189
convertPackedDecimalToUnicodeDecimal_(packedDecimal, packedOffset, unicodeDecimal,
1190
unicodeOffset, precision, decimalType);
1191
}
1192
1193
private static void convertPackedDecimalToUnicodeDecimal_(
1194
byte[] packedDecimal, int packedOffset, char[] unicodeDecimal,
1195
int unicodeOffset, int precision, int decimalType) {
1196
1197
if (precision <= 0)
1198
throw new IllegalArgumentException("negative precision");
1199
1200
int externalSignOffset = -1;
1201
switch (decimalType) {
1202
case UNICODE_UNSIGNED:
1203
break;
1204
case UNICODE_SIGN_SEPARATE_LEADING:
1205
externalSignOffset = unicodeOffset++;
1206
break;
1207
case UNICODE_SIGN_SEPARATE_TRAILING:
1208
externalSignOffset = unicodeOffset + precision;
1209
break;
1210
default:
1211
throw new IllegalArgumentException("invalid decimalType");
1212
}
1213
1214
byte zoneVal = UNICODE_HIGH_MASK;
1215
1216
// Get sign from Packed Decimal
1217
int end = packedOffset + precision / 2;
1218
byte sign = (byte) (packedDecimal[end] & CommonData.LOWER_NIBBLE_MASK);
1219
sign = CommonData.getSign(sign);
1220
1221
// handle even precision
1222
if (precision % 2 == 0) {
1223
unicodeDecimal[unicodeOffset] = (char) (zoneVal | (packedDecimal[packedOffset++] & CommonData.LOWER_NIBBLE_MASK));
1224
unicodeOffset++;
1225
}
1226
1227
// compute all the intermediate digits
1228
for (int i = packedOffset; i < end; i++) {
1229
unicodeDecimal[unicodeOffset] = (char) (zoneVal | (((packedDecimal[i] & CommonData.HIGHER_NIBBLE_MASK) >> 4) & CommonData.LOWER_NIBBLE_MASK));
1230
unicodeOffset++;
1231
unicodeDecimal[unicodeOffset] = (char) (zoneVal | (packedDecimal[i] & CommonData.LOWER_NIBBLE_MASK));
1232
unicodeOffset++;
1233
}
1234
1235
// deal with the last digit
1236
unicodeDecimal[unicodeOffset] = (char) (zoneVal | (((packedDecimal[end] & 0xF0) >> 4) & CommonData.LOWER_NIBBLE_MASK));
1237
1238
// put the sign
1239
if (decimalType != UNICODE_UNSIGNED) {
1240
if (sign == CommonData.PACKED_PLUS) {
1241
// put 2B for positive
1242
unicodeDecimal[externalSignOffset] = 0x2B;
1243
} else {
1244
// put 2D for negative
1245
unicodeDecimal[externalSignOffset] = 0x2D;
1246
}
1247
}
1248
}
1249
1250
/**
1251
* Convert a Packed Decimal in a byte array to a BigInteger. If the digital part of the input Packed Decimal is not
1252
* valid then the digital part of the output will not be valid. The sign of the input Packed Decimal is assumed to
1253
* to be positive unless the sign nibble contains one of the negative sign codes, in which case the sign of the
1254
* input Packed Decimal is interpreted as negative.
1255
*
1256
* Overflow can happen if the Packed Decimal value does not fit into the BigInteger. In this case, when
1257
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1258
* invalid result is returned.
1259
*
1260
* @param packedDecimal
1261
* byte array that holds the Packed Decimal to be converted
1262
* @param offset
1263
* offset in <code>packedDecimal</code> where the Packed Decimal is located
1264
* @param precision
1265
* number of decimal digits. Maximum valid precision is 253
1266
* @param checkOverflow
1267
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
1268
* specified precision (overflow)
1269
* @return BigInteger the resulting BigInteger
1270
*
1271
* @throws ArrayIndexOutOfBoundsException
1272
* if an invalid array access occurs
1273
* @throws NullPointerException
1274
* if <code>packedDecimal</code> is null
1275
*/
1276
public static BigInteger convertPackedDecimalToBigInteger(
1277
byte[] packedDecimal, int offset, int precision,
1278
boolean checkOverflow) {
1279
return convertPackedDecimalToBigDecimal(packedDecimal, offset,
1280
precision, 0, checkOverflow).toBigInteger();
1281
}
1282
1283
/**
1284
* Convert a Packed Decimal in a byte array to a BigDecimal. If the digital part of the input Packed Decimal is not
1285
* valid then the digital part of the output will not be valid. The sign of the input Packed Decimal is assumed to
1286
* to be positive unless the sign nibble contains one of the negative sign codes, in which case the sign of the
1287
* input Packed Decimal is interpreted as negative.
1288
*
1289
* Overflow can happen if the Packed Decimal value does not fit into the BigDecimal. In this case, when
1290
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1291
* invalid result is returned.
1292
*
1293
* @param packedDecimal
1294
* byte array that holds the Packed Decimal to be converted
1295
* @param offset
1296
* offset in <code>packedDecimal</code> where the Packed Decimal is located
1297
* @param precision
1298
* number of decimal digits. Maximum valid precision is 253
1299
* @param scale
1300
* scale of the BigDecimal to be returned
1301
* @param checkOverflow
1302
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
1303
* specified precision (overflow)
1304
*
1305
* @return BigDecimal the resulting BigDecimal
1306
*
1307
* @throws NullPointerException
1308
* if <code>packedDecimal</code> is null
1309
* @throws ArrayIndexOutOfBoundsException
1310
* /requires rounding if an invalid array access occurs
1311
*/
1312
public static BigDecimal convertPackedDecimalToBigDecimal(
1313
byte[] packedDecimal, int offset, int precision, int scale,
1314
boolean checkOverflow) {
1315
1316
if (precision <= 9) {
1317
return BigDecimal.valueOf(
1318
convertPackedDecimalToInteger(packedDecimal, offset, precision,
1319
checkOverflow), scale);
1320
} else if (precision <= 18) {
1321
return BigDecimal.valueOf(
1322
convertPackedDecimalToLong(packedDecimal, offset, precision,
1323
checkOverflow), scale);
1324
}
1325
1326
return slowSignedPackedToBigDecimal(packedDecimal, offset, precision,
1327
scale, checkOverflow);
1328
}
1329
1330
/**
1331
* Converts an External Decimal value in a byte array into a binary integer. If the digital part of the input
1332
* External Decimal is not valid then the digital part of the output will not be valid. The sign of the input
1333
* External Decimal is assumed to be positive unless the sign nibble or byte (depending on
1334
* <code>decimalType</code>) contains one of the negative sign codes, in which case the sign of the input External
1335
* Decimal is interpreted as negative.
1336
*
1337
* Overflow can happen if the External Decimal value does not fit into a binary integer. In this case, when
1338
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false the resulting number
1339
* will be wrapped around starting at the minimum/maximum possible integer value.
1340
*
1341
* @param externalDecimal
1342
* byte array which contains the External Decimal value
1343
* @param offset
1344
* the offset where the External Decimal value is located
1345
* @param precision
1346
* number of decimal digits. Maximum valid precision is 253
1347
* @param checkOverflow
1348
* if true an <code>ArithmeticException</code> or <code>IllegalArgumentException</code> may be thrown. If
1349
* false and there is an overflow, the result is undefined.
1350
* @param decimalType
1351
* constant value indicating the type of External Decimal
1352
*
1353
* @return int the resulting binary integer
1354
*
1355
* @throws NullPointerException
1356
* if <code>externalDecimal</code> is null
1357
* @throws ArrayIndexOutOfBoundsException
1358
* if an invalid array access occurs
1359
* @throws ArithmeticException
1360
* if <code>checkOverflow</code> is true and the result does not fit into a int (overflow)
1361
* @throws IllegalArgumentException
1362
* if <code>precision</code> or <code>decimalType</code> is invalid
1363
*/
1364
public static int convertExternalDecimalToInteger(byte[] externalDecimal,
1365
int offset, int precision, boolean checkOverflow, int decimalType) {
1366
if ((offset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length) || (offset < 0))
1367
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1368
"convertExternalDecimalToInteger is trying to access externalDecimal[" + offset + "] to externalDecimal[" + (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1) + "], " +
1369
" but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
1370
1371
if (precision <= 0)
1372
throw new IllegalArgumentException("Precision can't be negative.");
1373
1374
if (JITIntrinsicsEnabled()) {
1375
byte[] packedDecimal = new byte[precision / 2 + 1];
1376
convertExternalDecimalToPackedDecimal_(externalDecimal, offset, packedDecimal, 0, precision, decimalType);
1377
return convertPackedDecimalToInteger_(packedDecimal, 0, precision, checkOverflow);
1378
} else {
1379
return convertExternalDecimalToInteger_(externalDecimal, offset, precision, checkOverflow, decimalType);
1380
}
1381
}
1382
1383
private static int convertExternalDecimalToInteger_(byte[] externalDecimal,
1384
int offset, int precision, boolean checkOverflow, int decimalType) {
1385
int end = (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1);
1386
boolean isNegative = isExternalDecimalSignNegative(externalDecimal, offset, precision, decimalType);
1387
1388
1389
if (decimalType == EBCDIC_SIGN_SEPARATE_TRAILING) {
1390
end--;
1391
} else if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING) {
1392
offset++;
1393
}
1394
1395
int value = 0;
1396
if (isNegative)
1397
{
1398
if (precision < 10 || (checkOverflow == false)) //max/min values are -2,147,483,648 and 2,147,483,647, so no overflow possible
1399
{
1400
for (int i = offset; i <= end; i++)
1401
{
1402
value = value * 10 - (externalDecimal[i] & 0x0F);
1403
}
1404
}
1405
else //checkOverflow true, precision >= 10
1406
{
1407
int tenthOffset = end-9; //location of 10th digit if existent
1408
int offsetMod = offset > tenthOffset ? offset : tenthOffset; //only read last 10 digits
1409
for (int i = offsetMod; i <= end; i++)
1410
{
1411
value = value * 10 - (externalDecimal[i] & 0x0F);
1412
}
1413
//need value>0 for cases like 2,900,000,000 and digit>2 for 5,000,000,000
1414
if (value > 0 || (tenthOffset >= offset && (externalDecimal[tenthOffset] & 0x0F) > 2)) //check 10th digit
1415
{
1416
throw new ArithmeticException(
1417
"Decimal overflow - External Decimal too small for an int");
1418
}
1419
1420
//any more digits are overflow
1421
for (int i = offset; i < offsetMod; i++)
1422
{
1423
if ((externalDecimal[i] & 0x0F) > 0)
1424
{
1425
throw new ArithmeticException(
1426
"Decimal overflow - External Decimal too small for an int");
1427
}
1428
}
1429
}
1430
}
1431
else
1432
{
1433
if (precision < 10 || (checkOverflow == false)) //max/min values are -2,147,483,648 and 2,147,483,647, so no overflow possible
1434
{
1435
for (int i = offset; i <= end; i++)
1436
{
1437
value = value * 10 + (externalDecimal[i] & 0x0F);
1438
}
1439
}
1440
else //checkOverflow true, precision >= 10
1441
{
1442
int tenthOffset = end-9; //location of 10th digit if existent
1443
int offsetMod = offset > tenthOffset ? offset : tenthOffset; //only read last 10 digits
1444
for (int i = offsetMod; i <= end; i++)
1445
{
1446
value = value * 10 + (externalDecimal[i] & 0x0F);
1447
}
1448
//need value<0 for cases like 2,900,000,000 and digit>2 for 5,000,000,000
1449
if (value < 0 || (tenthOffset >= offset && (externalDecimal[tenthOffset] & 0x0F) > 2)) //check 10th digit
1450
{
1451
throw new ArithmeticException(
1452
"Decimal overflow - External Decimal too large for an int");
1453
}
1454
1455
//any more digits are overflow
1456
for (int i = offset; i < offsetMod; i++)
1457
{
1458
if ((externalDecimal[i] & 0x0F) > 0)
1459
{
1460
throw new ArithmeticException(
1461
"Decimal overflow - External Decimal too large for an int");
1462
}
1463
}
1464
}
1465
}
1466
return value;
1467
}
1468
1469
/**
1470
* Converts an External Decimal value in a byte array into a long. If the digital part of the input External Decimal
1471
* is not valid then the digital part of the output will not be valid. The sign of the input External Decimal is
1472
* assumed to be positive unless the sign nibble or byte (depending on <code>decimalType</code>) contains one of
1473
* the negative sign codes, in which case the sign of the input External Decimal is interpreted as negative.
1474
*
1475
* Overflow can happen if the External Decimal value does not fit into a binary long. When
1476
* <code>checkOverflow</code> is true overflow results in an <code>ArithmeticException</code>, when false the
1477
* resulting number will be wrapped around starting at the minimum/maximum possible long value.
1478
*
1479
* @param externalDecimal
1480
* byte array which contains the External Decimal value
1481
* @param offset
1482
* offset of the first byte of the Packed Decimal in the <code>externalDecimal</code>
1483
* @param precision
1484
* number of External Decimal digits. Maximum valid precision is 253
1485
* @param checkOverflow
1486
* if true an <code>ArithmeticException</code> will be thrown when the converted value cannot fit into
1487
* designated External Decimal array. If false and there is an overflow, the result is undefined.
1488
* @param decimalType
1489
* constant value indicating the type of External Decimal
1490
*
1491
* @return long the resulting binary long value
1492
*
1493
* @throws NullPointerException
1494
* if <code>externalDecimal</code> is null
1495
* @throws ArrayIndexOutOfBoundsException
1496
* if an invalid array access occurs
1497
* @throws ArithmeticException
1498
* if <code>checkOverflow</code> is true and the result does not fit into a long (overflow)
1499
* @throws IllegalArgumentException
1500
* if <code>precision</code> or <code>decimalType</code> is invalid
1501
*/
1502
public static long convertExternalDecimalToLong(byte[] externalDecimal,
1503
int offset, int precision, boolean checkOverflow, int decimalType) {
1504
if ((offset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length) || (offset < 0))
1505
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1506
"convertExternalDecimalToLong is trying to access externalDecimal[" + offset + "] to externalDecimal[" + (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1) + "], " +
1507
" but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
1508
1509
if (precision <= 0)
1510
throw new IllegalArgumentException("Precision can't be negative.");
1511
1512
if (JITIntrinsicsEnabled()) {
1513
byte[] packedDecimal = new byte[precision / 2 + 1];
1514
convertExternalDecimalToPackedDecimal_(externalDecimal, offset, packedDecimal, 0, precision, decimalType);
1515
return convertPackedDecimalToLong_(packedDecimal, 0, precision, checkOverflow);
1516
} else {
1517
return convertExternalDecimalToLong_(externalDecimal, offset, precision, checkOverflow, decimalType);
1518
}
1519
}
1520
1521
private static long convertExternalDecimalToLong_(byte[] externalDecimal,
1522
int offset, int precision, boolean checkOverflow, int decimalType) {
1523
int end = (offset + CommonData.getExternalByteCounts(precision, decimalType) - 1);
1524
boolean isNegative = isExternalDecimalSignNegative(externalDecimal, offset, precision, decimalType);
1525
1526
if (decimalType == EBCDIC_SIGN_SEPARATE_TRAILING) {
1527
end--;
1528
} else if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING) {
1529
offset++;
1530
}
1531
1532
long value = 0;
1533
if (isNegative)
1534
{
1535
if (precision < 19 || (checkOverflow == false)) //max/min values are -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807, so no overflow possible
1536
{
1537
for (int i = offset; i <= end; i++)
1538
{
1539
value = value * 10 - (externalDecimal[i] & 0x0F);
1540
}
1541
}
1542
else //checkOverflow true, precision >= 19
1543
{
1544
int offsetMod = offset > end-18 ? offset : end-18; //only read last 19 digits
1545
for (int i = offsetMod; i <= end; i++)
1546
{
1547
value = value * 10 - (externalDecimal[i] & 0x0F);
1548
}
1549
if (value > 0) //check 19th digit
1550
{
1551
throw new ArithmeticException(
1552
"Decimal overflow - External Decimal too small for a long");
1553
}
1554
1555
//any more digits are overflow
1556
for (int i = offset; i < offsetMod; i++)
1557
{
1558
if ((externalDecimal[i] & 0x0F) > 0)
1559
{
1560
throw new ArithmeticException(
1561
"Decimal overflow - External Decimal too small for a long");
1562
}
1563
}
1564
}
1565
}
1566
else
1567
{
1568
if (precision < 19 || (checkOverflow == false)) //max/min values are -9,223,372,036,854,775,808 and 9,223,372,036,854,775,807, so no overflow possible
1569
{
1570
for (int i = offset; i <= end; i++)
1571
{
1572
value = value * 10 + (externalDecimal[i] & 0x0F);
1573
}
1574
}
1575
else //checkOverflow true, precision >= 19
1576
{
1577
int offsetMod = offset > end-18 ? offset : end-18; //only read last 19 digits
1578
for (int i = offsetMod; i <= end; i++)
1579
{
1580
value = value * 10 + (externalDecimal[i] & 0x0F);
1581
}
1582
if (value < 0) //check 19th digit
1583
{
1584
throw new ArithmeticException(
1585
"Decimal overflow - External Decimal too large for a long");
1586
}
1587
1588
//any more digits are overflow
1589
for (int i = offset; i < offsetMod; i++)
1590
{
1591
if ((externalDecimal[i] & 0x0F) > 0)
1592
{
1593
throw new ArithmeticException(
1594
"Decimal overflow - External Decimal too large for a long");
1595
}
1596
}
1597
}
1598
}
1599
return value;
1600
}
1601
1602
/**
1603
* Converts an External Decimal in a byte array to a Packed Decimal in another byte array. If the digital part of
1604
* the input External Decimal is not valid then the digital part of the output will not be valid. The sign of the
1605
* input External Decimal is assumed to be positive unless the sign nibble or byte (depending on
1606
* <code>decimalType</code>) contains one of the negative sign codes, in which case the sign of the input External
1607
* Decimal is interpreted as negative.
1608
*
1609
* @param externalDecimal
1610
* byte array holding the External Decimal to be converted
1611
* @param externalOffset
1612
* offset in <code>externalDecimal</code> where the External Decimal is located
1613
* @param packedDecimal
1614
* byte array which will hold the Packed Decimal on a successful return
1615
* @param packedOffset
1616
* offset in <code>packedDecimal</code> where the Packed Decimal is expected to be located
1617
* @param precision
1618
* the number of decimal digits
1619
* @param decimalType
1620
* constant value indicating the type of External Decimal
1621
*
1622
*
1623
* @throws ArrayIndexOutOfBoundsException
1624
* if an invalid array access occurs
1625
* @throws NullPointerException
1626
* if <code>packedDecimal</code> or <code>externalDecimal</code> is null
1627
* @throws IllegalArgumentException
1628
* if <code>precision</code> or <code>decimalType</code> is invalid
1629
*/
1630
public static void convertExternalDecimalToPackedDecimal(
1631
byte[] externalDecimal, int externalOffset, byte[] packedDecimal,
1632
int packedOffset, int precision, int decimalType) {
1633
if ((externalOffset + CommonData.getExternalByteCounts(precision, decimalType) > externalDecimal.length) || (externalOffset < 0))
1634
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1635
"convertExternalDecimalToPackedDecimal is trying to access externalDecimal[" + externalOffset + "] to externalDecimal[" + (externalOffset + CommonData.getExternalByteCounts(precision, decimalType) - 1) + "], " +
1636
" but valid indices are from 0 to " + (externalDecimal.length - 1) + ".");
1637
1638
if ((packedOffset < 0) || (packedOffset + ((precision/ 2) + 1) > packedDecimal.length))
1639
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1640
"convertExternalDecimalToPackedDecimal is trying to access packedDecimal[" + packedOffset + "] to packedDecimal[" + (packedOffset + (precision/ 2)) + "], " +
1641
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
1642
1643
convertExternalDecimalToPackedDecimal_(externalDecimal, externalOffset, packedDecimal,
1644
packedOffset, precision, decimalType);
1645
}
1646
1647
private static void convertExternalDecimalToPackedDecimal_(
1648
byte[] externalDecimal, int externalOffset, byte[] packedDecimal,
1649
int packedOffset, int precision, int decimalType) {
1650
1651
boolean isNegative = isExternalDecimalSignNegative(externalDecimal, externalOffset, precision, decimalType);
1652
1653
if (decimalType < EXTERNAL_DECIMAL_MIN
1654
|| decimalType > EXTERNAL_DECIMAL_MAX)
1655
throw new IllegalArgumentException("invalid decimalType");
1656
if (precision <= 0)
1657
throw new IllegalArgumentException("negative precision");
1658
1659
int end = packedOffset + precision / 2;
1660
1661
// deal with sign leading
1662
if (decimalType == EBCDIC_SIGN_SEPARATE_LEADING) {
1663
externalOffset++;
1664
}
1665
1666
// handle even precision
1667
if (precision % 2 == 0) {
1668
packedDecimal[packedOffset++] = (byte) (externalDecimal[externalOffset++] & CommonData.LOWER_NIBBLE_MASK);
1669
}
1670
1671
for (int i = packedOffset; i < end; i++) {
1672
byte top = (byte) ((externalDecimal[externalOffset++] & CommonData.LOWER_NIBBLE_MASK) << 4);
1673
byte bottom = (byte) (externalDecimal[externalOffset++] & CommonData.LOWER_NIBBLE_MASK);
1674
packedDecimal[i] = (byte) (top | bottom);
1675
}
1676
1677
// deal with the last digit
1678
packedDecimal[end] = (byte) ((externalDecimal[externalOffset] & CommonData.LOWER_NIBBLE_MASK) << 4);
1679
1680
// Compute the sign
1681
if (isNegative)
1682
packedDecimal[end] |= CommonData.PACKED_MINUS;
1683
else
1684
packedDecimal[end] |= CommonData.PACKED_PLUS;
1685
}
1686
1687
/**
1688
* Convert an External Decimal in a byte array to a BigInteger. The sign of the input External Decimal is assumed to
1689
* to be positive unless the sign nibble or byte (depending on <code>decimalType</code>) contains one of the
1690
* negative sign codes, in which case the sign of the input External Decimal is interpreted as negative.
1691
*
1692
* Overflow can happen if the External Decimal value does not fit into the BigInteger. In this case, when
1693
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1694
* invalid result is returned.
1695
*
1696
* @param externalDecimal
1697
* byte array that holds the Packed Decimal to be converted
1698
* @param offset
1699
* offset in <code>externalDecimal</code> where the Packed Decimal is located
1700
* @param precision
1701
* number of decimal digits. Maximum valid precision is 253
1702
* @param checkOverflow
1703
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
1704
* specified precision (overflow)
1705
* @param decimalType
1706
* constant value indicating the type of External Decimal
1707
*
1708
* @return BigInteger the resulting BigInteger
1709
*
1710
* @throws ArrayIndexOutOfBoundsException
1711
* if an invalid array access occurs
1712
* @throws NullPointerException
1713
* if <code>externalDecimal</code> is null
1714
* @throws ArithmeticException
1715
* if <code>checkOverflow</code> is true and the result overflows
1716
* @throws IllegalArgumentException
1717
* if <code>decimalType</code> or <code>precision</code> is invalid, or the digital part of the input is
1718
* invalid.
1719
*/
1720
public static BigInteger convertExternalDecimalToBigInteger(
1721
byte[] externalDecimal, int offset, int precision,
1722
boolean checkOverflow, int decimalType) {
1723
byte[] packedDecimal = new byte[precision / 2 + 1];
1724
convertExternalDecimalToPackedDecimal(externalDecimal, offset,
1725
packedDecimal, 0, precision, decimalType);
1726
1727
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0, precision,true, true);
1728
if (cc != 0)
1729
throw new IllegalArgumentException("The input External Decimal is not valid.");
1730
1731
return convertPackedDecimalToBigDecimal(packedDecimal, 0, precision, 0,
1732
checkOverflow).toBigInteger();
1733
}
1734
1735
/**
1736
* Converts an External Decimal in a byte array to a BigDecimal. The sign of the input External Decimal is assumed
1737
* to be positive unless the sign nibble or byte (depending on <code>decimalType</code>) contains one of the
1738
* negative sign codes, in which case the sign of the input External Decimal is interpreted as negative.
1739
*
1740
* Overflow can happen if the External Decimal value does not fit into the BigDecimal. In this case, when
1741
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1742
* invalid result is returned.
1743
*
1744
* @param externalDecimal
1745
* byte array holding the External Decimal to be converted
1746
* @param offset
1747
* offset in <code>externalDecimal</code> where the External Decimal is located
1748
* @param precision
1749
* number of decimal digits. Maximum valid precision is 253
1750
* @param scale
1751
* scale of the BigDecimal
1752
* @param checkOverflow
1753
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
1754
* specified precision (overflow)
1755
* @param decimalType
1756
* constant value that indicates the type of External Decimal
1757
*
1758
* @return BigDecimal the resulting BigDecimal
1759
*
1760
* @throws NullPointerException
1761
* if <code>externalDecimal</code> is null
1762
* @throws ArrayIndexOutOfBoundsException
1763
* if an invalid array access occurs
1764
* @throws ArithmeticException
1765
* if <code>checkOverflow</code> is true and the result overflows
1766
* @throws IllegalArgumentException
1767
* if <code>checkOverflow</code> is true and the Packed Decimal is in an invalid format, or the digital
1768
* part of the input is invalid.
1769
*/
1770
public static BigDecimal convertExternalDecimalToBigDecimal(
1771
byte[] externalDecimal, int offset, int precision, int scale,
1772
boolean checkOverflow, int decimalType) {
1773
if (precision <= 9) {
1774
return BigDecimal.valueOf(
1775
convertExternalDecimalToInteger(externalDecimal, offset, precision,
1776
checkOverflow, decimalType), scale);
1777
} else if (precision <= 18) {
1778
return BigDecimal.valueOf(
1779
convertExternalDecimalToLong(externalDecimal, offset, precision,
1780
checkOverflow, decimalType), scale);
1781
}
1782
1783
byte[] packedDecimal = new byte[precision / 2 + 1];
1784
convertExternalDecimalToPackedDecimal(externalDecimal, offset,
1785
packedDecimal, 0, precision, decimalType);
1786
1787
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0, precision,true, true);
1788
if (cc != 0)
1789
throw new IllegalArgumentException("The input External Decimal is not valid.");
1790
1791
return convertPackedDecimalToBigDecimal(packedDecimal, 0, precision,
1792
scale, checkOverflow);
1793
}
1794
1795
private static boolean isExternalDecimalSignNegative(byte[] externalDecimal, int externalOffset,
1796
int precision, int decimalType)
1797
{
1798
byte signByte = 0;
1799
switch (decimalType)
1800
{
1801
case EBCDIC_SIGN_EMBEDDED_LEADING:
1802
signByte = (byte) (externalDecimal[externalOffset] & EXTERNAL_HIGH_MASK);
1803
if (signByte == CommonData.EXTERNAL_EMBEDDED_SIGN_MINUS ||
1804
signByte == CommonData.EXTERNAL_EMBEDDED_SIGN_MINUS_ALTERNATE_B)
1805
return true;
1806
break;
1807
1808
case EBCDIC_SIGN_EMBEDDED_TRAILING:
1809
signByte = (byte)(externalDecimal[externalOffset + precision - 1] & EXTERNAL_HIGH_MASK);
1810
if (signByte == CommonData.EXTERNAL_EMBEDDED_SIGN_MINUS ||
1811
signByte == CommonData.EXTERNAL_EMBEDDED_SIGN_MINUS_ALTERNATE_B)
1812
return true;
1813
break;
1814
1815
case EBCDIC_SIGN_SEPARATE_LEADING:
1816
signByte = externalDecimal[externalOffset];
1817
if (signByte == CommonData.EXTERNAL_SIGN_MINUS)
1818
return true;
1819
break;
1820
1821
case EBCDIC_SIGN_SEPARATE_TRAILING:
1822
signByte = externalDecimal[externalOffset + precision];
1823
if (signByte == CommonData.EXTERNAL_SIGN_MINUS)
1824
return true;
1825
break;
1826
1827
default:
1828
throw new IllegalArgumentException("Invalid decimal sign type.");
1829
}
1830
1831
return false;
1832
}
1833
1834
/**
1835
* Converts a Unicode Decimal value in a char array into a binary integer. The sign of the input Unicode Decimal is
1836
* assumed to be positive unless the sign char contains the negative sign code, in which case the sign of the
1837
* input Unicode Decimal is interpreted as negative.
1838
*
1839
* Overflow can happen if the Unicode Decimal value does not fit into a binary int. In this case, when
1840
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1841
* invalid result is returned.
1842
*
1843
* @param unicodeDecimal
1844
* char array which contains the Unicode Decimal value
1845
* @param offset
1846
* the offset where the Unicode Decimal value is located
1847
* @param precision
1848
* number of decimal digits. Maximum valid precision is 253
1849
* @param checkOverflow
1850
* if true an <code>ArithmeticException</code> or <code>IllegalArgumentException</code> may be thrown
1851
* @param unicodeType
1852
* constant value indicating the type of External Decimal
1853
*
1854
* @return int the resulting binary integer
1855
*
1856
* @throws NullPointerException
1857
* if <code>unicodeDecimal</code> is null
1858
* @throws ArrayIndexOutOfBoundsException
1859
* if an invalid array access occurs
1860
* @throws ArithmeticException
1861
* if <code>checkOverflow</code> is true and the result does not fit into a long (overflow)
1862
* @throws IllegalArgumentException
1863
* if <code>precision</code> or <code>decimalType</code> is invalid, or the digital part of the input is
1864
* invalid.
1865
*/
1866
public static int convertUnicodeDecimalToInteger(char[] unicodeDecimal,
1867
int offset, int precision, boolean checkOverflow, int unicodeType) {
1868
int size = unicodeType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
1869
if ((offset + size > unicodeDecimal.length) || (offset < 0))
1870
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1871
"convertUnicodeDecimalToInteger is trying to access unicodeDecimal[" + offset + "] to unicodeDecimal[" + (offset + size - 1) + "], " +
1872
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
1873
1874
if (JITIntrinsicsEnabled()) {
1875
byte[] packedDecimal = new byte[precision / 2 + 1];
1876
convertUnicodeDecimalToPackedDecimal_(unicodeDecimal, offset,
1877
packedDecimal, 0, precision, unicodeType);
1878
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0,
1879
precision, true, true);
1880
if (cc != 0)
1881
throw new IllegalArgumentException(
1882
"The input Unicode is not valid");
1883
1884
return convertPackedDecimalToInteger_(packedDecimal, 0, precision,
1885
checkOverflow);
1886
} else {
1887
return convertUnicodeDecimalToInteger_(unicodeDecimal, offset,
1888
precision, checkOverflow, unicodeType);
1889
}
1890
}
1891
1892
private static int convertUnicodeDecimalToInteger_(char[] unicodeDecimal,
1893
int offset, int precision, boolean checkOverflow, int unicodeType) {
1894
// Validate precision
1895
if (precision <= 0)
1896
throw new IllegalArgumentException("invalid precision");
1897
1898
// Validate decimalType and determine sign
1899
boolean positive = true;
1900
switch (unicodeType) {
1901
case UNICODE_UNSIGNED:
1902
break;
1903
case UNICODE_SIGN_SEPARATE_LEADING:
1904
positive = unicodeDecimal[offset++] == UNICODE_SIGN_PLUS;
1905
break;
1906
case UNICODE_SIGN_SEPARATE_TRAILING:
1907
positive = unicodeDecimal[offset + precision] == UNICODE_SIGN_PLUS;
1908
break;
1909
default:
1910
throw new IllegalArgumentException("invalid decimalType");
1911
}
1912
1913
int end = offset + precision;
1914
long val = 0;
1915
1916
// Trim leading 0s
1917
for (; offset < end && unicodeDecimal[offset] == UNICODE_ZERO; offset++) {
1918
precision--;
1919
}
1920
1921
// Preemptive check overflow: 10 digits in Integer.MAX_VALUE
1922
if (checkOverflow && precision > 10) {
1923
throw new ArithmeticException(
1924
"Decimal overflow - Unicode Decimal too large for an int");
1925
}
1926
1927
// Working in negatives as Integer.MIN_VALUE has a greater
1928
// distance from 0 than Integer.MAX_VALUE
1929
for ( ; offset < end; ++offset) {
1930
val *= 10;
1931
val -= unicodeDecimal[offset] & CommonData.LOWER_NIBBLE_MASK;
1932
}
1933
1934
// Normalize result
1935
val = positive ? -val : val;
1936
1937
// Check overflow
1938
if (checkOverflow && (val > Integer.MAX_VALUE || val < Integer.MIN_VALUE)) {
1939
throw new ArithmeticException(
1940
"Decimal overflow - Unicode Decimal too large for a int");
1941
}
1942
1943
return (int) val;
1944
}
1945
1946
/**
1947
* Converts a Unicode Decimal value in a char array into a binary long. The sign of the input Unicode Decimal is
1948
* assumed to be positive unless the sign char contains the negative sign code, in which case the sign of the
1949
* input Unicode Decimal is interpreted as negative.
1950
*
1951
* Overflow can happen if the Unicode Decimal value does not fit into a binary long. In this case, when
1952
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
1953
* invalid result is returned.
1954
*
1955
* @param unicodeDecimal
1956
* char array which contains the External Decimal value
1957
* @param offset
1958
* offset of the first byte of the Unicode Decimal in <code>unicodeDecimal</code>
1959
* @param precision
1960
* number of Unicode Decimal digits. Maximum valid precision is 253
1961
* @param checkOverflow
1962
* if true an <code>ArithmeticException</code> will be thrown when
1963
* @param unicodeType
1964
* constant value indicating the type of External Decimal
1965
*
1966
* @return long the resulting binary long value
1967
*
1968
* @throws NullPointerException
1969
* if <code>unicodeDecimal</code> is null
1970
* @throws ArrayIndexOutOfBoundsException
1971
* if an invalid array access occurs
1972
* @throws ArithmeticException
1973
* if <code>checkOverflow</code> is true and the result does not fit into a long (overflow)
1974
* @throws IllegalArgumentException
1975
* if <code>unicodeType</code> or <code>precision</code> is invalid, or the digital part of the input is
1976
* invalid.
1977
*/
1978
public static long convertUnicodeDecimalToLong(char[] unicodeDecimal,
1979
int offset, int precision, boolean checkOverflow, int unicodeType) {
1980
int size = unicodeType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
1981
if ((offset + size > unicodeDecimal.length) || (offset < 0))
1982
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
1983
"convertUnicodeDecimalToLong is trying to access unicodeDecimal[" + offset + "] to unicodeDecimal[" + (offset + size - 1) + "], " +
1984
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
1985
1986
if (JITIntrinsicsEnabled()) {
1987
byte[] packedDecimal = new byte[precision / 2 + 1];
1988
convertUnicodeDecimalToPackedDecimal_(unicodeDecimal, offset,
1989
packedDecimal, 0, precision, unicodeType);
1990
1991
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0, precision,true, true);
1992
if (cc != 0)
1993
throw new IllegalArgumentException("The input Unicode is not valid");
1994
1995
return convertPackedDecimalToLong_(packedDecimal, 0, precision,
1996
checkOverflow);
1997
} else {
1998
return convertUnicodeDecimalToLong_(unicodeDecimal, offset, precision, checkOverflow, unicodeType);
1999
}
2000
}
2001
2002
private static long convertUnicodeDecimalToLong_(char[] unicodeDecimal,
2003
int offset, int precision, boolean checkOverflow, int unicodeType) {
2004
// Validate precision
2005
if (precision <= 0)
2006
throw new IllegalArgumentException("invalid precision");
2007
2008
// Validate decimalType and determine sign
2009
boolean positive = true;
2010
switch (unicodeType) {
2011
case UNICODE_UNSIGNED:
2012
break;
2013
case UNICODE_SIGN_SEPARATE_LEADING:
2014
positive = unicodeDecimal[offset++] == UNICODE_SIGN_PLUS;
2015
break;
2016
case UNICODE_SIGN_SEPARATE_TRAILING:
2017
positive = unicodeDecimal[offset + precision] == UNICODE_SIGN_PLUS;
2018
break;
2019
default:
2020
throw new IllegalArgumentException("invalid decimalType");
2021
}
2022
2023
int end = offset + precision;
2024
long val = 0;
2025
2026
// Trim leading 0s
2027
for (; offset < end && unicodeDecimal[offset] == UNICODE_ZERO; offset++) {
2028
precision--;
2029
}
2030
2031
// Preemptive check overflow: 19 digits in Long.MAX_VALUE
2032
if (checkOverflow && precision > 19) {
2033
throw new ArithmeticException(
2034
"Decimal overflow - Unicode Decimal too large for a long");
2035
}
2036
2037
// Working in negatives as Long.MIN_VALUE has a greater
2038
// distance from 0 than Long.MAX_VALUE
2039
for ( ; offset < end; ++offset) {
2040
val *= 10;
2041
val -= unicodeDecimal[offset] & CommonData.LOWER_NIBBLE_MASK;
2042
}
2043
2044
// Normalize result
2045
val = positive ? -val : val;
2046
2047
// Check overflow
2048
if (checkOverflow) {
2049
if (positive && val < 0) {
2050
throw new ArithmeticException(
2051
"Decimal overflow - Unicode Decimal too large for a long");
2052
} else if (!positive && val > 0) {
2053
throw new ArithmeticException(
2054
"Decimal overflow - Unicode Decimal too small for a long");
2055
}
2056
}
2057
2058
return val;
2059
}
2060
2061
/**
2062
* Converts an Unicode Decimal in a char array to a Packed Decimal in a byte array. If the digital part of the input
2063
* Unicode Decimal is not valid then the digital part of the output will not be valid. The sign of the input Unicode
2064
* Decimal is assumed to be positive unless the sign byte contains the negative sign code, in which case the sign
2065
* of the input Unicode Decimal is interpreted as negative.
2066
*
2067
* @param unicodeDecimal
2068
* char array that holds the Unicode Decimal to be converted
2069
* @param unicodeOffset
2070
* offset in <code>unicodeDecimal</code> at which the Unicode Decimal is located
2071
* @param packedDecimal
2072
* byte array that will hold the Packed Decimal on a successful return
2073
* @param packedOffset
2074
* offset in <code>packedDecimal</code> where the Packed Decimal is expected to be located
2075
* @param precision
2076
* number of decimal digits
2077
* @param decimalType
2078
* constant value indicating the type of External Decimal
2079
*
2080
* @throws ArrayIndexOutOfBoundsException
2081
* if an invalid array access occurs
2082
* @throws NullPointerException
2083
* if <code>packedDecimal</code> or <code>unicodeDecimal</code> are null
2084
* @throws IllegalArgumentException
2085
* if <code>precision</code> or <code>decimalType</code> is invalid
2086
*/
2087
public static void convertUnicodeDecimalToPackedDecimal(
2088
char[] unicodeDecimal, int unicodeOffset, byte[] packedDecimal,
2089
int packedOffset, int precision, int decimalType) {
2090
int size = decimalType == DecimalData.UNICODE_UNSIGNED ? precision : precision + 1;
2091
if ((unicodeOffset + size > unicodeDecimal.length) || (unicodeOffset < 0))
2092
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
2093
"convertUnicodeDecimalToPackedDecimal is trying to access unicodeDecimal[" + unicodeOffset + "] to unicodeDecimal[" + (unicodeOffset + size - 1) + "], " +
2094
" but valid indices are from 0 to " + (unicodeDecimal.length - 1) + ".");
2095
if ((packedOffset < 0) || (packedOffset + ((precision/ 2) + 1) > packedDecimal.length))
2096
throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +
2097
"convertUnicodeDecimalToPackedDecimal is trying to access packedDecimal[" + packedOffset + "] to packedDecimal[" + (packedOffset + (precision/ 2)) + "], " +
2098
" but valid indices are from 0 to " + (packedDecimal.length - 1) + ".");
2099
2100
convertUnicodeDecimalToPackedDecimal_(unicodeDecimal, unicodeOffset,
2101
packedDecimal, packedOffset, precision, decimalType);
2102
}
2103
2104
private static void convertUnicodeDecimalToPackedDecimal_(
2105
char[] unicodeDecimal, int unicodeOffset, byte[] packedDecimal,
2106
int packedOffset, int precision, int decimalType) {
2107
2108
if (precision <= 0)
2109
throw new IllegalArgumentException("invalid precision");
2110
2111
int end = packedOffset + precision / 2;
2112
int signOffset = unicodeOffset;
2113
if (decimalType == UNICODE_SIGN_SEPARATE_LEADING) {
2114
unicodeOffset++;
2115
}
2116
// handle even precision
2117
if (precision % 2 == 0) {
2118
packedDecimal[packedOffset++] = (byte) (unicodeDecimal[unicodeOffset++] & CommonData.LOWER_NIBBLE_MASK);
2119
}
2120
2121
// compute all the intermediate digits
2122
for (int i = packedOffset; i < end; i++) {
2123
byte top = (byte) ((unicodeDecimal[unicodeOffset++] & CommonData.LOWER_NIBBLE_MASK) << 4);
2124
byte bottom = (byte) (unicodeDecimal[unicodeOffset++] & CommonData.LOWER_NIBBLE_MASK);
2125
packedDecimal[i] = (byte) (top | bottom);
2126
}
2127
packedDecimal[end] = (byte) ((unicodeDecimal[unicodeOffset++] & CommonData.LOWER_NIBBLE_MASK) << 4);
2128
2129
switch (decimalType)
2130
{
2131
case UNICODE_SIGN_SEPARATE_LEADING:
2132
if (unicodeDecimal[signOffset] == '-')
2133
packedDecimal[end] |= CommonData.PACKED_MINUS;
2134
else
2135
packedDecimal[end] |= CommonData.PACKED_PLUS;
2136
break;
2137
2138
case UNICODE_SIGN_SEPARATE_TRAILING:
2139
if (unicodeDecimal[unicodeOffset] == '-')
2140
packedDecimal[end] |= CommonData.PACKED_MINUS;
2141
else
2142
packedDecimal[end] |= CommonData.PACKED_PLUS;
2143
break;
2144
2145
case UNICODE_UNSIGNED:
2146
packedDecimal[end] |= CommonData.PACKED_PLUS;
2147
break;
2148
2149
default:
2150
throw new IllegalArgumentException("invalid decimalType");
2151
}
2152
}
2153
2154
/**
2155
* Convert a Unicode Decimal in a char array to a BigInteger. The sign of the input Unicode Decimal is assumed to
2156
* be positive unless the sign byte contains the negative sign code, in which case the sign of the input Unicode
2157
* Decimal is interpreted as negative.
2158
*
2159
* Overflow can happen if the Unicode Decimal value does not fit into a binary long. In this case, when
2160
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2161
* invalid result is returned.
2162
*
2163
* @param unicodeDecimal
2164
* char array that holds the Packed Decimal to be converted
2165
* @param offset
2166
* offset into <code>unicodeDecimal</code> where the Packed Decimal is located
2167
* @param precision
2168
* number of decimal digits. Maximum valid precision is 253
2169
* @param checkOverflow
2170
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2171
* specified precision (overflow)
2172
* @param decimalType
2173
* constant value indicating the type of External Decimal
2174
*
2175
* @return BigInteger the resulting BigInteger
2176
*
2177
* @throws ArrayIndexOutOfBoundsException
2178
* if an invalid array access occurs
2179
* @throws NullPointerException
2180
* if <code>unicodeDecimal</code> is null
2181
* @throws ArithmeticException
2182
* if <code>checkOverflow</code> is true and the result overflows
2183
* @throws IllegalArgumentException
2184
* if <code>decimalType</code> or <code>precision</code> is invalid, or the digital part of the input is
2185
* invalid.
2186
*/
2187
public static BigInteger convertUnicodeDecimalToBigInteger(
2188
char[] unicodeDecimal, int offset, int precision,
2189
boolean checkOverflow, int decimalType) {
2190
byte[] packedDecimal = new byte[precision / 2 + 1];
2191
convertUnicodeDecimalToPackedDecimal(unicodeDecimal, offset,
2192
packedDecimal, 0, precision, decimalType);
2193
2194
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0, precision,true, true);
2195
if (cc != 0)
2196
throw new IllegalArgumentException("The input Unicode is not valid");
2197
2198
return convertPackedDecimalToBigDecimal(packedDecimal, 0, precision, 0,
2199
checkOverflow).toBigInteger();
2200
}
2201
2202
/**
2203
* Converts a Unicode Decimal in a char array to a BigDecimal. The sign of the input Unicode Decimal is assumed to
2204
* to be positive unless the sign byte contains the negative sign code, in which case the sign of the input Unicode
2205
* Decimal is interpreted as negative.
2206
*
2207
* Overflow can happen if the Unicode Decimal value does not fit into the BigDecimal. In this case, when
2208
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2209
* invalid result is returned.
2210
*
2211
* @param unicodeDecimal
2212
* char array that holds the Unicode Decimal
2213
* @param offset
2214
* offset in <code>unicodeDecimal</code> where the Unicode Decimal is located
2215
* @param precision
2216
* number of decimal digits. Maximum valid precision is 253
2217
* @param scale
2218
* scale of the returned BigDecimal
2219
* @param checkOverflow
2220
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2221
* specified precision (overflow)
2222
* @param decimalType
2223
* constant value indicating the type of External Decimal
2224
*
2225
* @return BigDecimal
2226
*
2227
* @throws NullPointerException
2228
* if <code>unicodeDecimal</code> is null
2229
* @throws ArrayIndexOutOfBoundsException
2230
* if an invalid array access occurs
2231
* @throws ArithmeticException
2232
* if <code>checkOverflow</code> is true and the result overflows
2233
* @throws IllegalArgumentException
2234
* if <code>checkOverflow</code> is true and the Packed Decimal is in an invalid format, or the digital
2235
* part of the input is invalid.
2236
*/
2237
public static BigDecimal convertUnicodeDecimalToBigDecimal(
2238
char[] unicodeDecimal, int offset, int precision, int scale,
2239
boolean checkOverflow, int decimalType) {
2240
byte[] packedDecimal = new byte[precision / 2 + 1];
2241
convertUnicodeDecimalToPackedDecimal(unicodeDecimal, offset,
2242
packedDecimal, 0, precision, decimalType);
2243
2244
2245
int cc = PackedDecimal.checkPackedDecimal(packedDecimal, 0, precision,true, true);
2246
if (cc != 0)
2247
throw new IllegalArgumentException("The input Unicode is not valid");
2248
2249
return convertPackedDecimalToBigDecimal(packedDecimal, 0, precision,
2250
scale, checkOverflow);
2251
}
2252
2253
/**
2254
* Converts a BigInteger value into a Packed Decimal in a byte array
2255
*
2256
* Overflow can happen if the BigInteger does not fit into the byte array. In this case, when
2257
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2258
* invalid result is returned.
2259
*
2260
* @param bigIntegerValue
2261
* BigInteger value to be converted
2262
* @param packedDecimal
2263
* byte array which will hold the Packed Decimal on a successful return
2264
* @param offset
2265
* offset into <code>packedDecimal</code> where the Packed Decimal is expected to be located
2266
* @param precision
2267
* number of decimal digits. Maximum valid precision is 253s
2268
* @param checkOverflow
2269
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2270
* specified precision (overflow)
2271
*
2272
* @throws NullPointerException
2273
* if <code>packedDecimal</code> is null
2274
* @throws ArrayIndexOutOfBoundsException
2275
* if an invalid array access occurs
2276
* @throws ArithmeticException
2277
* if <code>checkOverflow</code> is true and the result overflows
2278
*/
2279
public static void convertBigIntegerToPackedDecimal(
2280
BigInteger bigIntegerValue, byte[] packedDecimal, int offset,
2281
int precision, boolean checkOverflow) {
2282
convertBigDecimalToPackedDecimal(new BigDecimal(bigIntegerValue),
2283
packedDecimal, offset, precision, checkOverflow);
2284
}
2285
2286
/**
2287
* Converts a BigInteger value into an External Decimal in a byte array
2288
*
2289
* Overflow can happen if the BigInteger does not fit into the byte array. In this case, when
2290
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2291
* invalid result is returned.
2292
*
2293
* @param bigIntegerValue
2294
* BigInteger value to be converted
2295
* @param externalDecimal
2296
* byte array which will hold the External Decimal on a successful return
2297
* @param offset
2298
* offset into <code>externalDecimal</code> where the External Decimal is expected to be located
2299
* @param precision
2300
* number of decimal digits. Maximum valid precision is 253
2301
* @param checkOverflow
2302
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2303
* specified precision (overflow)
2304
* @param decimalType
2305
* constant value that indicates the type of External Decimal
2306
*
2307
* @throws NullPointerException
2308
* if <code>externalDecimal</code> is null
2309
* @throws ArrayIndexOutOfBoundsException
2310
* if an invalid array access occurs
2311
* @throws ArithmeticException
2312
* if <code>checkOverflow</code> is true and the result overflows
2313
* @throws IllegalArgumentException
2314
* if <code>decimalType</code> or <code>precision</code> is invalid
2315
*/
2316
public static void convertBigIntegerToExternalDecimal(
2317
BigInteger bigIntegerValue, byte[] externalDecimal, int offset,
2318
int precision, boolean checkOverflow, int decimalType) {
2319
byte[] packedDecimal = new byte[precision / 2 + 1];
2320
convertBigDecimalToPackedDecimal(new BigDecimal(bigIntegerValue),
2321
packedDecimal, 0, precision, checkOverflow);
2322
convertPackedDecimalToExternalDecimal(packedDecimal, 0,
2323
externalDecimal, offset, precision, decimalType);
2324
}
2325
2326
/**
2327
* Converts a BigInteger value to a Unicode Decimal in a char array
2328
*
2329
* Overflow can happen if the BigInteger does not fit into the char array. In this case, when
2330
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2331
* invalid result is returned.
2332
*
2333
* @param bigIntegerValue
2334
* BigInteger value to be converted
2335
* @param unicodeDecimal
2336
* char array that will hold the Unicode decimal on a successful return
2337
* @param offset
2338
* offset into <code>unicodeDecimal</code> where the Unicode Decimal is expected to be located
2339
* @param precision
2340
* number of decimal digits. Maximum valid precision is 253
2341
* @param checkOverflow
2342
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2343
* specified precision (overflow)
2344
* @param decimalType
2345
* constant indicating the type of External Decimal
2346
*
2347
* @throws NullPointerException
2348
* if <code>unicodeDecimal</code> is null
2349
* @throws ArrayIndexOutOfBoundsException
2350
* if an invalid array access occurs
2351
* @throws ArithmeticException
2352
* if <code>checkOverflow</code> is true and the result overflows
2353
* @throws IllegalArgumentException
2354
* if <code>decimalType</code> or <code>precision</code> is invalid
2355
*/
2356
public static void convertBigIntegerToUnicodeDecimal(
2357
BigInteger bigIntegerValue, char[] unicodeDecimal, int offset,
2358
int precision, boolean checkOverflow, int decimalType) {
2359
byte[] packedDecimal = new byte[precision / 2 + 1];
2360
convertBigDecimalToPackedDecimal(new BigDecimal(bigIntegerValue),
2361
packedDecimal, 0, precision, checkOverflow);
2362
convertPackedDecimalToUnicodeDecimal(packedDecimal, 0, unicodeDecimal,
2363
offset, precision, decimalType);
2364
}
2365
2366
/**
2367
* Converts a BigDecimal into a Packed Decimal in a byte array
2368
*
2369
* Overflow can happen if the BigDecimal does not fit into the result byte array. In this case, when
2370
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2371
* invalid result is returned.
2372
*
2373
* @param bigDecimalValue
2374
* the BigDecimal value to be converted
2375
* @param packedDecimal
2376
* byte array which will hold the Packed Decimal on a successful return
2377
* @param offset
2378
* desired offset in <code>packedDecimal</code> where the Packed Decimal is expected to be located
2379
* @param precision
2380
* number of decimal digits. Maximum valid precision is 253
2381
* @param checkOverflow
2382
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2383
* specified precision (overflow)
2384
*
2385
* @throws NullPointerException
2386
* if <code>packedDecimal</code> is null
2387
* @throws ArrayIndexOutOfBoundsException
2388
* if an invalid array access occurs
2389
* @throws ArithmeticException
2390
* if <code>checkOverflow</code> is true and the result overflows
2391
*/
2392
public static void convertBigDecimalToPackedDecimal(
2393
BigDecimal bigDecimalValue, byte[] packedDecimal, int offset,
2394
int precision, boolean checkOverflow) {
2395
2396
int bdprec = bigDecimalValue.precision();
2397
if (bdprec <=9)
2398
{
2399
convertIntegerToPackedDecimal((int) bigDecimalValue.unscaledValue().longValue(),
2400
packedDecimal, offset, precision, checkOverflow);
2401
return;
2402
}
2403
if (bdprec <= 18)
2404
{
2405
convertLongToPackedDecimal(bigDecimalValue.unscaledValue().longValue(),
2406
packedDecimal, offset, precision, checkOverflow);
2407
return;
2408
}
2409
2410
slowBigDecimalToSignedPacked(bigDecimalValue, packedDecimal, offset,
2411
precision, checkOverflow);
2412
}
2413
2414
/**
2415
* Converts a BigDecimal value to an External Decimal in a byte array
2416
*
2417
* Overflow can happen if the BigDecimal does not fit into the result byte array. In this case, when
2418
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2419
* invalid result is returned.
2420
*
2421
* @param bigDecimalValue
2422
* BigDecimal value to be converted
2423
* @param externalDecimal
2424
* byte array that will hold the External Decimal on a successful return
2425
* @param offset
2426
* offset in <code>externalDecimal</code> where the External Decimal is expected to be located
2427
* @param precision
2428
* number of decimal digits. Maximum valid precision is 253
2429
* @param checkOverflow
2430
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2431
* specified precision (overflow)
2432
* @param decimalType
2433
* constant value indicating the External Decimal type
2434
*
2435
* @throws NullPointerException
2436
* if <code>externalDecimal</code> is null
2437
* @throws ArrayIndexOutOfBoundsException
2438
* if an invalid array access occurs
2439
* @throws ArithmeticException
2440
* if <code>checkOverflow</code> is true and the result overflows
2441
* @throws IllegalArgumentException
2442
* if <code>precision</code> or <code>decimalType</code> is invalid
2443
*/
2444
public static void convertBigDecimalToExternalDecimal(
2445
BigDecimal bigDecimalValue, byte[] externalDecimal, int offset,
2446
int precision, boolean checkOverflow, int decimalType) {
2447
2448
int bdprec = bigDecimalValue.precision();
2449
if (bdprec <=9)
2450
{
2451
convertIntegerToExternalDecimal((int) bigDecimalValue.unscaledValue().longValue(),
2452
externalDecimal, offset, precision, checkOverflow, decimalType);
2453
return;
2454
}
2455
if (bdprec <= 18)
2456
{
2457
convertLongToExternalDecimal(bigDecimalValue.unscaledValue().longValue(),
2458
externalDecimal, offset, precision, checkOverflow, decimalType);
2459
return;
2460
}
2461
2462
byte[] packedDecimal = new byte[precision / 2 + 1];
2463
convertBigDecimalToPackedDecimal(bigDecimalValue, packedDecimal, 0,
2464
precision, checkOverflow);
2465
convertPackedDecimalToExternalDecimal(packedDecimal, 0,
2466
externalDecimal, offset, precision, decimalType);
2467
}
2468
2469
/**
2470
* Converts a BigDecimal value to a Unicode Decimal in a char array
2471
*
2472
* Overflow can happen if the BigDecimal does not fit into the result char array. In this case, when
2473
* <code>checkOverflow</code> is true an <code>ArithmeticException</code> is thrown, when false a truncated or
2474
* invalid result is returned.
2475
*
2476
* @param bigDecimalValue
2477
* BigDecimal value to be converted
2478
* @param unicodeDecimal
2479
* char array which will hold the Unicode Decimal on a successful return
2480
* @param offset
2481
* offset in <code>unicodeDecimal</code> where the Unicode Decimal is expected to be located
2482
* @param precision
2483
* number of decimal digits. Maximum valid precision is 253
2484
* @param checkOverflow
2485
* if true an <code>ArithmeticException</code> will be thrown if the decimal value does not fit in the
2486
* specified precision (overflow)
2487
* @param decimalType
2488
* constant value that indicates the type of External Decimal
2489
*
2490
* @throws NullPointerException
2491
* if <code>unicodeDecimal</code> is null
2492
* @throws ArrayIndexOutOfBoundsException
2493
* if an invalid array access occurs
2494
* @throws ArithmeticException
2495
* if <code>checkOverflow</code> is true and the result overflows
2496
* @throws IllegalArgumentException
2497
* if <code>decimalType</code> or <code>precision</code> is invalid
2498
*/
2499
public static void convertBigDecimalToUnicodeDecimal(
2500
BigDecimal bigDecimalValue, char[] unicodeDecimal, int offset,
2501
int precision, boolean checkOverflow, int decimalType) {
2502
byte[] packedDecimal = new byte[precision / 2 + 1];
2503
convertBigDecimalToPackedDecimal(bigDecimalValue, packedDecimal, 0,
2504
precision, checkOverflow);
2505
convertPackedDecimalToUnicodeDecimal(packedDecimal, 0, unicodeDecimal,
2506
offset, precision, decimalType);
2507
}
2508
2509
// below is code taken from BigDecimalConverters
2510
// these are special functions recognized by the jit
2511
private static boolean DFPFacilityAvailable() {
2512
return false;
2513
}
2514
2515
private static boolean DFPUseDFP() {
2516
return false;
2517
}
2518
2519
private static BigDecimal createZeroBigDecimal() {
2520
return new BigDecimal(0); // don't use BigDecimal.ZERO, need a new
2521
// object every time
2522
}
2523
2524
private static boolean DFPConvertPackedToDFP(BigDecimal bd, long pack,
2525
int scale, boolean signed) {
2526
return false;
2527
}
2528
2529
private static long DFPConvertDFPToPacked(long dfpValue, boolean signed) {
2530
return Long.MAX_VALUE;
2531
}
2532
2533
private final static int getflags() {
2534
return 0x3;
2535
}
2536
2537
private final static long getlaside(BigDecimal bd) {
2538
return Long.MIN_VALUE;
2539
}
2540
2541
private static BigDecimal slowSignedPackedToBigDecimal(byte[] byteArray,
2542
int offset, int precision, int scale, boolean exceptions) {
2543
2544
2545
int length = (precision + 2) / 2;
2546
2547
int sn = byteArray[offset + length - 1] & CommonData.LOWER_NIBBLE_MASK;
2548
2549
char[] temp = new char[length * 2];
2550
temp[0] = (sn == 0x0D || sn == 0x0B) ? '-' : '0';
2551
for (int i = 0; i < length - 1; i++) {
2552
temp[2 * i + 1] = (char) ('0' + (byteArray[i + offset] >>> 4 & CommonData.LOWER_NIBBLE_MASK));
2553
temp[2 * i + 2] = (char) ('0' + (byteArray[i + offset] & CommonData.LOWER_NIBBLE_MASK));
2554
}
2555
temp[length * 2 - 1] = (char) ('0' + (byteArray[offset + length - 1] >>> 4 & CommonData.LOWER_NIBBLE_MASK));
2556
2557
return new BigDecimal(new BigInteger(new String(temp)), scale);
2558
}
2559
2560
private static void slowBigDecimalToSignedPacked(BigDecimal bd,
2561
byte[] byteArray, int offset, int precision, boolean checkOverflow) {
2562
2563
// digits are right justified in the return byte array
2564
if (checkOverflow && precision < bd.precision())
2565
throw new ArithmeticException(
2566
"Decimal overflow - precision of result Packed Decimal lesser than BigDecimal precision");
2567
2568
BigInteger value = bd.unscaledValue();
2569
char[] buffer = value.abs().toString().toCharArray();
2570
int numDigitsLeft = buffer.length - 1;
2571
int endPosition = numDigitsLeft % 2;
2572
int length = (precision + 2) / 2;
2573
int index = length - 2;
2574
2575
// take care of the sign nibble and the right most digit
2576
byteArray[offset + length - 1] = (byte) ((buffer[numDigitsLeft] - '0') << 4);
2577
byteArray[offset + length - 1] |= ((value.signum() == -1) ? 0x0D : 0x0C);
2578
2579
// compact 2 digits into each byte
2580
for (int i = numDigitsLeft - 1; i >= endPosition
2581
&& (offset + index >= 0); i -= 2, --index) {
2582
byteArray[offset + index] = (byte) (buffer[i] - '0');
2583
byteArray[offset + index] |= (byte) ((buffer[i - 1] - '0') << 4);
2584
}
2585
2586
// if there's a left over digit, put it in the last byte
2587
if (endPosition > 0 && (offset + index >= 0)) {
2588
byteArray[offset + index] = (byte) (buffer[0] - '0');
2589
}
2590
}
2591
2592
/*
2593
* Converts the Packed Decimal to equivalent long (in hex).
2594
*/
2595
private static long convertPackedToLong(byte[] byteArray, int offset,
2596
int length) {
2597
if (length == 8) {
2598
return ByteArrayUnmarshaller.readLong(byteArray, offset, true);
2599
} else if (length == 4) {
2600
// We need to zero extend the int to long.
2601
return ((long) ByteArrayUnmarshaller.readInt(byteArray, offset,
2602
true)) & 0xFFFFFFFFl;
2603
}
2604
2605
else {
2606
// slow way to load value into dfp
2607
long value = 0;
2608
for (int i = 0; i < length; ++i) {
2609
value = value << 8;
2610
value += ((long) (byteArray[offset + i] & 0xFF));
2611
}
2612
return value;
2613
}
2614
}
2615
2616
private static void convertLongToPacked(long value, byte[] byteArray,
2617
int offset, int length) {
2618
if (length == 8) {
2619
ByteArrayMarshaller.writeLong(value, byteArray, offset, true);
2620
} else if (length == 4) {
2621
ByteArrayMarshaller.writeInt((int) value, byteArray, offset, true);
2622
} else {
2623
for (int i = length - 1; i <= 0; i--) {
2624
byteArray[offset + i] = (byte) ((value) & 0xFF);
2625
value = value >> 8;
2626
}
2627
}
2628
}
2629
2630
}
2631
2632