| /******************************************************************************* |
| * Copyright (c) 2012 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Randy Rohrbach (Wind River Systems, Inc.) - Copied and modified to create the floating point plugin |
| *******************************************************************************/ |
| package org.eclipse.cdt.debug.ui.memory.floatingpoint; |
| |
| import java.math.BigInteger; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.text.DecimalFormat; |
| import java.text.DecimalFormatSymbols; |
| import java.util.Arrays; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.jface.dialogs.ErrorDialog; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.progress.UIJob; |
| |
| public class FPutilities |
| { |
| private static final int BYTE_MASK = 0xFF; |
| |
| // ANSI C "Smallest" and "largest" negative and positive float and double values |
| |
| public static final float floatNegMax = -3.40282347E+38f; // Largest negative float value; farthest from zero |
| public static final float floatNegMin = -1.17549435E-38f; // Smallest negative float value; closest to zero |
| public static final float floatPosMax = 1.17549435E+38f; // Largest positive float value; farthest from zero |
| public static final float floatPosMin = 3.40282347E-38f; // Smallest positive float value; closest to zero |
| |
| public static final double doubleNegMax = -1.7976931348623157E+308; // Largest positive double value |
| public static final double doubleNegMin = -2.2250738585072014E-308; // Smallest positive double value |
| public static final double doublePosMax = 1.7976931348623157E+308; // Largest positive double value |
| public static final double doublePosMin = 2.2250738585072014E-308; // Smallest positive double value |
| |
| public enum FPDataType |
| { |
| // Value (for persisteance), Bitsize of type, Number of internal precision decimal digits, Default displayed precision |
| |
| FLOAT ( 10, 32, 7, 8), // C/C++ single-precision "float" |
| DOUBLE ( 20, 64, 15, 8), // C/C++ double-precision "double" |
| FLOAT_80 ( 30, 80, 19, 16), // Extended precision |
| FLOAT_96 ( 40, 96, 0, 0), // TODO: unknown internal decimal digit precision; C/C++ extended-precision "long double" |
| |
| // Future work |
| |
| FLOAT_128 (50, 128, 33, 16), // TODO: known values, but not currently implmented |
| FLOAT_256 (60, 256, 0, 0), // TODO: unknown internal decimal digit precision |
| FLOAT_512 (70, 512, 0, 0); // TODO: unknown internal decimal digit precision |
| |
| // Member variables |
| |
| private int value; |
| private int bitsize; |
| private int decimalPrecision; |
| private int displayedPrecision; |
| |
| // Constructor |
| |
| private FPDataType(int value, int bitSize, int precisionDigits, int defaultDisplayPrecision) |
| { |
| this.value = value; |
| this.bitsize = bitSize; |
| this.decimalPrecision = precisionDigits; |
| this.displayedPrecision = defaultDisplayPrecision; |
| } |
| |
| // Getters |
| |
| public int getValue() { return value; } |
| public int getBitsize() { return bitsize; } |
| public int getDecimalPrecision() { return decimalPrecision; } |
| public int getDisplayedPrecision() { return displayedPrecision; } |
| public int getInternalPrecision() { return decimalPrecision; } |
| |
| public int getByteLength() |
| { |
| return bitsize/Byte.SIZE; |
| } |
| } |
| |
| // Byte ordering |
| |
| public enum Endian |
| { |
| // Value |
| |
| LITTLE (10), |
| BIG (20); |
| |
| // Member variables |
| |
| private int value; |
| |
| // Constructor |
| |
| private Endian(int value) |
| { |
| this.value = value; |
| } |
| |
| // Getters |
| |
| public int getValue() { return value; } |
| } |
| |
| // Justification (latent support) |
| |
| public enum Justification |
| { |
| LEFT, |
| RIGHT, |
| CENTER; |
| } |
| |
| // Convert raw float bits to a byte array |
| |
| public static byte[] rawFloatBitsToByteArray(int floatBits) |
| { |
| int byteCount = Integer.SIZE/Byte.SIZE; |
| byte[] result = new byte[byteCount]; |
| |
| for (int index = 0; index < byteCount; index++) |
| { |
| int offset = (result.length - 1 - index) * 8; |
| result[index] = (byte) ((floatBits >>> offset) & BYTE_MASK); |
| } |
| |
| return result; |
| } |
| |
| // Convert raw double bits to a byte array |
| |
| public static byte[] rawDoubleBitsToByteArray(long doubleBits) |
| { |
| int byteCount = Long.SIZE/Byte.SIZE; |
| byte[] result = new byte[byteCount]; |
| |
| for (int index = 0; index < byteCount; index++) |
| { |
| int offset = (result.length - 1 - index) * 8; |
| result[index] = (byte) ((doubleBits >>> offset) & BYTE_MASK); |
| } |
| |
| return result; |
| } |
| |
| // Return a byte array that is in reverse order of the passed-in array parameter |
| |
| public static byte[] reverseByteOrder(byte[] byteArray) |
| { |
| if (byteArray.length == 0) return new byte[0]; |
| |
| byte tempByte = 0; |
| byte[] reversedByteArray = new byte[byteArray.length]; |
| |
| // Copy the array that is passed in to the array that will be returned |
| |
| System.arraycopy(byteArray, 0, reversedByteArray, 0, byteArray.length); |
| |
| // Reverse the bytes |
| |
| for(int start = 0, end = reversedByteArray.length - 1; start < end; ++start, --end) |
| { |
| tempByte = reversedByteArray[start]; |
| reversedByteArray[start] = reversedByteArray[end]; |
| reversedByteArray[end] = tempByte; |
| } |
| |
| return reversedByteArray; |
| } |
| |
| // Convert a representation of a float or double in a byte array to a scientific notation string (Should we use BigDecimal here???) |
| |
| public static String byteArrayToSciNotation(FPDataType dt, boolean isLittleEndian, FPMemoryByte[] memByteArray, int maxDisplayDigits) throws ArithmeticException |
| { |
| int displayedDigits = 8; |
| |
| // If the byte array is not a 32-bit float or 64-bit double, throw an exception. |
| |
| if (memByteArray.length != (FPDataType.FLOAT.getByteLength()) && |
| memByteArray.length != (FPDataType.DOUBLE.getByteLength())) |
| throw new ArithmeticException("Conversion of the floating point number cannot be performed; invalid data type or byte array length."); //$NON-NLS-1$ |
| |
| // Create and initialize a DecimalFormat object for scientific notation. Specify a space |
| // for the preceding plus-sign, which lines up the first significant digit, decimal point |
| // and exponent character. Define the symbol strings for "Not a Number" and "Infinity." |
| |
| DecimalFormat df = new DecimalFormat("0.0E0"); //$NON-NLS-1$ |
| df.setPositivePrefix(" "); //$NON-NLS-1$ |
| |
| DecimalFormatSymbols dfSymbols = new DecimalFormatSymbols(); |
| dfSymbols.setNaN(" "+ FPRenderingMessages.getString("FPRendering.NAN")); //$NON-NLS-1$ //$NON-NLS-2$ |
| dfSymbols.setInfinity(FPRenderingMessages.getString("FPRendering.INFINITY")); //$NON-NLS-1$ |
| df.setDecimalFormatSymbols(dfSymbols); |
| |
| // Set the integer and fraction digits for normalized scientific notation. |
| |
| df.setMinimumIntegerDigits(1); |
| df.setMaximumIntegerDigits(1); |
| |
| if (dt == FPDataType.FLOAT) |
| displayedDigits = Math.min(maxDisplayDigits, FPDataType.FLOAT.getInternalPrecision()); |
| |
| if (dt == FPDataType.DOUBLE) |
| displayedDigits = Math.min(maxDisplayDigits, FPDataType.DOUBLE.getInternalPrecision()); |
| |
| df.setMinimumFractionDigits(displayedDigits - 1); |
| df.setMaximumFractionDigits(displayedDigits - 1); |
| |
| // Convert the byte array to a scientific notation floating point number string (only floats and doubles currently supported) |
| |
| ByteOrder byteOrder = isLittleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; |
| |
| return df.format(dt == FPDataType.FLOAT ? |
| ByteBuffer.wrap(memoryBytesToByteArray(memByteArray)).order(byteOrder).getFloat() : |
| ByteBuffer.wrap(memoryBytesToByteArray(memByteArray)).order(byteOrder).getDouble()); |
| } |
| |
| // Convert a floating point string to a byte array (*** only 'floats' and 'doubles' currently supported ***) |
| |
| public static byte[] floatingStringToByteArray(FPDataType dt, String valueString, int dataTypeBitCount) throws NumberFormatException |
| { |
| // Remove whitespace and check for non-zero length |
| valueString = valueString.trim().replaceAll(" ", ""); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| if (valueString.length() != 0) |
| { |
| // Float handling |
| |
| if (dt == FPDataType.FLOAT || FPDataType.FLOAT.getBitsize() == dataTypeBitCount) |
| { |
| // Convert the string to a float. Check the range. Convert to byte array. |
| |
| float floatValue = new Float(valueString).floatValue(); |
| floatValue = floatLimitCheck(floatValue); |
| return rawFloatBitsToByteArray(Float.floatToRawIntBits(floatValue)); |
| } |
| |
| // Double handling |
| |
| if (dt == FPDataType.DOUBLE || FPDataType.DOUBLE.getBitsize() == dataTypeBitCount) |
| { |
| // Convert the string to a double. Check the range. Convert to byte array. |
| |
| double doubleValue = new Double(valueString).doubleValue(); |
| doubleValue = doubleLimitCheck(doubleValue); |
| return rawDoubleBitsToByteArray(Double.doubleToRawLongBits(doubleValue)); |
| } |
| } |
| |
| return new byte[0]; |
| } |
| |
| // Convert from an FPMemoryByte array to a byte array |
| |
| public static byte[] memoryBytesToByteArray(FPMemoryByte[] memoryByteArray) |
| { |
| byte[] byteArray = new byte[memoryByteArray.length]; |
| |
| for (int index = 0; index < memoryByteArray.length; index++) |
| byteArray[index] = memoryByteArray[index].getValue(); |
| |
| return byteArray; |
| } |
| |
| // Convert from a byte array to a MemoryByte array |
| |
| public static FPMemoryByte[] byteArrayToMemoryBytes(Endian endian, byte[] byteArray) |
| { |
| FPMemoryByte[] memoryBytes = new FPMemoryByte[byteArray.length]; |
| |
| for (int index = 0; index < byteArray.length; index++) |
| { |
| memoryBytes[index] = new FPMemoryByte(); |
| memoryBytes[index].setBigEndian(endian == Endian.BIG); |
| memoryBytes[index].setValue(byteArray[index]); |
| } |
| |
| return memoryBytes; |
| } |
| |
| // Check the character for being valid for number entry, both standard and scientific notation |
| |
| public static boolean validEditCharacter(char character) |
| { |
| return (character >= '0' && character <= '9') || |
| character == '+' || character == '-' || |
| character == 'e' || character == 'E' || |
| character == '.' || character == ' '; |
| } |
| |
| // Validate floating point number string |
| |
| public static boolean isValidFormat(String string) |
| { |
| // Rules: |
| // - A minimum of one digit preceding the optional exponent character is required. |
| // - Allowable characters: 0-9, a decimal point, '+' and '-' number |
| // signs, exponent characters 'e' and 'E', and spaces. |
| // |
| // Strings may also have: |
| // - One [optional] decimal point |
| // - A maximum of two [optional] number signs (one before the number and one after the exponent character) |
| // - Only one [optional] exponent character is allowed |
| |
| boolean digit = false; |
| char[] charArray = string.toCharArray(); |
| |
| // Phase I check: |
| |
| String scientificNotationPattern = "^[-+]??(\\d++[.]\\d*?|[.]?\\d+?|\\d+(?=[eE]))([eE][-+]??\\d++)?$"; //$NON-NLS-1$ |
| |
| if (!Pattern.matches(scientificNotationPattern, string)) |
| return false; |
| |
| // Phase II check |
| |
| for (int index = 0; index < string.length(); index++) |
| { |
| // Check for a digit |
| |
| if (charArray[index] >= '0' && charArray[index] <= '9') |
| digit = true; |
| |
| // Make sure it's a valid/allowable character |
| |
| if (!validEditCharacter(charArray[index])) |
| return false; |
| |
| // Only one decimal point and exponent character is allowed |
| |
| if (FPutilities.countMatches(string.toLowerCase(), ".") > 1 || FPutilities.countMatches(string.toLowerCase(), "e") > 1) //$NON-NLS-1$ //$NON-NLS-2$ |
| return false; |
| |
| // Number signs are only allowed in the first position and following the exponent character. |
| |
| if (((charArray[index] == '+' || charArray[index] == '-') && index != 0) && |
| (charArray[index-1] != 'e' && charArray[index-1] != 'E')) |
| return false; |
| |
| // Decimal points are not allowed after the exponent character |
| |
| int eIndex = string.toLowerCase().indexOf('e'); |
| |
| if (charArray[index] == '.' && eIndex != -1 && eIndex < index) |
| return false; |
| } |
| |
| return digit; |
| } |
| |
| // Return a string of the specified length filled with the specified character |
| |
| public static String fillString(int length, char character) |
| { |
| if (length < 1) return ""; //$NON-NLS-1$ |
| char[] charArray = new char[length]; |
| Arrays.fill(charArray, character); |
| return new String(charArray); |
| } |
| |
| // Count the 'subString' matches in 'string' |
| |
| public static int countMatches(String string, String subString) |
| { |
| if (string.length() == 0 || subString.length() == 0) return 0; |
| |
| int count = 0; |
| int index = 0; |
| |
| while ((index = string.indexOf(subString, index)) != -1) |
| { |
| count++; |
| index += subString.length(); |
| } |
| |
| return count; |
| } |
| |
| // Print out a stack trace; useful for UI operations where stopping at a breakpoint causes button press context to be lost |
| |
| public static void stackTrace(int depth) |
| { |
| int offset = 3; // Ignore frames contributed to the stack based on call to this method |
| if (depth == 0) depth = 4; // Default depth if zero supplied |
| |
| // Get the stack frames for the current thread; start at the offset |
| |
| StackTraceElement[] seArray = Thread.currentThread().getStackTrace(); |
| |
| if (seArray.length > offset) |
| { |
| System.out.println("Displaying " + depth + " of " + seArray.length + " stack trace elements"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| for (int index = offset; index < Math.min(depth + offset, seArray.length + offset); index++) |
| System.out.println(" " + seArray[index].getClassName() + "." + seArray[index].getMethodName() + ": line " + seArray[index].getLineNumber()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| else |
| System.out.println("No stack frames to display"); //$NON-NLS-1$ |
| } |
| |
| // Pop up a message inside the UI thread |
| |
| public static void popupMessage(final String title, final String errorText, final Status status) |
| { |
| UIJob job = new UIJob("Floating Point Renderer") //$NON-NLS-1$ |
| { |
| // Notify the user of some condition via a pop-up box. |
| |
| @Override |
| public IStatus runInUIThread(IProgressMonitor monitor) |
| { |
| ErrorDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), title, errorText, status); |
| return Status.OK_STATUS; |
| } |
| }; |
| |
| job.setSystem(true); |
| job.schedule(); |
| } |
| |
| // Check float range. Returns -Infinity, the original value or +Infinity |
| |
| public static float floatLimitCheck(float floatValue) |
| { |
| if (floatValue != 0.0f && floatValue != Float.NEGATIVE_INFINITY && floatValue != Float.POSITIVE_INFINITY) |
| { |
| if (floatValue < 0) |
| { |
| if (Float.compare(floatValue, floatNegMax) < 0 || Float.compare(floatValue, floatNegMin) > 0) |
| return Float.NEGATIVE_INFINITY; |
| } |
| else |
| { |
| if (Float.compare(floatValue, floatPosMin) < 0 || Float.compare(floatValue, floatPosMax) > 0) |
| return Float.POSITIVE_INFINITY; |
| } |
| } |
| |
| return floatValue; |
| } |
| |
| // Check double range. Returns a value of RangeCheck |
| |
| public static double doubleLimitCheck(double doubleValue) |
| { |
| if (doubleValue != 0.0 && doubleValue != Double.NEGATIVE_INFINITY && doubleValue != Double.POSITIVE_INFINITY) |
| { |
| if (doubleValue < 0) |
| { |
| if (Double.compare(doubleValue, doubleNegMax) < 0 || Double.compare(doubleValue, doubleNegMin) > 0) |
| return Double.NEGATIVE_INFINITY; |
| } |
| else |
| { |
| if (Double.compare(doubleValue, doublePosMin) < 0 || Double.compare(doubleValue, doublePosMax) > 0) |
| return Double.POSITIVE_INFINITY; |
| } |
| } |
| |
| return doubleValue; |
| } |
| |
| // Convert a BigInteger to a hex String and return only the ending number of specified digits. |
| |
| public static String bi2HexStr(BigInteger bi, int lastDigits) |
| { |
| final int PAD_LENGTH = 12; |
| String base16 = bi.toString(16); |
| base16 = fillString(PAD_LENGTH - base16.length(), '0') + base16; |
| return "0x" + base16.substring(PAD_LENGTH - lastDigits).toUpperCase(); //$NON-NLS-1$ |
| } |
| |
| // Convert a BigInteger to a decimal String and return only the ending number of |
| // specified digits. For example: bi2HexStr(239248506, 5) = "48506" |
| |
| public static String bi2DecStr(BigInteger bi, int lastDigits) |
| { |
| final int PAD_LENGTH = 12; |
| String base10 = bi.toString(); |
| base10 = fillString(PAD_LENGTH - base10.length(), '0') + base10; |
| return base10.substring(PAD_LENGTH - lastDigits); |
| } |
| } |