/******************************************************************************* * Copyright (c) 2011, 2014 Ericsson, Ecole Polytechnique de Montreal 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: * Matthew Khouzam - Initial API and implementation * Simon Marchi - Initial API and implementation * Marc-Andre Laperle - Add min/maximum for validation *******************************************************************************/ package org.eclipse.tracecompass.ctf.core.event.types; import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull; import java.math.BigInteger; import java.nio.ByteOrder; import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.ctf.core.CTFException; import org.eclipse.tracecompass.ctf.core.event.io.BitBuffer; import org.eclipse.tracecompass.ctf.core.event.scope.IDefinitionScope; /** * A CTF integer declaration. * * The declaration of a integer basic data type. * * @version 1.0 * @author Matthew Khouzam * @author Simon Marchi */ @NonNullByDefault public final class IntegerDeclaration extends Declaration implements ISimpleDatatypeDeclaration { // ------------------------------------------------------------------------ // Helpers // ------------------------------------------------------------------------ private static final int SIZE_64 = 64; private static final int SIZE_32 = 32; private static final int SIZE_27 = 27; private static final int SIZE_16 = 16; private static final int SIZE_8 = 8; private static final int SIZE_5 = 5; private static final int BYTE_ALIGN = 8; private static final int BASE_10 = 10; /** * unsigned int 32 bits big endian */ public static final IntegerDeclaration UINT_32B_DECL = new IntegerDeclaration(32, false, ByteOrder.BIG_ENDIAN); /** * unsigned int 32 bits little endian */ public static final IntegerDeclaration UINT_32L_DECL = new IntegerDeclaration(32, false, ByteOrder.LITTLE_ENDIAN); /** * signed int 32 bits big endian */ public static final IntegerDeclaration INT_32B_DECL = new IntegerDeclaration(32, true, ByteOrder.BIG_ENDIAN); /** * signed int 32 bits little endian */ public static final IntegerDeclaration INT_32L_DECL = new IntegerDeclaration(32, true, ByteOrder.LITTLE_ENDIAN); /** * unsigned int 32 bits big endian */ public static final IntegerDeclaration UINT_64B_DECL = new IntegerDeclaration(64, false, ByteOrder.BIG_ENDIAN); /** * unsigned int 64 bits little endian */ public static final IntegerDeclaration UINT_64L_DECL = new IntegerDeclaration(64, false, ByteOrder.LITTLE_ENDIAN); /** * signed int 64 bits big endian */ public static final IntegerDeclaration INT_64B_DECL = new IntegerDeclaration(64, true, ByteOrder.BIG_ENDIAN); /** * signed int 64 bits little endian */ public static final IntegerDeclaration INT_64L_DECL = new IntegerDeclaration(64, true, ByteOrder.LITTLE_ENDIAN); /** * unsigned 8 bit int endianness doesn't matter since it's 8 bits (byte) */ public static final IntegerDeclaration UINT_8_DECL = new IntegerDeclaration(8, false, ByteOrder.BIG_ENDIAN); /** * signed 8 bit int endianness doesn't matter since it's 8 bits (char) */ public static final IntegerDeclaration INT_8_DECL = new IntegerDeclaration(8, true, ByteOrder.BIG_ENDIAN); /** * Unsigned 5 bit int, used for event headers */ public static final IntegerDeclaration UINT_5B_DECL = new IntegerDeclaration(5, false, 10, ByteOrder.BIG_ENDIAN, Encoding.NONE, "", 1); //$NON-NLS-1$ /** * Unsigned 5 bit int, used for event headers */ public static final IntegerDeclaration UINT_5L_DECL = new IntegerDeclaration(5, false, 10, ByteOrder.LITTLE_ENDIAN, Encoding.NONE, "", 1); //$NON-NLS-1$ /** * Unsigned 5 bit int, used for event headers */ public static final IntegerDeclaration UINT_27B_DECL = new IntegerDeclaration(27, false, 10, ByteOrder.BIG_ENDIAN, Encoding.NONE, "", 1); //$NON-NLS-1$ /** * Unsigned 5 bit int, used for event headers */ public static final IntegerDeclaration UINT_27L_DECL = new IntegerDeclaration(27, false, 10, ByteOrder.LITTLE_ENDIAN, Encoding.NONE, "", 1); //$NON-NLS-1$ /** * Unsigned 16 bit int, used for event headers */ public static final IntegerDeclaration UINT_16B_DECL = new IntegerDeclaration(16, false, ByteOrder.BIG_ENDIAN); /** * Unsigned 16 bit int, used for event headers */ public static final IntegerDeclaration UINT_16L_DECL = new IntegerDeclaration(16, false, ByteOrder.LITTLE_ENDIAN); // ------------------------------------------------------------------------ // Attributes // ------------------------------------------------------------------------ private final int fLength; private final boolean fSigned; private final int fBase; private final boolean fIsByteOrderSet; private final ByteOrder fByteOrder; private final Encoding fEncoding; private final long fAlignment; private final String fClock; // ------------------------------------------------------------------------ // Constructors // ------------------------------------------------------------------------ /** * Factory, some common types cached * * @param len * The length in bits * @param signed * Is the integer signed? false == unsigned * @param base * The base (10-16 are most common) * @param byteOrder * Big-endian little-endian or other * @param encoding * ascii, utf8 or none. * @param clock * The clock path, can be null * @param alignment * The minimum alignment. Should be >= 1 * @return the integer declaration */ public static IntegerDeclaration createDeclaration(int len, boolean signed, int base, @Nullable ByteOrder byteOrder, Encoding encoding, String clock, long alignment) { if (encoding.equals(Encoding.NONE) && (clock.equals("")) && base == BASE_10 && byteOrder != null) { //$NON-NLS-1$ if (alignment == BYTE_ALIGN) { switch (len) { case SIZE_8: return signed ? INT_8_DECL : UINT_8_DECL; case SIZE_16: if (!signed) { if (isBigEndian(byteOrder)) { return UINT_16B_DECL; } return UINT_16L_DECL; } break; case SIZE_32: if (signed) { if (isBigEndian(byteOrder)) { return INT_32B_DECL; } return INT_32L_DECL; } if (isBigEndian(byteOrder)) { return UINT_32B_DECL; } return UINT_32L_DECL; case SIZE_64: if (signed) { if (isBigEndian(byteOrder)) { return INT_64B_DECL; } return INT_64L_DECL; } if (isBigEndian(byteOrder)) { return UINT_64B_DECL; } return UINT_64L_DECL; default: } } else if (alignment == 1) { switch (len) { case SIZE_5: if (!signed) { if (isBigEndian(byteOrder)) { return UINT_5B_DECL; } return UINT_5L_DECL; } break; case SIZE_27: if (!signed) { if (isBigEndian(byteOrder)) { return UINT_27B_DECL; } return UINT_27L_DECL; } break; default: break; } } } return new IntegerDeclaration(len, signed, base, byteOrder, encoding, clock, alignment); } private static boolean isBigEndian(@Nullable ByteOrder byteOrder) { return (byteOrder != null) && byteOrder.equals(ByteOrder.BIG_ENDIAN); } /** * Constructor * * @param len * The length in bits * @param signed * Is the integer signed? false == unsigned * @param base * The base (10-16 are most common) * @param byteOrder * Big-endian little-endian or other * @param encoding * ascii, utf8 or none. * @param clock * The clock path, can be null * @param alignment * The minimum alignment. Should be ≥ 1 */ private IntegerDeclaration(int len, boolean signed, int base, @Nullable ByteOrder byteOrder, Encoding encoding, String clock, long alignment) { fLength = len; fSigned = signed; fBase = base; fIsByteOrderSet = byteOrder != null; fByteOrder = (byteOrder == null) ? ByteOrder.nativeOrder() : byteOrder; fEncoding = encoding; fClock = clock; fAlignment = Math.max(alignment, 1); } private IntegerDeclaration(int len, boolean signed, @Nullable ByteOrder byteOrder) { this(len, signed, BASE_10, byteOrder, Encoding.NONE, "", BYTE_ALIGN); //$NON-NLS-1$ } // ------------------------------------------------------------------------ // Getters/Setters/Predicates // ------------------------------------------------------------------------ /** * Is the integer signed? * * @return the is the integer signed */ public boolean isSigned() { return fSigned; } /** * Get the integer base commonly decimal or hex * * @return the integer base */ public int getBase() { return fBase; } /** * @since 2.0 */ @Override public boolean isByteOrderSet() { return fIsByteOrderSet; } @Override public ByteOrder getByteOrder() { return fByteOrder; } /** * Get encoding, chars are 8 bit ints * * @return the encoding */ public Encoding getEncoding() { return fEncoding; } /** * Is the integer a character (8 bits and encoded?) * * @return is the integer a char */ public boolean isCharacter() { return (fLength == SIZE_8) && (fEncoding != Encoding.NONE); } /** * Is the integer an unsigned byte (8 bits and no sign)? * * @return is the integer an unsigned byte */ public boolean isUnsignedByte() { return (fLength == SIZE_8) && (!fSigned); } /** * Get the length in bits for this integer * * @return the length of the integer */ public int getLength() { return fLength; } @Override public long getAlignment() { return fAlignment; } /** * The integer's clock, since timestamps are stored in ints * * @return the integer's clock, can be null. (most often it is) */ public String getClock() { return fClock; } @Override public int getMaximumSize() { return fLength; } // ------------------------------------------------------------------------ // Operations // ------------------------------------------------------------------------ @Override public IntegerDefinition createDefinition(@Nullable IDefinitionScope definitionScope, String fieldName, BitBuffer input) throws CTFException { ByteOrder byteOrder = input.getByteOrder(); input.setByteOrder(fByteOrder); long value = read(input); input.setByteOrder(byteOrder); return new IntegerDefinition(this, definitionScope, fieldName, value); } @Override public String toString() { return "[declaration] integer[length:" + fLength + (fSigned ? " " : " un") + "signed" + " base:" + fBase + " byteOrder:" + fByteOrder + " encoding:" + fEncoding + " alignment:" + fAlignment + " clock:" + fClock + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ } /** * Get the maximum value for this integer declaration. * * @return The maximum value for this integer declaration */ public BigInteger getMaxValue() { /* * Compute the number of bits able to represent an unsigned number, * ignoring sign bit. */ int significantBits = fLength - (fSigned ? 1 : 0); /* * For a given N significant bits, compute the maximal value which is (1 * << N) - 1. */ return checkNotNull(BigInteger.ONE.shiftLeft(significantBits).subtract(BigInteger.ONE)); } /** * Get the minimum value for this integer declaration. * * @return The minimum value for this integer declaration */ public BigInteger getMinValue() { if (!fSigned) { return checkNotNull(BigInteger.ZERO); } /* * Compute the number of bits able to represent an unsigned number, * without the sign bit. */ int significantBits = fLength - 1; /* * For a given N significant bits, compute the minimal value which is - * (1 << N). */ return checkNotNull(BigInteger.ONE.shiftLeft(significantBits).negate()); } private long read(BitBuffer input) throws CTFException { /* Offset the buffer position wrt the current alignment */ alignRead(input); boolean signed = isSigned(); int length = getLength(); long bits = 0; /* * Is the endianness of this field the same as the endianness of the * input buffer? If not, then temporarily set the buffer's endianness to * this field's just to read the data */ ByteOrder previousByteOrder = input.getByteOrder(); if ((getByteOrder() != input.getByteOrder())) { input.setByteOrder(getByteOrder()); } if (length > SIZE_64) { throw new CTFException("Cannot read an integer with over 64 bits. Length given: " + length); //$NON-NLS-1$ } bits = input.get(length, signed); /* * Put the input buffer's endianness back to original if it was changed */ if (previousByteOrder != input.getByteOrder()) { input.setByteOrder(previousByteOrder); } return bits; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (int) (fAlignment ^ (fAlignment >>> 32)); result = prime * result + fBase; result = prime * result + fByteOrder.toString().hashCode(); result = prime * result + fClock.hashCode(); result = prime * result + fEncoding.hashCode(); result = prime * result + fLength; result = prime * result + (fSigned ? 1231 : 1237); return result; } @Override public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } IntegerDeclaration other = (IntegerDeclaration) obj; if (!isBinaryEquivalent(other)) { return false; } if (!fByteOrder.equals(other.fByteOrder)) { return false; } if (!fClock.equals(other.fClock)) { return false; } if (fEncoding != other.fEncoding) { return false; } if (fBase != other.fBase) { return false; } return true; } @Override public boolean isBinaryEquivalent(@Nullable IDeclaration obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } IntegerDeclaration other = (IntegerDeclaration) obj; return isBinaryEquivalent(other); } private boolean isBinaryEquivalent(IntegerDeclaration other) { if (fAlignment != other.fAlignment) { return false; } if (fLength != other.fLength) { return false; } if (fSigned != other.fSigned) { return false; } // no need for base // no need for encoding // no need for clock // byte inversion is ok on byte order if the element is one byte long if ((fLength != BYTE_ALIGN) && !fByteOrder.equals(other.fByteOrder)) { return false; } return true; } }