All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.codec.BaseMetricsImpl Maven / Gradle / Ivy

Go to download

Artifact with encoding and decoding (not in terms of encryption/decryption) implementations (codecs) such as BASE64 encoding / decoding.

There is a newer version: 3.3.8
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.codec;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import org.refcodes.data.CharSet;
import org.refcodes.data.PaddingChar;
import org.refcodes.exception.BugException;
import org.refcodes.textual.VerboseTextBuilder;

/**
 * {@link BaseMetrics} implementation for playing around with your own
 * configuration. You may use a template as one provided by
 * {@link BaseMetricsConfig} and tweak some attributes as you wish.
 */
public class BaseMetricsImpl implements BaseMetrics {

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	private static final int BYTES_PER_INT = 4;

	private static final int BITS_PER_BYTE = 8;

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private final Map _charToValueMap = new HashMap<>();
	private int _numberBase;
	private int _bytesPerInt;
	private char[] _charSet;
	private int _bitsPerDigit;
	private int _digitsPerInt;
	private int _digitsPerByte;
	private int _digitMask;
	private char _paddingChar;
	private int _bytesPerBlock;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBase the base
	 */
	public BaseMetricsImpl( int aBase ) {
		this( aBase, CharSet.BASE64.getCharSet() );
	}

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBase the base
	 * @param aCharSet the char set
	 */
	public BaseMetricsImpl( int aBase, char[] aCharSet ) {
		this( aBase, aCharSet, PaddingChar.BASE64.getChar() );
	}

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBase the base
	 * @param aCharSet the char set
	 * @param aPaddingChar the padding char
	 */
	public BaseMetricsImpl( int aBase, char[] aCharSet, char aPaddingChar ) {
		_numberBase = aBase;
		_charSet = aCharSet;
		_bitsPerDigit = toBitsPerDigit( aBase );
		_digitMask = toDigitMask( _bitsPerDigit );
		_bytesPerInt = toBytesPerInt( _bitsPerDigit );
		_digitsPerInt = toDigitsPerInt( _bytesPerInt, _bitsPerDigit );
		_digitsPerByte = toDigitsPerByte( aBase );
		_paddingChar = aPaddingChar;
		_bytesPerBlock = toBytesPerBlock( _bitsPerDigit );
		intiCharToValueMap( aCharSet );
	}

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBase the base
	 * @param aBitsPerDigit the bits per digit
	 * @param aDigitsPerByte the digits per byte
	 * @param aDigitsPerInt the digits per int
	 * @param aBytesPerInt the bytes per int
	 * @param aCharSet the char set
	 */
	public BaseMetricsImpl( int aBase, int aBitsPerDigit, int aDigitsPerByte, int aDigitsPerInt, int aBytesPerInt, char[] aCharSet ) {
		this( aBase, aBitsPerDigit, aDigitsPerByte, aDigitsPerInt, aBytesPerInt, aCharSet, PaddingChar.BASE64.getChar() );
	}

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBase the base
	 * @param aBitsPerDigit the bits per digit
	 * @param aDigitsPerByte the digits per byte
	 * @param aDigitsPerInt the digits per int
	 * @param aBytesPerInt the bytes per int
	 * @param aCharSet the char set
	 * @param aPaddingChar the padding char
	 */
	public BaseMetricsImpl( int aBase, int aBitsPerDigit, int aDigitsPerByte, int aDigitsPerInt, int aBytesPerInt, char[] aCharSet, char aPaddingChar ) {
		_numberBase = aBase;
		_charSet = aCharSet;
		_digitMask = (int) Math.pow( 2, aBitsPerDigit ) - 1;
		_bytesPerInt = aBytesPerInt;
		_bitsPerDigit = aBitsPerDigit;
		_digitsPerInt = aDigitsPerInt;
		_digitsPerByte = aDigitsPerByte;
		intiCharToValueMap( aCharSet );
		_paddingChar = aPaddingChar;
		_bytesPerBlock = toBytesPerBlock( aBitsPerDigit );
	}

	/**
	 * Instantiates a new base metrics impl.
	 *
	 * @param aBaseMetrics the base metrics
	 */
	public BaseMetricsImpl( BaseMetrics aBaseMetrics ) {
		_bitsPerDigit = aBaseMetrics.getBitsPerDigit();
		_bytesPerInt = aBaseMetrics.getBytesPerInt();
		intiCharToValueMap( aBaseMetrics.getCharSet() );
		_digitMask = aBaseMetrics.getDigitMask();
		_digitsPerByte = aBaseMetrics.getDigitsPerByte();
		_digitsPerInt = aBaseMetrics.getDigitsPerInt();
		_numberBase = aBaseMetrics.getNumberBase();
		_paddingChar = aBaseMetrics.getPaddingChar();
		_bytesPerBlock = toBytesPerBlock( aBaseMetrics.getBytesPerBlock() );
	}

	/**
	 * Inti char to value map.
	 *
	 * @param aCharSet the char set
	 */
	private void intiCharToValueMap( char[] aCharSet ) {
		for ( int i = 0; i < aCharSet.length; i++ ) {
			_charToValueMap.put( aCharSet[i], i );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getNumberBase() {
		return _numberBase;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public char[] getCharSet() {
		return _charSet;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getBytesPerInt() {
		return _bytesPerInt;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getBytesPerBlock() {
		return _bytesPerBlock;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getDigitsPerInt() {
		return _digitsPerInt;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getBitsPerDigit() {
		return _bitsPerDigit;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getDigitsPerByte() {
		return _digitsPerByte;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int getDigitMask() {
		return _digitMask;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int toValue( char aChar ) {
		if ( aChar == _paddingChar ) {
			return 0;
		}
		final Integer theValue = _charToValueMap.get( aChar );
		if ( theValue == null ) {
			throw new IllegalArgumentException( "The character '" + aChar + "' does not belong to the given <" + getNumberBase() + "> number base metrics with charset: " + VerboseTextBuilder.asString( getCharSet() ) );
		}
		return theValue;

	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public char toChar( int aValue ) {
		return _charSet[aValue];
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public char getPaddingChar() {
		return _paddingChar;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + _bitsPerDigit;
		result = prime * result + _bytesPerBlock;
		result = prime * result + _bytesPerInt;
		result = prime * result + Arrays.hashCode( _charSet );
		result = prime * result + ( ( _charToValueMap == null ) ? 0 : _charToValueMap.hashCode() );
		result = prime * result + _digitMask;
		result = prime * result + _digitsPerByte;
		result = prime * result + _digitsPerInt;
		result = prime * result + _numberBase;
		result = prime * result + _paddingChar;
		return result;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean equals( Object obj ) {
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( getClass() != obj.getClass() ) {
			return false;
		}
		final BaseMetricsImpl other = (BaseMetricsImpl) obj;
		if ( _bitsPerDigit != other._bitsPerDigit ) {
			return false;
		}
		if ( _bytesPerBlock != other._bytesPerBlock ) {
			return false;
		}
		if ( _bytesPerInt != other._bytesPerInt ) {
			return false;
		}
		if ( !Arrays.equals( _charSet, other._charSet ) ) {
			return false;
		}
		if ( _charToValueMap == null ) {
			if ( other._charToValueMap != null ) {
				return false;
			}
		}
		else if ( !_charToValueMap.equals( other._charToValueMap ) ) {
			return false;
		}
		if ( _digitMask != other._digitMask ) {
			return false;
		}
		if ( _digitsPerByte != other._digitsPerByte ) {
			return false;
		}
		if ( _digitsPerInt != other._digitsPerInt ) {
			return false;
		}
		if ( _numberBase != other._numberBase ) {
			return false;
		}
		if ( _paddingChar != other._paddingChar ) {
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getSimpleName() + " [numberBase=" + _numberBase + ", bytes/int=" + _bytesPerInt + ", bits/digit=" + _bitsPerDigit + ", digits/int=" + _digitsPerInt + ", digits/byte=" + _digitsPerByte + ", digitMask=" + _digitMask + ", paddingChar='" + _paddingChar + "', bytes/block=" + _bytesPerBlock + "]";
	}

	// /////////////////////////////////////////////////////////////////////////
	// UTILITY:
	// /////////////////////////////////////////////////////////////////////////

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * To digit mask.
	 *
	 * @param aBitsPerDigit the bits per digit
	 * 
	 * @return the int
	 */
	protected static int toDigitMask( int aBitsPerDigit ) {
		return (int) Math.pow( 2, aBitsPerDigit ) - 1;
	}

	/**
	 * To bits per digit.
	 *
	 * @param aBase the base
	 * 
	 * @return the int
	 */
	protected static int toBitsPerDigit( int aBase ) {
		int theBitsPerDigit = Integer.toBinaryString( aBase - 1 ).length();
		// Make it fit into byte portions of an integer:
		while ( BITS_PER_BYTE % theBitsPerDigit != 0 && 2 * BITS_PER_BYTE % theBitsPerDigit != 0 && 3 * BITS_PER_BYTE % theBitsPerDigit != 0 && 4 * BITS_PER_BYTE % theBitsPerDigit != 0 ) { // && 5 * BITS_PER_BYTE % theBitsPerDigit != 0 
			theBitsPerDigit++;
		}
		return theBitsPerDigit;
	}

	/**
	 * To digits per int.
	 *
	 * @param aBytesPerInt the bytes per int
	 * @param aBitsPerDigit the bits per digit
	 * 
	 * @return the int
	 */
	protected static int toDigitsPerInt( int aBytesPerInt, int aBitsPerDigit ) {
		return BITS_PER_BYTE * aBytesPerInt / aBitsPerDigit;
	}

	/**
	 * To digits per byte.
	 *
	 * @param aBase the base
	 * 
	 * @return the int
	 */
	protected static int toDigitsPerByte( int aBase ) {
		int theDigitsPerByte = 0;
		int eMax = 1;
		while ( eMax < 255 ) {
			theDigitsPerByte++;
			eMax *= aBase;
		}
		return theDigitsPerByte;
	}

	/**
	 * To bytes per int.
	 *
	 * @param aBitsPerDigit the bits per digit
	 * 
	 * @return the int
	 */
	protected static int toBytesPerInt( int aBitsPerDigit ) {
		for ( int i = ( BYTES_PER_INT * BITS_PER_BYTE ); i > 0; i-- ) {
			if ( i % aBitsPerDigit == 0 ) {
				return i / BITS_PER_BYTE;
			}
		}
		for ( int i = 1; i <= aBitsPerDigit; i++ ) {
			if ( i * BITS_PER_BYTE % aBitsPerDigit == 0 ) {
				return ( i * BITS_PER_BYTE ) / BITS_PER_BYTE;
			}
		}
		throw new BugException( "We must have encountered a bug as we cannot determine the word length for a base codec with <" + aBitsPerDigit + "> bits per digit." );
	}

	/**
	 * Calculates the number of bytes which fully fit into a base encoded block.
	 * 
	 * @param aBitsPerDigit The bits used per digit.
	 * 
	 * @return The number of bytes completely fitting into one block.
	 */
	protected static int toBytesPerBlock( int aBitsPerDigit ) {
		// if ( aBitsPerDigit < BITS_PER_BYTE ) return aBitsPerDigit % BITS_PER_BYTE;
		int theBytesPerBlock = 1;
		while ( ( theBytesPerBlock * BITS_PER_BYTE ) % aBitsPerDigit != 0 ) {
			theBytesPerBlock++;
		}
		return theBytesPerBlock;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy