Path: blob/master/jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/PackedDecimal.java
12575 views
/*[INCLUDE-IF DAA]*/1/*******************************************************************************2* Copyright (c) 2013, 2021 IBM Corp. and others3*4* This program and the accompanying materials are made available under5* the terms of the Eclipse Public License 2.0 which accompanies this6* distribution and is available at https://www.eclipse.org/legal/epl-2.0/7* or the Apache License, Version 2.0 which accompanies this distribution and8* is available at https://www.apache.org/licenses/LICENSE-2.0.9*10* This Source Code may also be made available under the following11* Secondary Licenses when the conditions for such availability set12* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU13* General Public License, version 2 with the GNU Classpath14* Exception [1] and GNU General Public License, version 2 with the15* OpenJDK Assembly Exception [2].16*17* [1] https://www.gnu.org/software/classpath/license.html18* [2] http://openjdk.java.net/legal/assembly-exception.html19*20* 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-exception21*******************************************************************************/22package com.ibm.dataaccess;2324import java.math.BigInteger;25import java.util.Arrays;2627import com.ibm.dataaccess.CommonData;2829/**30* Arithmetic, copying and shifting operations for Packed Decimal data.31*32* @author IBM33* @version $Revision$ on $Date$34*/35public final class PackedDecimal {3637/**38* Private constructor, class contains only static methods.39*/40private PackedDecimal() {41super();42}4344private static final ThreadLocal<PackedDecimalOperand> op1_threadLocal = new ThreadLocal<PackedDecimalOperand>() {45protected PackedDecimalOperand initialValue()46{47return new PackedDecimalOperand();48}49};50private static final ThreadLocal<PackedDecimalOperand> op2_threadLocal = new ThreadLocal<PackedDecimalOperand>() {51protected PackedDecimalOperand initialValue()52{53return new PackedDecimalOperand();54}55};56private static final ThreadLocal<PackedDecimalOperand> sum_threadLocal = new ThreadLocal<PackedDecimalOperand>() {57protected PackedDecimalOperand initialValue()58{59return new PackedDecimalOperand();60}61};6263/**64* Checks the validity of a Packed Decimal, return code indicating the status of the Packed Decimal.65*66* @param byteArray67* the source container array68* @param offset69* starting offset of the Packed Decimal70* @param precision71* precision of the Packed Decimal. Maximum valid precision is 25372* @param ignoreHighNibbleForEvenPrecision73* if true, ignore to check if the top nibble (first 4 bits) of the input is an invalid sign value in the74* case of even precision75* @param canOverwriteHighNibbleForEvenPrecision76* if true, change the high nibble to a zero in case of even precision77* @return the condition code: 0 All digit codes and the sign valid 1 Sign invalid 2 At least one digit code invalid78* 3 Sign invalid and at least one digit code invalid79*80* @throws NullPointerException81* if <code>byteArray</code> is null82* @throws ArrayIndexOutOfBoundsException83* if an invalid array access occurs84*/85public static int checkPackedDecimal(byte[] byteArray, int offset,86int precision, boolean ignoreHighNibbleForEvenPrecision,87boolean canOverwriteHighNibbleForEvenPrecision) {88if ((offset + ((precision / 2) + 1) > byteArray.length) || (offset < 0))89throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +90"checkPackedDecimal is trying to access byteArray[" + offset + "] to byteArray[" + (offset + (precision / 2)) + "]" +91" but valid indices are from 0 to " + (byteArray.length - 1) + ".");9293return checkPackedDecimal_(byteArray, offset, precision,94ignoreHighNibbleForEvenPrecision, canOverwriteHighNibbleForEvenPrecision);95}9697private static int checkPackedDecimal_(byte[] byteArray, int offset,98int precision, boolean ignoreHighNibbleForEvenPrecision,99boolean canOverwriteHighNibbleForEvenPrecision) {100101if (precision < 1)102throw new IllegalArgumentException("Illegal Precision.");103104boolean evenPrecision = precision % 2 == 0 ? true : false;105int signOffset = offset + CommonData.getPackedByteCount(precision) - 1;106107int returnCode = 0;108109if (canOverwriteHighNibbleForEvenPrecision && evenPrecision) {110byteArray[offset] = (byte) (byteArray[offset] & CommonData.LOWER_NIBBLE_MASK);111}112113if (evenPrecision && ignoreHighNibbleForEvenPrecision)114{115if ((byteArray[offset] & CommonData.LOWER_NIBBLE_MASK) > 0x09)116returnCode = 2;117offset++;118}119120121// ordinary checking places here:122int i;123for (i = offset; i < signOffset && byteArray[i] == CommonData.PACKED_ZERO; i++)124;125126for (; i < signOffset; i++)127{128if ((byteArray[i] & CommonData.LOWER_NIBBLE_MASK) > 0x09 ||129(byteArray[i] & CommonData.HIGHER_NIBBLE_MASK & 0xFF) > 0x90)130{131returnCode = 2;132break;133}134}135136if (i == signOffset && (byteArray[signOffset] & CommonData.HIGHER_NIBBLE_MASK & 0xFF) > 0x90)137returnCode = 2;138139// Check sign nibble140if ((byteArray[signOffset] & CommonData.LOWER_NIBBLE_MASK) < 0x0A)141{142returnCode++;143}144return returnCode;145}146147// Assuming canOverwriteMostSignificantEvenNibble == false148/**149* Checks the validity of a Packed Decimal, return code indicating the status of the Packed Decimal. The most150* significant nibble cannot be overwritten.151*152* @param byteArray153* the source container array154* @param offset155* starting offset of the Packed Decimal156* @param precision157* precision of the Packed Decimal158* @param ignoreHighNibbleForEvenPrecision159* if true, ignore the high nibble in the case of even precision160* @return the condition code: 0 All digit codes and the sign valid 1 Sign invalid 2 At least one digit code invalid161* 3 Sign invalid and at least one digit code invalid162*163* @throws NullPointerException164* if <code>byteArray</code> is null165* @throws ArrayIndexOutOfBoundsException166* if an invalid array access occurs167*/168public static int checkPackedDecimal(byte[] byteArray, int offset,169int precision, boolean ignoreHighNibbleForEvenPrecision) {170return checkPackedDecimal(byteArray, offset, precision,171ignoreHighNibbleForEvenPrecision, false);172}173174// Assuming canOverwriteMostSignificantEvenNibble == false175/**176* Checks the validity of a Packed Decimal, return code indicating the status of the Packed Decimal. Don't ignore177* the most significant nibble. The most significant nibble cannot be overwritten.178*179* @param byteArray180* the source container array181* @param offset182* starting offset of the Packed Decimal183* @param precision184* precision of the Packed Decimal185* @return the condition code: 0 All digit codes and the sign valid 1 Sign invalid 2 At least one digit code invalid186* 3 Sign invalid and at least one digit code invalid187*188* @throws NullPointerException189* if <code>byteArray</code> is null190* @throws ArrayIndexOutOfBoundsException191* if an invalid array access occurs192*/193public static int checkPackedDecimal(byte[] byteArray, int offset,194int precision) {195return checkPackedDecimal(byteArray, offset, precision, false, false);196}197198private static void copyRemainingDigits(PackedDecimalOperand op1,199PackedDecimalOperand op2, boolean checkOverflow)200throws ArithmeticException {201PackedDecimalOperand sum = sum_threadLocal.get();202203int bytes;204// copy any high order digits left in larger value to result205if (op1.currentOffset >= op1.offset) {206207bytes = op1.currentOffset - op1.offset + 1;208int sumBytes = sum.currentOffset - sum.offset + 1;209if (bytes >= sumBytes) {210if (checkOverflow && bytes > sumBytes)211throw new ArithmeticException(212"Decimal overflow during addition/subtraction.");213else214bytes = sum.currentOffset - sum.offset + 1;215sum.currentOffset = sum.currentOffset - bytes + 1;216op1.currentOffset += -bytes + 1;217System.arraycopy(op1.byteArray, op1.currentOffset,218sum.byteArray, sum.currentOffset, bytes);219if (sum.precision % 2 == 0) {220byte highNibble = (byte) (sum.byteArray[sum.currentOffset] & CommonData.HIGHER_NIBBLE_MASK);221if (checkOverflow && bytes == sumBytes222&& highNibble != 0x00)223throw new ArithmeticException(224"Decimal overflow during addition/subtraction.");225else226sum.byteArray[sum.currentOffset] &= CommonData.LOWER_NIBBLE_MASK;227}228} else {229sum.currentOffset = sum.currentOffset - bytes + 1;230System.arraycopy(op1.byteArray, op1.offset, sum.byteArray,231sum.currentOffset, bytes);232}233sum.currentOffset--;234235}236// pad high order result bytes with zeros237if (sum.currentOffset >= sum.offset) {238bytes = sum.currentOffset - sum.offset + 1;239Arrays.fill(sum.byteArray, sum.offset, sum.offset + bytes, CommonData.PACKED_ZERO);240}241}242243/**244* Add two Packed Decimals in byte arrays. The sign of an input Packed Decimal is assumed to be positive unless245* the sign nibble contains one of the negative sign codes, in which case the sign of the respective input Packed246* Decimal is interpreted as negative.247*248* @param result249* byte array that will hold the sum of the two operand Packed Decimals250* @param resultOffset251* offset into <code>result</code> where the sum Packed Decimal begins252* @param resultPrecision253* number of Packed Decimal digits for the sum. Maximum valid precision is 253254* @param op1Decimal255* byte array that holds the first operand Packed Decimal.256* @param op1Offset257* offset into <code>op1Decimal</code> where the Packed Decimal. is located258* @param op1Precision259* number of Packed Decimal digits for the first operand. Maximum valid precision is 253260* @param op2Decimal261* byte array that holds the second operand Packed Decimal262* @param op2Offset263* offset into <code>op2Decimal</code> where the Packed Decimal is located264* @param op2Precision265* number of Packed Decimal digits for the second operand. Maximum valid precision is 253266* @param checkOverflow267* check for overflow268*269* @throws NullPointerException270* if any of the byte arrays are null271* @throws ArrayIndexOutOfBoundsException272* if an invalid array access occurs273* @throws ArithmeticException274* if an overflow occurs during the computation of the sum275*/276public static void addPackedDecimal(byte[] result, int resultOffset,277int resultPrecision, byte[] op1Decimal, int op1Offset,278int op1Precision, byte[] op2Decimal, int op2Offset,279int op2Precision, boolean checkOverflow) throws ArithmeticException {280if ((resultOffset + ((resultPrecision / 2) + 1) > result.length) || (resultOffset < 0))281throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +282"addPackedDecimal is trying to access result[" + resultOffset + "] to result[" + (resultOffset + (resultPrecision / 2)) + "]" +283" but valid indices are from 0 to " + (result.length - 1) + ".");284285if ((op1Offset < 0) || (op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length))286throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +287"addPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +288" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");289290if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))291throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +292"addPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +293" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");294295addPackedDecimal_(result, resultOffset, resultPrecision, op1Decimal, op1Offset,296op1Precision, op2Decimal, op2Offset, op2Precision, checkOverflow);297}298299private static void addPackedDecimal_(byte[] result, int resultOffset,300int resultPrecision, byte[] op1Decimal, int op1Offset,301int op1Precision, byte[] op2Decimal, int op2Offset,302int op2Precision, boolean checkOverflow) throws ArithmeticException {303// capture result type information304sum_threadLocal.get().setSumOperand(result, resultOffset,305resultPrecision);306// ignore leading zeros in operand values307op1_threadLocal.get().setOperand(op1Decimal, op1Offset, op1Precision);308op2_threadLocal.get().setOperand(op2Decimal, op2Offset, op2Precision);309// add values310computeValue(checkOverflow);311}312313/**314* Subtracts two Packed Decimals in byte arrays. The sign of an input Packed Decimal is assumed to be positive315* unless the sign nibble contains one of the negative sign codes, in which case the sign of the respective input316* Packed Decimal is interpreted as negative.317*318* @param result319* byte array that will hold the difference of the two operand Packed Decimals320* @param resultOffset321* offset into <code>result</code> where the result Packed Decimal is located322* @param resultPrecision323* number of Packed Decimal digits for the result. Maximum valid precision is 253324* @param op1Decimal325* byte array that holds the first Packed Decimal operand326* @param op1Offset327* offset into <code>op1Decimal</code> where the first operand is located328* @param op1Precision329* number of Packed Decimal digits for the first operand. Maximum valid precision is 253330* @param op2Decimal331* byte array that holds the second Packed Decimal operand332* @param op2Offset333* offset into <code>op2Decimal</code> where the second operand is located334* @param op2Precision335* number of Packed Decimal digits for the second operand. Maximum valid precision is 253336* @param checkOverflow337* check for overflow338*339* @throws NullPointerException340* if any of the byte arrays are null341* @throws ArrayIndexOutOfBoundsException342* if an invalid array access occurs343* @throws ArithmeticException344* if an overflow occurs during the computation of the difference345*/346public static void subtractPackedDecimal(byte[] result, int resultOffset,347int resultPrecision, byte[] op1Decimal, int op1Offset,348int op1Precision, byte[] op2Decimal, int op2Offset,349int op2Precision, boolean checkOverflow) throws ArithmeticException {350if ((resultOffset + ((resultPrecision / 2) + 1) > result.length) || (resultOffset < 0))351throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +352"subtractPackedDecimal is trying to access result[" + resultOffset + "] to result[" + (resultOffset + (resultPrecision / 2)) + "]" +353" but valid indices are from 0 to " + (result.length - 1) + ".");354355if ((op1Offset < 0) || (op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length))356throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +357"subtractPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +358" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");359360if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))361throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +362"subtractPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +363" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");364365subtractPackedDecimal_(result, resultOffset, resultPrecision, op1Decimal, op1Offset,366op1Precision, op2Decimal, op2Offset, op2Precision, checkOverflow);367}368369private static void subtractPackedDecimal_(byte[] result, int resultOffset,370int resultPrecision, byte[] op1Decimal, int op1Offset,371int op1Precision, byte[] op2Decimal, int op2Offset,372int op2Precision, boolean checkOverflow) throws ArithmeticException {373374PackedDecimalOperand sum = sum_threadLocal.get();375PackedDecimalOperand op1 = op1_threadLocal.get();376PackedDecimalOperand op2 = op2_threadLocal.get();377378// capture result type information379sum.setSumOperand(result, resultOffset, resultPrecision);380// ignore leading zeros in operand values381op1.setOperand(op1Decimal, op1Offset, op1Precision);382op2.setOperand(op2Decimal, op2Offset, op2Precision);383// change op2 sign for subtraction384if ((op2.sign & CommonData.LOWER_NIBBLE_MASK) == CommonData.PACKED_PLUS)385op2.sign = (op2.sign & CommonData.HIGHER_NIBBLE_MASK)386| CommonData.PACKED_MINUS;387else388op2.sign = (op2.sign & CommonData.HIGHER_NIBBLE_MASK)389| CommonData.PACKED_PLUS;390// add values391computeValue(checkOverflow);392}393394/**395* Create a positive Packed Decimal representation of zero.396*397* @param byteArray398* byte array which will hold the packed zero399* @param offset400* offset into <code>toBytes</code> where the packed zero begins401* @param len402* length of the packed zero. Maximum valid length is 253403*404* @throws NullPointerException405* if <code>toBytes</code> is null406* @throws ArrayIndexOutOfBoundsException407* if an invalid array access occurs408*/409public static void setPackedZero(byte[] byteArray, int offset, int len) {410int byteLen = CommonData.getPackedByteCount(len);411Arrays.fill(byteArray, offset, offset + byteLen - 1, CommonData.PACKED_ZERO);412byteArray[offset + byteLen - 1] = CommonData.PACKED_PLUS;413}414415private static void computeSum(PackedDecimalOperand op1,416PackedDecimalOperand op2, boolean checkOverflow)417throws ArithmeticException {418419PackedDecimalOperand sum = sum_threadLocal.get();420421boolean carry;// add op2 sign digit to op1 sign digit422sum.indexValue = ((op1.signDigit + op2.signDigit) << 1) & 0x3FF;423sum.byteValue = CommonData.getPackedSumValues(sum.indexValue);424carry = sum.byteValue < op1.signDigit;425sum.byteArray[sum.signOffset] = (byte) (sum.byteValue | (op1.sign & CommonData.LOWER_NIBBLE_MASK));426427// add op2 digits to op1 digits428for (; op2.currentOffset >= op2.offset429&& sum.currentOffset >= sum.offset; op2.currentOffset -= 1, op1.currentOffset -= 1, sum.currentOffset -= 1) {430if (checkOverflow && sum.currentOffset < sum.offset) {431throw new ArithmeticException(432"Decimal overflow in addPackedDecimal.");433}434;435op1.byteValue = op1.byteArray[op1.currentOffset]436& CommonData.INTEGER_MASK;437op2.byteValue = op2.byteArray[op2.currentOffset]438& CommonData.INTEGER_MASK;439op1.indexValue = op1.byteValue440+ (op1.byteValue & CommonData.HIGHER_NIBBLE_MASK);441op2.indexValue = op2.byteValue442+ (op2.byteValue & CommonData.HIGHER_NIBBLE_MASK);443sum.indexValue = op1.indexValue + op2.indexValue;444if (carry)445sum.byteValue = CommonData446.getPackedSumPlusOneValues(sum.indexValue);447else448sum.byteValue = CommonData.getPackedSumValues(sum.indexValue);449carry = (sum.byteValue & CommonData.INTEGER_MASK) < ((op1.byteValue & CommonData.INTEGER_MASK) + (op2.byteValue & CommonData.INTEGER_MASK));450sum.byteArray[sum.currentOffset] = (byte) (sum.byteValue);451}452// if carry, add one to remaining high order digits from op1453for (; carry && op1.currentOffset >= op1.offset454&& sum.currentOffset >= sum.offset; op1.currentOffset -= 1, sum.currentOffset -= 1) {455if (checkOverflow && sum.currentOffset < sum.offset) {456throw new ArithmeticException(457"Decimal overflow in addPackedDecimal");458}459sum.byteValue = CommonData460.getPackedAddOneValues(op1.byteArray[op1.currentOffset])461& CommonData.INTEGER_MASK;462carry = (sum.byteValue == 0x00);463sum.byteArray[sum.currentOffset] = (byte) (sum.byteValue);464}465if (sum.currentOffset < sum.offset) // reached the end466{467if ((carry && checkOverflow)468|| (checkOverflow && op1.currentOffset >= op1.offset))469throw new ArithmeticException(470"Decimal overflow in addPackedDecimal");471else if (sum.precision % 2 == 0) {472if ((byte) (sum.byteArray[sum.offset] & CommonData.HIGHER_NIBBLE_MASK) > (byte) 0x00473&& checkOverflow)474throw new ArithmeticException(475"Decimal overflow in addPackedDecimal");476sum.byteArray[sum.offset] &= CommonData.LOWER_NIBBLE_MASK;477}478return;479}480// if still carry, add high order one to sum481if (carry) { // carry482sum.byteArray[sum.currentOffset] = 1;483sum.currentOffset -= 1;484}485// copy any remaining digits486copyRemainingDigits(op1, op2, checkOverflow);487}488489private static void computeDifference(PackedDecimalOperand op1,490PackedDecimalOperand op2, boolean checkOverflow)491throws ArithmeticException {492493PackedDecimalOperand sum = sum_threadLocal.get();494495boolean borrow;496// compute difference from sign byte497borrow = op1.signDigit < op2.signDigit;498499sum.byteValue = Math.abs((op1.signDigit >> 4) - (op2.signDigit >> 4)500+ 10) % 10;501sum.byteArray[sum.signOffset] = (byte) ((sum.byteValue << 4) | (op1.sign & CommonData.LOWER_NIBBLE_MASK));502503// compute op1 digit values - op2 digit values ;504for (op2.currentOffset = op2.signOffset - 1, op1.currentOffset = op1.signOffset - 1; op2.currentOffset >= op2.offset505&& sum.currentOffset >= sum.offset; op2.currentOffset -= 1, op1.currentOffset -= 1, sum.currentOffset -= 1) {506if (checkOverflow && sum.currentOffset < sum.offset) {507throw new ArithmeticException(508"Decimal overflow in subtractPackedDecimal");509}510511op1.byteValue = op1.byteArray[op1.currentOffset]512& CommonData.INTEGER_MASK;513op2.byteValue = op2.byteArray[op2.currentOffset]514& CommonData.INTEGER_MASK;515op1.indexValue = op1.byteValue516+ (op1.byteValue & CommonData.HIGHER_NIBBLE_MASK);517op2.indexValue = op2.byteValue518+ (op2.byteValue & CommonData.HIGHER_NIBBLE_MASK);519sum.indexValue = (op1.indexValue - op2.indexValue) & 0x3FF;520if (borrow)521sum.byteValue = CommonData522.getPackedDifferenceMinusOneValues(sum.indexValue);523else524sum.byteValue = CommonData525.getPackedDifferenceValues(sum.indexValue);526borrow = (sum.byteValue & CommonData.INTEGER_MASK) > ((op1.byteValue & CommonData.INTEGER_MASK) - (op2.byteValue & CommonData.INTEGER_MASK));527if (sum.currentOffset >= sum.offset)528sum.byteArray[sum.currentOffset] = (byte) (sum.byteValue);529}530// if borrow, subtract one from remaining high order digits531for (; borrow && op1.currentOffset >= op1.offset532&& sum.currentOffset >= sum.offset; op1.currentOffset -= 1, sum.currentOffset -= 1) {533if (checkOverflow && sum.currentOffset < sum.offset) {534throw new ArithmeticException(535"Decimal overflow in subtractPackedDecimal");536}537sum.byteValue = CommonData538.getPackedBorrowOneValues(op1.byteArray[op1.currentOffset])539& CommonData.INTEGER_MASK;540borrow = (sum.byteValue == 0x99);541if (sum.currentOffset >= sum.offset)542sum.byteArray[sum.currentOffset] = (byte) (sum.byteValue);543}544545copyRemainingDigits(op1, op2, checkOverflow);546}547548private static void computeValue(boolean checkOverflow)549throws ArithmeticException {550551PackedDecimalOperand sum = sum_threadLocal.get();552PackedDecimalOperand op1 = op1_threadLocal.get();553PackedDecimalOperand op2 = op2_threadLocal.get();554555if ((op1.sign & CommonData.LOWER_NIBBLE_MASK) == (op2.sign & CommonData.LOWER_NIBBLE_MASK)) {556// signs are same, compute sum of values557// add less bytes to more bytes558if (op1.bytes < op2.bytes)559computeSum(op2, op1, checkOverflow);560else561computeSum(op1, op2, checkOverflow);562} else { // signs are different, compute difference of values563// subtract smaller value from larger value so we will always564// have one to borrow565if (op1.bytes < op2.bytes)566computeDifference(op2, op1, checkOverflow); // op2 has more567// non-zero bytes568else if (op1.bytes > op2.bytes)569computeDifference(op1, op2, checkOverflow); // op2 has more570// non-zero bytes571else {572// compare values to find which is larger.573// adjust offsets at same time - difference between equal bytes574// is 0575findDifference: {576for (; op1.offset < op1.signOffset; op1.offset += 1, op2.offset += 1) {577op1.byteValue = op1.byteArray[op1.offset];578op2.byteValue = op2.byteArray[op2.offset];579if (op1.byteValue != op2.byteValue)580break findDifference;581}582op1.byteValue = op1.byteArray[op1.offset]583& CommonData.HIGHER_NIBBLE_MASK;584op2.byteValue = op2.byteArray[op2.offset]585& CommonData.HIGHER_NIBBLE_MASK;586if (op1.byteValue == op2.byteValue) {587// values are equal, difference is zero588setPackedZero(sum.byteArray, sum.offset, sum.precision);589return;590}591}592if ((op1.byteValue & CommonData.INTEGER_MASK) > (op2.byteValue & CommonData.INTEGER_MASK))593computeDifference(op1, op2, checkOverflow);594else595computeDifference(op2, op1, checkOverflow);596}597}598}599600// The implementations of multiply, divide and remainder are based on601// BigDecimal602// for simplicity and also because it has been shown that they have603// acceptable performance.604605private static final int MULTIPLY = 1, DIVIDE = 2, REMAINDER = 3;606607/**608* Multiplies two Packed Decimals in byte arrays. The sign of an input Packed Decimal is assumed to be positive609* unless the sign nibble contains one of the negative sign codes, in which case the sign of the respective input610* Packed Decimal is interpreted as negative.611*612* @param result613* byte array that will hold the product Packed Decimal614* @param resultOffset615* offset into <code>result</code> where the product Packed Decimal is located616* @param resultPrecision617* the length (number of digits) of the product Packed Decimal. Maximum valid precision is 253618* @param op1Decimal619* byte array that will hold the multiplicand Packed Decimal620* @param op1Offset621* offset into <code>op1Decimal</code> where the multiplicand is located622* @param op1Precision623* the length (number of digits) of the multiplicand Packed Decimal. Maximum valid precision is 253624* @param op2Decimal625* byte array that will hold the multiplier626* @param op2Offset627* offset into <code>op2Decimal</code> where the multiplier is located628* @param op2Precision629* the length (number of digits) of the multiplier Packed Decimal. Maximum valid precision is 253630* @param checkOverflow631* if set to true, Packed Decimals are validated before multiplication and an632* <code>IllegalArgumentException</code> or <code>ArithmeticException</code> may be thrown. If not, the633* result can be invalid634*635* @throws NullPointerException636* if any of the byte arrays are null637* @throws ArrayIndexOutOfBoundsException638* if an invalid array access occurs639* @throws IllegalArgumentException640* if an overflow occurs during multiplication641* @throws ArithmeticException642* if any of the Packed Decimal operands are invalid643*/644public static void multiplyPackedDecimal(byte[] result, int resultOffset,645int resultPrecision, byte[] op1Decimal, int op1Offset,646int op1Precision, // multiplicand647byte[] op2Decimal, int op2Offset, int op2Precision, // multiplier648boolean checkOverflow) {649if ((resultOffset + ((resultPrecision / 2) + 1) > result.length) || (resultOffset < 0))650throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +651"multiplyPackedDecimal is trying to access result[" + resultOffset + "] to result[" + (resultOffset + (resultPrecision / 2)) + "]" +652" but valid indices are from 0 to " + (result.length - 1) + ".");653654if ((op1Offset < 0) || (op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length))655throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +656"multiplyPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +657" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");658659if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))660throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +661"multiplyPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +662" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");663664multiplyPackedDecimal_(result, resultOffset, resultPrecision, op1Decimal, op1Offset,665op1Precision, op2Decimal, op2Offset, op2Precision, checkOverflow);666}667668private static void multiplyPackedDecimal_(byte[] result, int resultOffset,669int resultPrecision, byte[] op1Decimal, int op1Offset,670int op1Precision, // multiplicand671byte[] op2Decimal, int op2Offset, int op2Precision, // multiplier672boolean checkOverflow) {673packedDecimalBinaryOp(MULTIPLY, result, resultOffset, resultPrecision,674op1Decimal, op1Offset, op1Precision, op2Decimal, op2Offset,675op2Precision, checkOverflow);676}677678/**679* Divides two Packed Decimals is byte arrays. The sign of an input Packed Decimal is assumed to be positive680* unless the sign nibble contains one of the negative sign codes, in which case the sign of the respective input681* Packed Decimal is interpreted as negative.682*683* @param result684* byte array that will hold the quotient Packed Decimal685* @param resultOffset686* offset into <code>result</code> where the quotient Packed Decimal is located687* @param resultPrecision688* the length (number of digits) of the quotient Packed Decimal. Maximum valid precision is 253689* @param op1Decimal690* byte array that will hold the dividend Packed Decimal691* @param op1Offset692* offset into <code>op1Decimal</code> where the dividend is located693* @param op1Precision694* the length (number of digits) of the dividend Packed Decimal. Maximum valid precision is 253695* @param op2Decimal696* byte array that will hold the divisor697* @param op2Offset698* offset into <code>op2Decimal</code> where the divisor is located699* @param op2Precision700* the length (number of digits) of the divisor Packed Decimal. Maximum valid precision is 253701* @param checkOverflow702* if set to true, Packed Decimals are validated before division and an703* <code>IllegalArgumentException</code> or <code>ArithmeticException</code> may be thrown. If not, the704* result can be invalid705*706* @throws NullPointerException707* if any of the byte arrays are null708* @throws ArrayIndexOutOfBoundsException709* if an invalid array access occurs710* @throws IllegalArgumentException711* if an overflow occurs during division712* @throws ArithmeticException713* if any of the Packed Decimal operands are invalid714*/715public static void dividePackedDecimal(byte[] result, int resultOffset,716int resultPrecision, byte[] op1Decimal, int op1Offset,717int op1Precision, byte[] op2Decimal, int op2Offset,718int op2Precision, boolean checkOverflow) {719if ((resultOffset + ((resultPrecision / 2) + 1) > result.length) || (resultOffset < 0))720throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +721"dividePackedDecimal is trying to access result[" + resultOffset + "] to result[" + (resultOffset + (resultPrecision / 2)) + "]" +722" but valid indices are from 0 to " + (result.length - 1) + ".");723724if ((op1Offset < 0) || (op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length))725throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +726"dividePackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +727" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");728729if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))730throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +731"dividePackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +732" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");733734dividePackedDecimal_(result, resultOffset, resultPrecision, op1Decimal, op1Offset,735op1Precision, op2Decimal, op2Offset, op2Precision, checkOverflow);736}737738private static void dividePackedDecimal_(byte[] result, int resultOffset,739int resultPrecision, byte[] op1Decimal, int op1Offset,740int op1Precision, byte[] op2Decimal, int op2Offset,741int op2Precision, boolean checkOverflow) {742packedDecimalBinaryOp(DIVIDE, result, resultOffset, resultPrecision,743op1Decimal, op1Offset, op1Precision, op2Decimal, op2Offset,744op2Precision, checkOverflow);745746}747748/**749* Calculates the remainder resulting from the division of two Packed Decimals in byte arrays. The sign of an input750* Packed Decimal is assumed to be positive unless the sign nibble contains one of the negative sign codes, in751* which case the sign of the respective input Packed Decimal is interpreted as negative.752*753* @param result754* byte array that will hold the remainder Packed Decimal755* @param resultOffset756* offset into <code>result</code> where the remainder Packed Decimal is located757* @param resultPrecision758* number of Packed Decimal digits for the remainder. Maximum valid precision is 253759* @param op1Decimal760* byte array that will hold the dividend Packed Decimal761* @param op1Offset762* offset into <code>op1Decimal</code> where the dividend is located763* @param op1Precision764* number of Packed Decimal digits for the dividend. Maximum valid precision is 253765* @param op2Decimal766* byte array that will hold the divisor767* @param op2Offset768* offset into <code>op2Decimal</code> where the divisor is located769* @param op2Precision770* number of Packed Decimal digits for the divisor. Maximum valid precision is 253771* @param checkOverflow772* if set to true, Packed Decimals are validated before division and an773* <code>IllegalArgumentException</code> or <code>ArithmeticException</code> may be thrown. If not, the774* result can be invalid775*776* @throws NullPointerException777* if any of the byte arrays are null778* @throws ArrayIndexOutOfBoundsException779* if an invalid array access occurs780* @throws IllegalArgumentException781* if an overflow occurs during division782* @throws ArithmeticException783* if any of the Packed Decimal operands are invalid784*/785public static void remainderPackedDecimal(byte[] result, int resultOffset,786int resultPrecision, byte[] op1Decimal, int op1Offset,787int op1Precision, byte[] op2Decimal, int op2Offset,788int op2Precision, boolean checkOverflow) {789if ((resultOffset + ((resultPrecision / 2) + 1) > result.length) || (resultOffset < 0))790throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +791"remainderPackedDecimal is trying to access result[" + resultOffset + "] to result[" + (resultOffset + (resultPrecision / 2)) + "]" +792" but valid indices are from 0 to " + (result.length - 1) + ".");793794if ((op1Offset < 0) || (op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length))795throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +796"remainderPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +797" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");798799if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))800throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +801"remainderPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +802" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");803804remainderPackedDecimal_(result, resultOffset, resultPrecision, op1Decimal, op1Offset,805op1Precision, op2Decimal, op2Offset, op2Precision, checkOverflow);806}807private static void remainderPackedDecimal_(byte[] result, int resultOffset,808int resultPrecision, byte[] op1Decimal, int op1Offset,809int op1Precision, byte[] op2Decimal, int op2Offset,810int op2Precision, boolean checkOverflow) {811packedDecimalBinaryOp(REMAINDER, result, resultOffset, resultPrecision,812op1Decimal, op1Offset, op1Precision, op2Decimal, op2Offset,813op2Precision, checkOverflow);814}815816private static BigInteger getBigInteger(byte[] pd, int offset, int length) {817int end = offset + length - 1;818StringBuilder sb = new StringBuilder();819820if ((pd[end] & CommonData.LOWER_NIBBLE_MASK) == 0x0B821|| (pd[end] & CommonData.LOWER_NIBBLE_MASK) == 0x0D)822sb.append('-');823824for (int i = offset; i < end; i++) {825sb.append((char) ('0' + (pd[i] >> 4 & CommonData.LOWER_NIBBLE_MASK)));826sb.append((char) ('0' + (pd[i] & CommonData.LOWER_NIBBLE_MASK)));827}828sb.append((char) ('0' + (pd[end] >> 4 & CommonData.LOWER_NIBBLE_MASK)));829return new BigInteger(sb.toString());830}831832private static void putBigInteger(byte[] pd, int offset, int length,833BigInteger bigInt, boolean checkOverflow)834throws ArithmeticException {835int end = offset + length - 1;836837// could use abs(), but want to avoid creating another BigInteger object838char[] chars = bigInt.toString().toCharArray();839boolean neg = chars[0] == '-';840841int charStart = neg ? 1 : 0;842int charEnd = chars.length - 1;843844pd[end--] = (byte) ((neg ? 0x0D : 0x0C) | (chars[charEnd--] - '0' << 4));845846while (end >= offset) {847byte b = 0;848if (charEnd >= charStart) {849b = (byte) (chars[charEnd--] - '0');850if (charEnd >= charStart) {851b |= chars[charEnd--] - '0' << 4;852}853}854pd[end--] = b;855}856857if (checkOverflow && charEnd < charStart) {858while (charEnd >= charStart) {859if (chars[charEnd--] != '0') {860throw new ArithmeticException(861"Packed Decimal overflow during multiplication/division, non-zero digits lost");862}863}864}865}866867private static void zeroTopNibbleIfEven(byte[] bytes, int offset, int prec) {868if (prec % 2 == 0)869bytes[offset] &= CommonData.LOWER_NIBBLE_MASK;870}871872private static void packedDecimalBinaryOp(int op, byte[] result,873int offsetResult, int precResult, byte[] op1, int op1Offset,874int precOp1, byte[] op2, int op2Offset, int precOp2,875boolean checkOverflow) {876877zeroTopNibbleIfEven(op1, op1Offset, precOp1);878zeroTopNibbleIfEven(op2, op2Offset, precOp2);879880// check for long881switch (op) {882case MULTIPLY:883if (precOp1 + precOp2 <= 18 && precResult <= 18) {884long op1long = DecimalData.convertPackedDecimalToLong(op1,885op1Offset, precOp1, checkOverflow);886long op2long = DecimalData.convertPackedDecimalToLong(op2,887op2Offset, precOp2, checkOverflow);888long resultLong = op1long * op2long;889DecimalData.convertLongToPackedDecimal(resultLong, result,890offsetResult, precResult, checkOverflow);891if (resultLong == 0)892forceSign(result, offsetResult, precResult, op1, op1Offset,893precOp1, op2, op2Offset, precOp2);894return;895}896break;897case DIVIDE:898case REMAINDER:899if (precOp1 <= 18 && precOp2 <= 18 && precResult <= 18) {900long op1long = DecimalData.convertPackedDecimalToLong(op1,901op1Offset, precOp1, checkOverflow);902long op2long = DecimalData.convertPackedDecimalToLong(op2,903op2Offset, precOp2, checkOverflow);904long resultLong;905if (op == DIVIDE)906resultLong = op1long / op2long;907else908resultLong = op1long % op2long;909DecimalData.convertLongToPackedDecimal(resultLong, result,910offsetResult, precResult, checkOverflow);911if (resultLong == 0)912forceSign(result, offsetResult, precResult, op1, op1Offset,913precOp1, op2, op2Offset, precOp2);914return;915}916break;917}918919// long is too small920BigInteger op1BigInt;921BigInteger op2BigInt;922try {923op1BigInt = getBigInteger(op1, op1Offset,924precisionToByteLength(precOp1));925op2BigInt = getBigInteger(op2, op2Offset,926precisionToByteLength(precOp2));927} catch (NumberFormatException e) {928if (checkOverflow)929throw new IllegalArgumentException("Invalid packed data value",930e);931return;932}933934BigInteger resultBigInt = null;935switch (op) {936case MULTIPLY:937resultBigInt = op1BigInt.multiply(op2BigInt);938break;939case DIVIDE:940resultBigInt = op1BigInt.divide(op2BigInt);941break;942case REMAINDER:943resultBigInt = op1BigInt.remainder(op2BigInt);944break;945}946947putBigInteger(result, offsetResult, precisionToByteLength(precResult),948resultBigInt, checkOverflow);949950// force the sign because BigInteger will never produce negative zero951if (BigInteger.ZERO.equals(resultBigInt)) {952forceSign(result, offsetResult, precResult, op1, op1Offset,953precOp1, op2, op2Offset, precOp2);954}955}956957/**958* Using BigInteger or long will never produce negative zero, so we need to959* make sure to set the correct sign code.960*/961private static void forceSign(byte[] result, int offsetResult,962int precResult, byte[] op1, int op1Offset, int precOp1, byte[] op2,963int op2Offset, int precOp2) {964965int endOp1 = op1Offset + precisionToByteLength(precOp1) - 1;966int endOp2 = op2Offset + precisionToByteLength(precOp2) - 1;967int endResult = offsetResult + precisionToByteLength(precResult) - 1;968result[endResult] |= 0x0F;969result[endResult] &= signMask(op1[endOp1], op2[endOp2]);970}971972private static int precisionToByteLength(int precision) {973return (precision + 2) / 2;974}975976private static byte signMask(byte a, byte b) {977return (byte) (sign(a) * sign(b) > 0 ? 0xFC : 0xFD);978}979980private static int sign(byte b) {981return (CommonData.getSign(b & CommonData.LOWER_NIBBLE_MASK) == CommonData.PACKED_MINUS) ? -1 : 1;982}983984/**985* Checks if the first Packed Decimal operand is lesser than the second one.986*987* @param op1Decimal988* byte array that holds the first Packed Decimal operand989* @param op1Offset990* offset into <code>op1Decimal</code> where the first operand is located991* @param op1Precision992* number of Packed Decimal digits for the first operand993* @param op2Decimal994* byte array that holds the second Packed Decimal operand995* @param op2Offset996* offset into <code>op2Decimal</code> where the second operand is located997* @param op2Precision998* number of Packed Decimal digits for the second operand999*1000* @return true if <code>op1Decimal < op2Decimal</code>, false otherwise1001*1002* @throws NullPointerException1003* if any of the byte arrays are null1004* @throws ArrayIndexOutOfBoundsException1005* if an invalid array access occurs1006*/1007public static boolean lessThanPackedDecimal(byte[] op1Decimal,1008int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1009int op2Precision) {1010if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1011throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1012"lessThanPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1013" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");10141015if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1016throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1017"lessThanPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1018" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");10191020return lessThanPackedDecimal_(op1Decimal, op1Offset, op1Precision,1021op2Decimal, op2Offset, op2Precision);1022}10231024private static boolean lessThanPackedDecimal_(byte[] op1Decimal,1025int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1026int op2Precision) {10271028return greaterThanPackedDecimal_(op2Decimal, op2Offset, op2Precision, op1Decimal,1029op1Offset, op1Precision);1030}10311032/**1033* Checks if the first Packed Decimal operand is less than or equal to the second one.1034*1035* @param op1Decimal1036* byte array that holds the first Packed Decimal operand1037* @param op1Offset1038* offset into <code>op1Decimal</code> where the first operand is located1039* @param op1Precision1040* number of Packed Decimal digits for the first operand1041* @param op2Decimal1042* byte array that holds the second Packed Decimal operand1043* @param op2Offset1044* offset into <code>op2Decimal</code> where the second operand is located1045* @param op2Precision1046* number of Packed Decimal digits for the second operand1047*1048* @return true if <code>op1Decimal <= op2Decimal</code>, false otherwise1049*1050* @throws NullPointerException1051* if any of the byte arrays are null1052* @throws ArrayIndexOutOfBoundsException1053* if an invalid array access occurs1054*/1055public static boolean lessThanOrEqualsPackedDecimal(byte[] op1Decimal,1056int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1057int op2Precision) {1058if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1059throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1060"lessThanOrEqualsPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1061" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");10621063if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1064throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1065"lessThanOrEqualsPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1066" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");10671068return lessThanOrEqualsPackedDecimal_(op1Decimal, op1Offset, op1Precision,1069op2Decimal, op2Offset, op2Precision);10701071}1072private static boolean lessThanOrEqualsPackedDecimal_(byte[] op1Decimal,1073int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1074int op2Precision) {1075return !greaterThanPackedDecimal_(op1Decimal, op1Offset, op1Precision,1076op2Decimal, op2Offset, op2Precision);1077}10781079/**1080* Checks if the first Packed Decimal operand is greater than the second one.1081*1082* @param op1Decimal1083* byte array that holds the first Packed Decimal operand1084* @param op1Offset1085* offset into <code>op1Decimal</code> where the first operand is located1086* @param op1Precision1087* number of Packed Decimal digits for the first operand1088* @param op2Decimal1089* byte array that holds the second Packed Decimal operand1090* @param op2Offset1091* offset into <code>op2Decimal</code> where the second operand is located1092* @param op2Precision1093* number of Packed Decimal digits for the second operand1094*1095* @return true if <code>op1Decimal > op2Decimal</code>, false otherwise1096*1097* @throws NullPointerException1098* if any of the byte arrays are null1099* @throws ArrayIndexOutOfBoundsException1100* if an invalid array access occurs1101*/1102public static boolean greaterThanPackedDecimal(byte[] op1Decimal,1103int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1104int op2Precision) {1105if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1106throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1107"greaterThanPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1108" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");11091110if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1111throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1112"greaterThanPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1113" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");11141115return greaterThanPackedDecimal_(op1Decimal, op1Offset, op1Precision,1116op2Decimal, op2Offset, op2Precision);1117}11181119private static boolean greaterThanPackedDecimal_(byte[] op1Decimal,1120int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1121int op2Precision) {11221123if (op1Precision < 1 || op2Precision < 1)1124throw new IllegalArgumentException("Invalid Precision for an operand");11251126//boolean result = false;1127int op1End = op1Offset + precisionToByteLength(op1Precision) - 1;1128int op2End = op2Offset + precisionToByteLength(op2Precision) - 1;11291130//check signs, if a different signs we're done!1131byte op1Sign = CommonData.getSign((byte)(op1Decimal[op1End] & CommonData.LOWER_NIBBLE_MASK));1132byte op2Sign = CommonData.getSign((byte)(op2Decimal[op2End] & CommonData.LOWER_NIBBLE_MASK));11331134boolean isNegative = (op1Sign == CommonData.PACKED_MINUS) ? true : false;11351136//skip leading zeros1137for (; op1Offset < op1End && op1Decimal[op1Offset] == CommonData.PACKED_ZERO; op1Offset++)1138;1139for (; op2Offset < op2End && op2Decimal[op2Offset] == CommonData.PACKED_ZERO; op2Offset++)1140;11411142int op1Length = op1End - op1Offset;1143int op2Length = op2End - op2Offset;11441145if (op1Length + op2Length == 0)1146{1147int op1LastDigit = (op1Decimal[op1Offset] >> 4) & CommonData.LOWER_NIBBLE_MASK;1148int op2LastDigit = (op2Decimal[op2Offset] >> 4) & CommonData.LOWER_NIBBLE_MASK;1149if (op1LastDigit + op2LastDigit == 0) //both zeros1150return false;1151if (op1Sign < op2Sign)1152return true;1153else if (op1Sign > op2Sign)1154return false;1155if ((op1LastDigit > op2LastDigit && !isNegative) || (op1LastDigit < op2LastDigit && isNegative))1156return true;11571158return false;1159}11601161if (op1Sign < op2Sign)1162return true;1163else if (op1Sign > op2Sign)1164return false;11651166if ((op1Length > op2Length && !isNegative) || (op1Length < op2Length && isNegative))1167return true;1168else if ((op1Length < op2Length && !isNegative) || (op1Length > op2Length && isNegative))1169return false;11701171while(op1Offset < op1End && op2Offset < op2End)1172{1173int op1Byte = (op1Decimal[op1Offset] & 0xFF);1174int op2Byte = (op2Decimal[op2Offset] & 0xFF);1175int opDiff = op1Byte - op2Byte;11761177if ((opDiff < 0 && !isNegative) || (opDiff > 0 && isNegative))1178return false;1179else if ((opDiff > 0 && !isNegative) || (opDiff < 0 && isNegative))1180return true;11811182op1Offset++;1183op2Offset++;1184}1185//last digit1186int op1LastDigit = (op1Decimal[op1Offset] >> 4) & CommonData.LOWER_NIBBLE_MASK;1187int op2LastDigit = (op2Decimal[op2Offset] >> 4) & CommonData.LOWER_NIBBLE_MASK;11881189if ((op1LastDigit > op2LastDigit && !isNegative) || (op1LastDigit < op2LastDigit && isNegative))1190return true;11911192return false; //either equal or less than11931194}11951196/**1197* Checks if the first Packed Decimal operand is greater than or equal to the second one.1198*1199* @param op1Decimal1200* byte array that holds the first Packed Decimal operand1201* @param op1Offset1202* offset into <code>op1Decimal</code> where the first operand is located1203* @param op1Precision1204* number of Packed Decimal digits for the first operand1205* @param op2Decimal1206* byte array that holds the second Packed Decimal operand1207* @param op2Offset1208* offset into <code>op2Decimal</code> where the second operand is located1209* @param op2Precision1210* number of Packed Decimal digits for the second operand1211*1212* @return true if <code>op1Decimal >= op2Decimal</code>, false otherwise1213*1214* @throws NullPointerException1215* if any of the byte arrays are null1216* @throws ArrayIndexOutOfBoundsException1217* if an invalid array access occurs1218*/1219public static boolean greaterThanOrEqualsPackedDecimal(byte[] op1Decimal,1220int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1221int op2Precision) {1222if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1223throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1224"greaterThanOrEqualsPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1225" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");12261227if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1228throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1229"greaterThanOrEqualsPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1230" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");12311232return greaterThanOrEqualsPackedDecimal_(op1Decimal, op1Offset, op1Precision,1233op2Decimal, op2Offset, op2Precision);1234}12351236private static boolean greaterThanOrEqualsPackedDecimal_(byte[] op1Decimal,1237int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1238int op2Precision) {1239return !lessThanPackedDecimal_(op1Decimal, op1Offset, op1Precision,1240op2Decimal, op2Offset, op2Precision);1241}12421243/**1244* Checks if the two Packed Decimal operands are equal.1245*1246* @param op1Decimal1247* byte array that holds the first Packed Decimal operand1248* @param op1Offset1249* offset into <code>op1Decimal</code> where the first operand is located1250* @param op1Precision1251* number of Packed Decimal digits for the first operand1252* @param op2Decimal1253* byte array that holds the second Packed Decimal operand1254* @param op2Offset1255* offset into <code>op2Decimal</code> where the second operand is located1256* @param op2Precision1257* number of Packed Decimal digits for the second operand1258*1259* @return true if op1Decimal == op2Decimal, false otherwise1260*1261* @throws NullPointerException1262* if any of the byte arrays are null1263* @throws ArrayIndexOutOfBoundsException1264* if an invalid array access occurs1265*/1266public static boolean equalsPackedDecimal(byte[] op1Decimal, int op1Offset,1267int op1Precision, byte[] op2Decimal, int op2Offset, int op2Precision) {1268if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1269throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1270"equalsPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1271" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");12721273if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1274throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1275"equalsPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1276" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");12771278return equalsPackedDecimal_(op1Decimal, op1Offset, op1Precision,1279op2Decimal, op2Offset, op2Precision);1280}12811282private static boolean equalsPackedDecimal_(byte[] op1Decimal, int op1Offset, int op1Precision,1283byte[] op2Decimal, int op2Offset, int op2Precision) {1284if (op1Precision < 1 || op2Precision < 1)1285throw new IllegalArgumentException("Invalid Precision for an operand");12861287int op1End = op1Offset + precisionToByteLength(op1Precision) - 1;1288int op2End = op2Offset + precisionToByteLength(op2Precision) - 1;12891290byte op1Sign = CommonData.getSign((byte) (op1Decimal[op1End] & CommonData.LOWER_NIBBLE_MASK));1291byte op2Sign = CommonData.getSign((byte) (op2Decimal[op2End] & CommonData.LOWER_NIBBLE_MASK));12921293boolean op1IsEven = op1Precision % 2 == 0;1294boolean op2IsEven = op2Precision % 2 == 0;12951296// Must handle the +0 and -0 case1297if (op1Sign != op2Sign)1298return checkZeroBetweenOpOffsetAndOpEnd (op1Decimal, op1Offset, op1End, op1IsEven, true) &&1299checkZeroBetweenOpOffsetAndOpEnd (op2Decimal, op2Offset, op2End, op2IsEven, true);13001301// Check if least significant digit is different1302if ((op1Decimal[op1End] & CommonData.HIGHER_NIBBLE_MASK) !=1303(op2Decimal[op2End] & CommonData.HIGHER_NIBBLE_MASK)) {1304return false;1305}13061307// Avoid decrementing if op1End == op1Offset || op2End == op2Offset1308if (op1End > op1Offset && op2End > op2Offset) {1309op1End--;1310op2End--;1311}13121313while (op1End > op1Offset && op2End > op2Offset) {1314// Check if intermediate digits are different1315if (op1Decimal[op1End] != op2Decimal[op2End])1316return false;13171318op1End--;1319op2End--;1320}13211322// At this point it is true that op1End == op1Offset || op2End == op2Offset1323if (op1End == op1Offset) {1324if (op1IsEven) {1325// Check if most significant digit is different1326if ((op1Decimal[op1End] & CommonData.LOWER_NIBBLE_MASK) ==1327(op2Decimal[op2End] & CommonData.LOWER_NIBBLE_MASK)) {1328return checkZeroBetweenOpOffsetAndOpEnd (op2Decimal, op2Offset, op2End, op2IsEven, !op2IsEven || op2End != op2Offset);1329}1330} else {1331// Check if two most significant digits are different1332if (op1Decimal[op1End] == op2Decimal[op2End])1333return checkZeroBetweenOpOffsetAndOpEnd (op2Decimal, op2Offset, op2End, op2IsEven, false);1334}1335} else {1336if (op2IsEven) {1337// Check if most significant digit is different1338if ((op1Decimal[op1End] & CommonData.LOWER_NIBBLE_MASK) ==1339(op2Decimal[op2End] & CommonData.LOWER_NIBBLE_MASK)) {1340return checkZeroBetweenOpOffsetAndOpEnd (op1Decimal, op1Offset, op1End, op1IsEven, !op1IsEven || op1End != op1Offset);1341}1342} else {1343// Check if two most significant digits are different1344if (op1Decimal[op1End] == op2Decimal[op2End])1345return checkZeroBetweenOpOffsetAndOpEnd (op1Decimal, op1Offset, op1End, op1IsEven, false);1346}1347}13481349return false;1350}13511352/**1353* Checks if the Packed Decimal digits between <code>opDecimal[opOffset]</code> and <code>opDecimal[opEnd]</code>1354* (inclusive depending on checkHighNibbleAtOpEnd) are all zeros.1355*1356* @param opDecimal1357* byte array that holds the Packed Decimal operand1358* @param opOffset1359* offset into <code>opDecimal</code> where the first byte of the Packed Decimal is located1360* @param opEnd1361* offset into <code>opDecimal</code> where the last byte to be checked is located1362* @param opIsEven1363* boolean is true if the <code>opDecimal</code> has even precision1364* @param checkHighNibbleAtOpEnd1365* boolean indicates whether the high nibble of <code>opDecimal[opEnd]</code> should be checked that it1366* contains a zero. If false, opEnd will be decremented and this will be the new starting point of the1367* algorithm which verifies that the remaining Packed Decimal digits are zeros.1368*1369* @return true if all digits from opOffset to opEnd are zero, false otherwise1370*1371* @throws ArrayIndexOutOfBoundsException1372* if an invalid array access occurs1373*/1374private static boolean checkZeroBetweenOpOffsetAndOpEnd(byte[] opDecimal, int opOffset, int opEnd, boolean opIsEven, boolean checkHighNibbleAtOpEnd) {1375if (checkHighNibbleAtOpEnd && (opDecimal[opEnd] & CommonData.HIGHER_NIBBLE_MASK) != 0x00)1376return false;13771378if (opEnd-- > opOffset) {1379// Handle intermediate bytes1380while (opEnd > opOffset) {1381if (opDecimal[opEnd--] != 0x00)1382return false;1383}13841385// Handle the most significant nibble/byte1386if (opIsEven) {1387if ((opDecimal[opEnd] & CommonData.LOWER_NIBBLE_MASK) != 0x00)1388return false;1389} else {1390if (opDecimal[opEnd] != 0x00)1391return false;1392}1393}13941395return true;1396}139713981399/**1400* Checks if the two Packed Decimal operands are unequal.1401*1402* @param op1Decimal1403* byte array that holds the first Packed Decimal operand1404* @param op1Offset1405* offset into <code>op1Decimal</code> where the first operand is located1406* @param op1Precision1407* number of Packed Decimal digits for the first operand1408* @param op2Decimal1409* byte array that holds the second Packed Decimal operand1410* @param op2Offset1411* offset into <code>op2Decimal</code> where the second operand is located1412* @param op2Precision1413* number of Packed Decimal digits for the second operand1414*1415* @return true if op1Decimal != op2Decimal, false otherwise1416*1417* @throws NullPointerException1418* if any of the byte arrays are null1419* @throws ArrayIndexOutOfBoundsException1420* if an invalid array access occurs1421*/1422public static boolean notEqualsPackedDecimal(byte[] op1Decimal,1423int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1424int op2Precision) {1425if ((op1Offset + ((op1Precision / 2) + 1) > op1Decimal.length) || (op1Offset < 0))1426throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1427"notEqualsPackedDecimal is trying to access op1Decimal[" + op1Offset + "] to op1Decimal[" + (op1Offset + (op1Precision / 2)) + "]" +1428" but valid indices are from 0 to " + (op1Decimal.length - 1) + ".");14291430if ((op2Offset < 0) || (op2Offset + ((op2Precision / 2) + 1) > op2Decimal.length))1431throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1432"notEqualsPackedDecimal is trying to access op2Decimal[" + op2Offset + "] to op2Decimal[" + (op2Offset + (op2Precision / 2)) + "]" +1433" but valid indices are from 0 to " + (op2Decimal.length - 1) + ".");14341435return notEqualsPackedDecimal_(op1Decimal, op1Offset, op1Precision,1436op2Decimal, op2Offset, op2Precision);1437}14381439private static boolean notEqualsPackedDecimal_(byte[] op1Decimal,1440int op1Offset, int op1Precision, byte[] op2Decimal, int op2Offset,1441int op2Precision) {1442return !equalsPackedDecimal(op1Decimal, op1Offset, op1Precision,1443op2Decimal, op2Offset, op2Precision);1444}14451446private static void roundUpPackedDecimal(byte[] packedDecimal, int offset,1447int end, int precision, int roundingDigit, boolean checkOverflow) {14481449packedDecimal[end] = CommonData.getPackedAddOneSignValues(packedDecimal[end]);14501451if ((byte) (packedDecimal[end] & CommonData.HIGHER_NIBBLE_MASK) == (byte) 0x00)1452{1453byte[] addTenArray = { 0x01, packedDecimal[end] };1454addPackedDecimal(packedDecimal, offset, precision,1455packedDecimal, offset, precision, addTenArray, 0, 2,1456checkOverflow);1457}1458}14591460/**1461* In case of a shift operation, this method is called to ensure if the resulting value of the shift is1462* zero that it is converted to a positive zero, meaning {0x....0C}. This is done to match the expected1463* result in COBOL.1464*/1465private static void checkIfZero(byte[] packedDecimal, int offset, int end, int precision, boolean isEvenPrecision) {14661467if (CommonData.getSign(packedDecimal[end] & CommonData.LOWER_NIBBLE_MASK) != CommonData.PACKED_MINUS)1468return;1469if (isEvenPrecision && ((packedDecimal[offset] & CommonData.LOWER_NIBBLE_MASK) != 0x00))1470return;1471//else if (packedDecimal[offset] != 0x00)1472// return;1473int i = offset;// + 1;1474for (; i < end && packedDecimal[i] == 0x00; i++) ;1475if (i < end)1476return;1477else if ((packedDecimal[end] & CommonData.HIGHER_NIBBLE_MASK) == 0x00)1478packedDecimal[end] = CommonData.PACKED_PLUS;14791480return;1481}14821483/**1484* Right shift, and optionally round, a Packed Decimal. If the resultant is zero,1485* it can either be a positive zero 0x0C or a negative zero 0x0D.1486*1487* @param destination1488* byte array that holds the result of the right shift (and rounding)1489* @param destinationOffset1490* offset into <code>destination</code> where the result Packed Decimal is located1491* @param destinationPrecision1492* number of Packed Decimal digits in the destination1493* @param source1494* byte array that holds the Packed Decimal operand to be right shifted1495* @param sourceOffset1496* offset into <code>source</code> where the operand is located1497* @param sourcePrecision1498* number of Packed Decimal digits in the operand1499* @param shiftAmount1500* amount by which the source will be right shifted1501* @param round1502* if set to true, causes rounding to occur1503* @param checkOverflow1504* if set to true, check for overflow1505*1506* @throws NullPointerException1507* if any of the byte arrays are null1508* @throws ArrayIndexOutOfBoundsException1509* if an invalid array access occurs1510* @throws ArithmeticException1511* if a decimal overflow occurs1512* @throws IllegalArgumentException1513* if the <code>shiftAmount</code> or <code>sourcePrecision</code> parameter is invalid or1514* the <code>source</code> packed decimal contains invalid sign or digit code1515*/1516public static void shiftRightPackedDecimal(byte[] destination,1517int destinationOffset, int destinationPrecision, byte[] source,1518int sourceOffset, int sourcePrecision, int shiftAmount,1519boolean round, boolean checkOverflow) {15201521if ((destinationOffset + ((destinationPrecision / 2) + 1) > destination.length) || (destinationOffset < 0))1522throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1523"shiftRightPackedDecimal is trying to access destinationDecimal[" + destinationOffset + "] to destinationDecimal[" + (destinationOffset + (destinationPrecision / 2)) + "]" +1524" but valid indices are from 0 to " + (destination.length - 1) + ".");15251526if ((sourceOffset < 0) || (sourceOffset + ((sourcePrecision / 2) + 1) > source.length))1527throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1528"shiftRightPackedDecimal is trying to access sourceDecimal[" + sourceOffset + "] to sourceDecimal[" + (sourceOffset + (sourcePrecision / 2)) + "]" +1529" but valid indices are from 0 to " + (source.length - 1) + ".");15301531shiftRightPackedDecimal_(destination, destinationOffset, destinationPrecision,1532source, sourceOffset, sourcePrecision, shiftAmount,1533round, checkOverflow);1534}15351536private static void shiftRightPackedDecimal_(byte[] destination,1537int destinationOffset, int destinationPrecision, byte[] source,1538int sourceOffset, int sourcePrecision, int shiftAmount,1539boolean round, boolean checkOverflow) {15401541// Figure out the sign of the source Packed Decimal1542int end = sourceOffset + precisionToByteLength(sourcePrecision) - 1;1543byte sign = CommonData.getSign(source[end] & CommonData.LOWER_NIBBLE_MASK);1544int newDstOffset = destinationOffset;1545int newDstPrec = destinationPrecision;1546int dstEnd = destinationOffset1547+ precisionToByteLength(destinationPrecision) - 1;1548int highDigit = sourcePrecision - 1;1549int lowDigit = 0;1550int precDiff = sourcePrecision - destinationPrecision;1551;1552int newSrcOffset;1553int newSrcEnd;1554boolean evenPrecision = sourcePrecision % 2 == 0;1555boolean isLeastSigDigitInHighNibble = false;1556boolean isMostSigDigitInLowNibble = false;1557int roundingDigit = 0;1558boolean overRanPD = false; // when the shifting left no digits to copy15591560if (destinationPrecision < 1 || sourcePrecision < 1 || shiftAmount < 0) {1561throw new IllegalArgumentException(1562"Invalid Precisions or shift amount");1563}15641565if(checkPackedDecimal_(source, sourceOffset, sourcePrecision, true, false) != 0) {1566throw new IllegalArgumentException(1567"Invalid sign or digit code in input packed decimal");1568}15691570lowDigit += shiftAmount;1571precDiff -= shiftAmount; // every time we shift, we lose precision1572if (precDiff > 0) // if we still need to lose some precision, take off1573// higher digits1574{1575highDigit -= precDiff;1576if (checkOverflow) {1577int bytes;1578int iter = 0;1579if (evenPrecision) {1580precDiff--;1581iter++;1582if ((byte) (source[sourceOffset] & CommonData.LOWER_NIBBLE_MASK) != 0x00)1583throw new ArithmeticException(1584"Decimal overflow in shiftRightPackedDecimal.");1585}1586bytes = (precDiff) / 2;1587precDiff -= bytes * 2;15881589for (int i = 0; i < bytes; i++) {1590if (source[i + sourceOffset + iter] != 0x00)1591throw new ArithmeticException(1592"Decimal overflow in shiftRightPackedDecimal.");1593}15941595if (precDiff == 11596&& (byte) (source[bytes + sourceOffset] & CommonData.HIGHER_NIBBLE_MASK) != 0x00)1597throw new ArithmeticException(1598"Decimal overflow in shiftRightPackedDecimal.");15991600}1601}1602// add zeros if precDiff < 0, do this at end1603int newDifference = (highDigit + 1) - lowDigit; // might want to deal1604// with diff = 11605if (checkOverflow && newDifference < 1)1606overRanPD = true;1607int highDiff = sourcePrecision - (highDigit + 1);16081609if (evenPrecision && newDifference % 2 == 0) // still even1610{1611if (highDiff % 2 == 0) // both low and high moved even1612{1613isMostSigDigitInLowNibble = true; // the highest digit is on a1614// low nibble1615isLeastSigDigitInHighNibble = true; // the lowest digit is on a1616// high nibble1617newSrcOffset = sourceOffset + (highDiff) / 2;1618newSrcEnd = end - (lowDigit) / 2;1619} else // high moved odd, low moved odd1620{1621newSrcOffset = sourceOffset + (highDiff + 1) / 2;1622newSrcEnd = end - (lowDigit + 1) / 2;1623}1624} else if (evenPrecision && newDifference % 2 != 0) {1625if (highDiff % 2 != 0) // high moved odd, low moved even1626{1627isLeastSigDigitInHighNibble = true;1628newSrcOffset = sourceOffset + (highDiff + 1) / 2;1629newSrcEnd = end - (lowDigit) / 2;1630} else // high moved even, low moved odd, perfect scenario1631{1632isMostSigDigitInLowNibble = true;1633newSrcOffset = sourceOffset + (highDiff) / 2;1634newSrcEnd = end - (lowDigit + 1) / 2;1635}1636} else if (!evenPrecision && newDifference % 2 == 0) {1637if (highDiff % 2 == 0) // high moved even, low moved odd1638{1639newSrcOffset = sourceOffset + (highDiff) / 2;1640newSrcEnd = end - (lowDigit + 1) / 2;1641} else // high moved odd, low moved even1642{1643isMostSigDigitInLowNibble = true; // get this high nibble1644// individually1645isLeastSigDigitInHighNibble = true; // get this lower nibble1646// individually1647newSrcOffset = sourceOffset + (highDiff) / 2;1648newSrcEnd = end - (lowDigit) / 2;1649}1650} else {1651if (highDiff % 2 == 0) // both moved even, perfect scenario1652{1653isLeastSigDigitInHighNibble = true;1654newSrcOffset = sourceOffset + (highDiff) / 2;1655newSrcEnd = end - (lowDigit) / 2;1656} else // both moved odd1657{1658isMostSigDigitInLowNibble = true;1659newSrcOffset = sourceOffset + (highDiff) / 2;1660newSrcEnd = end - (lowDigit + 1) / 2;1661}1662}16631664// find rounding digit1665if (round && shiftAmount > 0 && newDifference > -1) {1666if (isLeastSigDigitInHighNibble)1667roundingDigit = (source[newSrcEnd])1668& CommonData.LOWER_NIBBLE_MASK;1669else1670roundingDigit = (source[newSrcEnd + 1] >> 4)1671& CommonData.LOWER_NIBBLE_MASK;1672;1673}16741675while (precDiff < 0) // deal with dst prec being higher, put leading1676// zeros1677{1678destination[newDstOffset] = 0x00;1679if (newDstPrec % 2 == 0 || precDiff == -1) {1680if (newDstPrec % 2 == 0)1681newDstOffset++;1682precDiff++;1683newDstPrec--;16841685} else if ((precDiff / -2) >= 1) {1686precDiff += 2;1687newDstPrec -= 2;1688newDstOffset++;1689}1690}16911692if (isLeastSigDigitInHighNibble && !overRanPD) // can do arrayCopy,1693// instead of individual1694// nibbles1695{1696if (!isMostSigDigitInLowNibble) // just a simple arrayCopy1697System.arraycopy(source, newSrcOffset, destination,1698newDstOffset, newSrcEnd - newSrcOffset + 1);1699else {1700destination[newDstOffset] = (byte) ((source[newSrcOffset] & CommonData.LOWER_NIBBLE_MASK));1701newDstOffset++;1702newSrcOffset++;1703System.arraycopy(source, newSrcOffset, destination,1704newDstOffset, newSrcEnd - newSrcOffset + 1);1705}1706} else if (!overRanPD) // must copy each nibble over1707{1708int newDstEnd = newDstOffset + precisionToByteLength(newDstPrec)1709- 1;1710byte shiftByte = newSrcEnd < sourceOffset ? 0x00 : source[newSrcEnd];1711destination[newDstEnd] = (byte) ((shiftByte << 4) & CommonData.HIGHER_NIBBLE_MASK);1712newDstEnd--;1713newDstPrec--;1714byte current_nibble = 0;1715while ((newSrcEnd > newSrcOffset) && (newDstEnd > newDstOffset)) {1716current_nibble = (byte) ((source[newSrcEnd] >> 4) & CommonData.LOWER_NIBBLE_MASK);1717newSrcEnd--;1718current_nibble |= (byte) ((source[newSrcEnd] << 4) & CommonData.HIGHER_NIBBLE_MASK);1719destination[newDstEnd] = current_nibble;1720newDstEnd--;1721newDstPrec -= 2;1722}1723if (newDstPrec > 0) {1724destination[newDstEnd] = (byte) ((source[newSrcEnd] >> 4) & CommonData.LOWER_NIBBLE_MASK);1725newSrcEnd--;1726newDstPrec--;1727if (newDstPrec > 0 && isMostSigDigitInLowNibble)1728destination[newDstEnd] |= (byte) ((source[newSrcEnd] << 4) & CommonData.HIGHER_NIBBLE_MASK);1729}1730}1731// sign assignment1732if(overRanPD && roundingDigit < 5 && sign != CommonData.PACKED_PLUS)1733destination[dstEnd] = (byte) ((destination[dstEnd] & CommonData.HIGHER_NIBBLE_MASK) | sign);1734else1735{1736destination[dstEnd] = (byte) ((destination[dstEnd] & CommonData.HIGHER_NIBBLE_MASK) | (sign));1737if (round && roundingDigit >= 5)1738roundUpPackedDecimal(destination, destinationOffset, dstEnd,1739destinationPrecision, roundingDigit, checkOverflow);1740if (checkOverflow || round)1741checkIfZero(destination, destinationOffset, dstEnd, destinationPrecision, (destinationPrecision % 2 == 0 ? true : false));17421743}1744}17451746/**1747* Left shift a Packed Decimal appending zeros to the right. In the absence of overflow, the sign1748* of a zero can either be positive 0x0C or negative 0x0D.1749*1750* @param destination1751* byte array that holds the result of the left shift1752* @param destinationOffset1753* offset into <code>destination</code> where the result Packed Decimal is located1754* @param destinationPrecision1755* number of Packed Decimal digits in the destination1756* @param source1757* byte array that holds the Packed Decimal operand to be left shifted1758* @param sourceOffset1759* offset into <code>source</code> where the operand is located1760* @param sourcePrecision1761* number of Packed Decimal digits in the operand1762* @param shiftAmount1763* amount by which the source will be left shifted1764* @param checkOverflow1765* if set to true, check for overflow1766*1767* @throws NullPointerException1768* if any of the byte arrays are null1769* @throws ArrayIndexOutOfBoundsException1770* if an invalid array access occurs1771* @throws ArithmeticException1772* if a decimal overflow occurs1773* @throws IllegalArgumentException1774* if the <code>shiftAmount</code> or <code>sourcePrecision</code> parameter is invalid or1775* the <code>source</code> packed decimal contains invalid sign or digit code1776*/1777public static void shiftLeftPackedDecimal(byte[] destination,1778int destinationOffset, int destinationPrecision, byte[] source,1779int sourceOffset, int sourcePrecision, int shiftAmount,1780boolean checkOverflow) {1781if ((destinationOffset + ((destinationPrecision / 2) + 1) > destination.length) || (destinationOffset < 0))1782throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1783"shiftLeftPackedDecimal is trying to access destinationDecimal[" + destinationOffset + "] to destinationDecimal[" + (destinationOffset + (destinationPrecision / 2)) + "]" +1784" but valid indices are from 0 to " + (destination.length - 1) + ".");17851786if ((sourceOffset < 0) || (sourceOffset + ((sourcePrecision / 2) + 1) > source.length))1787throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " +1788"shiftLeftPackedDecimal is trying to access sourceDecimal[" + sourceOffset + "] to sourceDecimal[" + (sourceOffset + (sourcePrecision / 2)) + "]" +1789" but valid indices are from 0 to " + (source.length - 1) + ".");17901791shiftLeftPackedDecimal_(destination, destinationOffset, destinationPrecision,1792source, sourceOffset, sourcePrecision, shiftAmount,1793checkOverflow);1794}17951796private static void shiftLeftPackedDecimal_(byte[] destination,1797int destinationOffset, int destinationPrecision, byte[] source,1798int sourceOffset, int sourcePrecision, int shiftAmount,1799boolean checkOverflow) {18001801if (destinationPrecision < 1 || sourcePrecision < 1 || shiftAmount < 0) {1802throw new IllegalArgumentException(1803"Invalid Precisions or shift amount");1804}18051806if(checkPackedDecimal_(source, sourceOffset, sourcePrecision, true, false) != 0) {1807throw new IllegalArgumentException(1808"Invalid sign or digit code in input packed decimal");1809}1810// Figure out the sign of the source Packed Decimal1811int end = sourceOffset + precisionToByteLength(sourcePrecision) - 1;1812byte sign = CommonData.getSign(source[end] & CommonData.LOWER_NIBBLE_MASK);1813boolean overRanPD = false;1814int sourceLength = precisionToByteLength(sourcePrecision);1815int destinationLength = precisionToByteLength(destinationPrecision);1816int shiftByte = shiftAmount / 2;1817int zerosBytesAtFront = 0;1818int totalZeros = 0;18191820// odd Precision1821if (sourcePrecision % 2 != 0) {18221823// Figure out how many 0x00s are at the front starting from the1824// offset1825for (zerosBytesAtFront = 0; zerosBytesAtFront < sourceLength; zerosBytesAtFront++) {1826if (source[zerosBytesAtFront + sourceOffset] != 0x00)1827break;1828}18291830// Figure out if 0x0*1831if ((byte) (source[zerosBytesAtFront + sourceOffset] & CommonData.HIGHER_NIBBLE_MASK) == 0x00)1832totalZeros++;18331834}// even precision1835else {1836// Figure out if bottom nibble of offset is 0 if it is then count1837// how many zero bytes there are at the front1838if ((byte) (source[sourceOffset] & CommonData.LOWER_NIBBLE_MASK) == 0x00) {1839totalZeros++;1840for (int i = 1; i < sourceLength; i++) {1841if (source[i + sourceOffset] != 0x00)1842break;1843else1844zerosBytesAtFront++;1845}1846if ((byte) (source[zerosBytesAtFront + sourceOffset + 1] & CommonData.HIGHER_NIBBLE_MASK) == 0x00)1847totalZeros++;1848}1849}1850// Add up all the zeros1851totalZeros += zerosBytesAtFront * 2;18521853// Check for overflow1854if (checkOverflow1855&& ((destinationPrecision + totalZeros < sourcePrecision1856+ shiftAmount)))1857throw new ArithmeticException(1858"Overflow - Destination precision not enough to hold the result of the shift operation");18591860// Zero out destination array in range1861Arrays.fill(destination, destinationOffset, destinationOffset1862+ destinationLength, (byte) 0x00);18631864int startIndexSource = zerosBytesAtFront + sourceOffset;1865int diff = destinationLength - sourceLength;1866int startIndexDestination = destinationOffset + zerosBytesAtFront1867- shiftByte + diff;1868int numberLength = sourceLength - zerosBytesAtFront; // non-zeros digits1869// length in bytes1870int movedSignIndex = startIndexDestination + numberLength - 1; // where1871// the1872// moved1873// sign1874// is1875// displaced1876// to18771878// if all digits are completely shifted out do nothing1879if (destinationPrecision <= shiftAmount)1880{1881if (checkOverflow && sign != CommonData.PACKED_PLUS)1882overRanPD = true;1883}1884else1885{1886// Even shift, can move byte by byte1887if (shiftAmount % 2 == 0)1888{1889if (startIndexDestination < destinationOffset)1890{1891int difference = sourcePrecision - (destinationPrecision - shiftAmount);1892int sourceIndex;1893if (sourcePrecision % 2 == 0)1894sourceIndex = sourceOffset + ((difference + 1) / 2);1895else1896sourceIndex = sourceOffset + (difference / 2);18971898System.arraycopy(source, sourceIndex, destination, destinationOffset, destinationLength1899- (shiftAmount / 2));1900}1901else1902System.arraycopy(source, startIndexSource, destination,1903startIndexDestination, sourceLength - zerosBytesAtFront);19041905// Strip off moved sign1906if (movedSignIndex >= 0)1907destination[movedSignIndex] = (byte) (destination[movedSignIndex] & CommonData.HIGHER_NIBBLE_MASK);1908}1909else1910{1911byte top;1912byte bottom;19131914// Move each nibble by appropriate index1915for (int i = 0; i < numberLength; i++) {1916top = (byte) (source[startIndexSource + i] & CommonData.HIGHER_NIBBLE_MASK);1917bottom = (byte) (source[startIndexSource + i] & CommonData.LOWER_NIBBLE_MASK);19181919if (startIndexDestination - 1 + i >= destinationOffset) {1920destination[startIndexDestination - 1 + i] = (byte) ((top >> 4 & CommonData.LOWER_NIBBLE_MASK) | (byte) (destination[startIndexDestination1921- 1 + i] & CommonData.HIGHER_NIBBLE_MASK));1922}1923if (startIndexDestination + i >= destinationOffset) {1924destination[startIndexDestination + i] = (byte) (bottom << 4);1925}19261927}1928// Strip off moved sign1929if (movedSignIndex >= 0)1930destination[movedSignIndex] = CommonData.PACKED_ZERO;1931}1932}1933int destinationEnd = destinationOffset + destinationLength - 1;1934boolean isDestionationEvenPrecision = destinationPrecision % 2 == 0 ? true : false;1935// Mask top Nibble1936if (isDestionationEvenPrecision)1937destination[destinationOffset] = (byte) (destination[destinationOffset] & CommonData.LOWER_NIBBLE_MASK);1938// Put sign in proper position19391940if (overRanPD)1941destination[destinationEnd] = (byte) (CommonData.PACKED_PLUS | destination[destinationEnd]);1942else1943{1944destination[destinationEnd] = (byte) (sign | destination[destinationEnd]);1945//checkOverflow will generate pdshlOverflow, which will cause it to reset sign. pdshl will not1946if (checkOverflow)1947checkIfZero(destination, destinationOffset, destinationEnd, destinationPrecision, isDestionationEvenPrecision);1948}1949}19501951/**1952* Creates a copy of a Packed Decimal.1953*1954* @param destination1955* byte array representing the destination1956* @param destinationOffset1957* offset into destination <code>destination</code> where the Packed Decimal is located1958* @param destinationPrecision1959* number of Packed Decimal digits for the destination1960* @param source1961* byte array which holds the source Packed Decimal1962* @param sourceOffset1963* offset into <code>source</code> where the Packed Decimal is located1964* @param sourcePrecision1965* number of Packed Decimal digits for the source. Maximum valid precision is 2531966* @param checkOverflow1967* if set to true, moved Packed Decimals are validated, and an <code>IllegalArgumentException</code> or1968* <code>ArithmeticException</code> is thrown if non-zero nibbles are truncated during the move1969* operation. If set to false, no validating is conducted.1970*1971* @throws NullPointerException1972* if any of the byte arrays are null1973* @throws ArrayIndexOutOfBoundsException1974* if an invalid array access occurs1975* @throws IllegalArgumentException1976* if the source Packed Decimal is invalid1977* @throws ArithmeticException1978* if a decimal overflow occurs1979*/1980public static void movePackedDecimal(byte[] destination,1981int destinationOffset, int destinationPrecision, byte[] source,1982int sourceOffset, int sourcePrecision, boolean checkOverflow) {1983shiftLeftPackedDecimal(destination, destinationOffset,1984destinationPrecision, source, sourceOffset,1985sourcePrecision, 0, checkOverflow);19861987}19881989private static class PackedDecimalOperand {19901991PackedDecimalOperand() {1992super();1993}19941995private static final byte PACKED_ZERO = 0x00;19961997byte[] byteArray;1998int offset;1999int precision;2000int bytes;2001int signOffset;2002int currentOffset;2003int signByteValue;2004int sign;2005int signDigit;2006int byteValue;2007int indexValue;20082009/**2010* Sets up the attributes of a Packed Decimal operand. Truncates leading zeros. Captures the sign value.2011*2012* @param byteArray2013* byte array which holds the Packed Decimal2014* @param offset2015* offset in <code>byteArray</code> where the Packed Decimal begins2016* @param precision2017* number of Packed Decimal digits. Maximum valid precision is 2532018*2019* @throws NullPointerException2020* if <code>byteArray</code> is null2021* @throws ArrayIndexOutOfBounds2022* if an invalid array access occurs2023*/2024public void setOperand(byte[] byteArray, int offset, int precision) {20252026this.byteArray = byteArray;2027this.offset = offset;2028this.precision = precision;20292030// truncate leading zeros2031bytes = CommonData.getPackedByteCount(precision);2032signOffset = this.offset + bytes - 1;2033currentOffset = signOffset - 1;2034for (; byteArray[this.offset] == PACKED_ZERO2035&& this.offset < signOffset; this.offset++)2036;2037bytes = signOffset - this.offset + 1;20382039// capture sign values2040signByteValue = this.byteArray[signOffset]2041& CommonData.INTEGER_MASK;2042signDigit = signByteValue & CommonData.HIGHER_NIBBLE_MASK;2043sign = CommonData.getSign(signByteValue & CommonData.LOWER_NIBBLE_MASK);20442045}20462047/**2048* Sets up attributes of a Packed Decimal which is about to be the result operand holding the sum of two Packed2049* Decimal operands. Does not truncate leading zeroes. Does not capture the sign.2050*2051* @param byteArray2052* byte array which holds the Packed Decimal2053* @param offset2054* offset in <code>byteArray</code> where the Packed Decimal begins2055* @param precision2056* number of Packed Decimal digits. Maximum valid precision is 2532057*2058* @throws NullPointerException2059* if <code>byteArray</code> is null2060* @throws ArrayIndexOutOfBounds2061* if an invalid array access occurs2062*/2063public void setSumOperand(byte[] byteArray, int offset, int precision) {20642065this.byteArray = byteArray;2066this.offset = offset;2067this.precision = precision;20682069bytes = CommonData.getPackedByteCount(precision);2070signOffset = this.offset + bytes - 1;2071currentOffset = signOffset - 1;20722073}20742075}20762077}207820792080