From f3da6569ed7f58c21fb9f5aca1fb6dcd722d5dcc Mon Sep 17 00:00:00 2001 From: Shubham Verma Date: Fri, 22 Sep 2023 11:14:48 -0400 Subject: [PATCH] Add checkExternalDecimal API to DAA Signed-off-by: Shubham Verma --- .../com/ibm/dataaccess/ExternalDecimal.java | 172 ++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/ExternalDecimal.java diff --git a/jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/ExternalDecimal.java b/jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/ExternalDecimal.java new file mode 100644 index 00000000000..3fa10bd1be8 --- /dev/null +++ b/jcl/src/openj9.dataaccess/share/classes/com/ibm/dataaccess/ExternalDecimal.java @@ -0,0 +1,172 @@ +/*[INCLUDE-IF DAA]*/ +/******************************************************************************* + * Copyright IBM Corp. and others 2023 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ +package com.ibm.dataaccess; + +public class ExternalDecimal { + /** + * Private constructor, class contains only static methods. + */ + private ExternalDecimal() { + super(); + } + + + private static byte EXTERNAL_DECIMAL_SPACE_SYMBOL = (byte)0x40; + private static int EXTERNAL_DECIMAL_INVALID_DIGIT = 2; + private static int EXTERNAL_DECIMAL_INVALID_SIGN = 1; + + /** + * Checks the validity of a External Decimal, returns an integer indicating the status of the External Decimal. + * + * @param byteArray + * source array. + * @param offset + * starting offset of the External Decimal. + * @param precision + * number of digits to be verified. + * @param decimalType + * constant indicating the type of the decimal. Supported values are + * DecimalData.EBCDIC_SIGN_EMBEDDED_TRAILING, DecimalData.EBCDIC_SIGN_EMBEDDED_LEADING, + * DecimalData.EBCDIC_SIGN_SEPARATE_TRAILING, DecimalData.EBCDIC_SIGN_SEPARATE_LEADING. + * @param bytesWithSpaces + * represents number of left most bytes containing EBCDIC space character if sign is + * embedded, ignored otherwise. + * + * @return the condition code: + * 0 if all digit codes and the sign are valid, + * 1 if the sign is invalid, + * 2 if at least one digit code is invalid, + * 3 if sign and at least one digit code is invalid. + * + * @throws NullPointerException + * if byteArray is null. + * @throws ArrayIndexOutOfBoundsException + * if an invalid array access occurs. + * @throws IllegalArgumentException + * if the precision is less than 1. + * @throws IllegalArgumentException + * if the offset is less than 0. + * @throws IllegalArgumentException + * if decimalType is invalid. + * @throws IllegalArgumentException + * if bytesWithSpaces is less than 0 when sign is embedded. + */ + public static int checkExternalDecimal(byte[] byteArray, int offset, + int precision, int decimalType, int bytesWithSpaces) { + if (precision < 1) + throw new IllegalArgumentException("Precision must be greater than 0."); + + if (offset < 0) + throw new IllegalArgumentException("Offset must be non-negative integer."); + + if (decimalType < DecimalData.EXTERNAL_DECIMAL_MIN || decimalType > DecimalData.EXTERNAL_DECIMAL_MAX) + throw new IllegalArgumentException("Invalid decimalType."); + + final int lengthNeeded = offset + CommonData.getExternalByteCounts(precision, decimalType); + if (lengthNeeded > byteArray.length) + throw new ArrayIndexOutOfBoundsException("Array access index out of bounds. " + + "checkExternalDecimal is trying to access byteArray[" + offset + "] to byteArray[" + (lengthNeeded - 1) + "]" + + " but valid indices are from 0 to " + (byteArray.length - 1) + "."); + + return checkExternalDecimal_(byteArray, offset, precision, decimalType, bytesWithSpaces); + } + + private static int checkExternalDecimal_(byte[] byteArray, int offset, + int precision, int decimalType, int bytesWithSpaces) { + + // Digit is invalid if: + // - number of non space digits is equal to 0 (precision == bytesWithSpaces) + // - number of non space digits is greater than precision (bytesWithSpaces < 0) + if (decimalType == DecimalData.EBCDIC_SIGN_EMBEDDED_TRAILING + && (precision == bytesWithSpaces || bytesWithSpaces < 0)) { + return 3; + } + + int startIndex = offset; // start of sign-digit/zone-digit bytes + int endIndex = startIndex + CommonData.getExternalByteCounts(precision, decimalType) - 1; + boolean isSignEmbedded = false; + byte signByte; + switch (decimalType) { + case DecimalData.EBCDIC_SIGN_EMBEDDED_TRAILING: + isSignEmbedded = true; + signByte = byteArray[endIndex]; + --endIndex; + startIndex += bytesWithSpaces; + break; + case DecimalData.EBCDIC_SIGN_SEPARATE_TRAILING: + signByte = byteArray[endIndex]; + --endIndex; + break; + case DecimalData.EBCDIC_SIGN_EMBEDDED_LEADING: + isSignEmbedded = true; + signByte = byteArray[startIndex]; + ++startIndex; + break; + case DecimalData.EBCDIC_SIGN_SEPARATE_LEADING: + signByte = byteArray[startIndex]; + ++startIndex; + break; + default: + signByte = (byte)0x9F; // random invalid sign byte + break; + } + + int returnCode = 0; + if (decimalType == DecimalData.EBCDIC_SIGN_EMBEDDED_TRAILING && bytesWithSpaces > 0) { + // Validate bytes before digits + for (int index = offset; index < startIndex; ++index) { + // Only EXTERNAL_DECIMAL_SPACE_SYMBOL and F[0-9] are considered valid + if (byteArray[index] != EXTERNAL_DECIMAL_SPACE_SYMBOL + && (((byteArray[index] & (byte)CommonData.HIGHER_NIBBLE_MASK) & 0xF0) != 0xF0 // zone nibble + || (byteArray[index] & (byte)CommonData.LOWER_NIBBLE_MASK) > 0x09)) { // digit nibble + returnCode = EXTERNAL_DECIMAL_INVALID_DIGIT; + } + } + } + + // Validate digits + for (int index = startIndex; index <= endIndex && returnCode < EXTERNAL_DECIMAL_INVALID_DIGIT; ++index) { + // Only valid digits are F[0-9] + if (byteArray[index] == EXTERNAL_DECIMAL_SPACE_SYMBOL + || ((byteArray[index] & (byte)CommonData.HIGHER_NIBBLE_MASK) & 0xF0) != 0xF0 // zone nibble + || (byteArray[index] & (byte)CommonData.LOWER_NIBBLE_MASK) > 0x09) { // digit nibble + returnCode = EXTERNAL_DECIMAL_INVALID_DIGIT; + } + } + + // Validate sign byte + if (isSignEmbedded) { + // Check digit nibble embedded in sign byte + if ((signByte & (byte)CommonData.LOWER_NIBBLE_MASK) > 0x09) returnCode = EXTERNAL_DECIMAL_INVALID_DIGIT; + + // Check sign nibble for preferred and alternate representations + if (((signByte & (byte)CommonData.HIGHER_NIBBLE_MASK) & 0xF0) < 0xA0) returnCode |= EXTERNAL_DECIMAL_INVALID_SIGN; + } + else if (!isSignEmbedded + && signByte != CommonData.EXTERNAL_SIGN_MINUS + && signByte != CommonData.EXTERNAL_SIGN_PLUS) { + returnCode |= EXTERNAL_DECIMAL_INVALID_SIGN; + } + return returnCode; + } +}