/*--------------------------------------------------------------------------- Decimal encoding sample code (C) Copyright International Business Machines Corporation 2003 All Rights Reserved. This sample code is intended to illustrate encoding of decimal floating-point numbers in binary bit strings, and corresponding decoding. Please see the document "General Decimal Arithmetic" at http://speleotrove.com/decimal for a full description of the encoding formats used. This sample code is experimental, and may contain errors. It is offered on an as-is basis. In particular, this sample code is designed to be illustrative rather than to be an implementation optimised for any particular purpose. Indeed, a number of obvious optimisations are omitted in the interests of maximising clarity. Please send comments, suggestions, and corrections to the author: Dave Clark IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK dave_clark@uk.ibm.com --------------------------------------------------------------------------*/ package com.ibm.eou.decimal; /** * This class represents a field of binary bits, and * provides some fundamental operations on them. Instances * are delivered as the result of decimal encoding methods * on {@link DecimalEncoder DecimalEncoder}, and may be * supplied as parameters to decimal decoding methods * on {@link DecimalDecoder DecimalDecoder}. * @author Dave Clark, IBM Ease of Use */ public class BitString { final int fNumberOfBits; final byte[] fBits; /** * Create a new field of bits using the bit values stored * in a string as 1s or 0s. A character '1' in the string * is taken to be a 1 bit (touched), a character '0' in the * string is taken to be a 0 bit (touched), and any other * character is taken to be a 0 bit (untouched). The length * of the string is taken as the number of bits in the field. */ public static BitString createFromBinary(final String bitpattern) { final BitString result = new BitString(bitpattern.length()); for (int i = 0; i < bitpattern.length(); i++) { final char value = bitpattern.charAt(i); if ((value == '1') || (value == '0')) result.storeBit(i, (value == '1')); } return result; } /** * Create a new field of bits using the nybble values stored * in a string as hexadecimal digits. A character '0' to 'f' * in the string is taken to be four bits (touched), and any * other character is taken to be four 0 bits (untouched). Four * times the length of the string is taken as the number of * bits in the field. */ public static BitString createFromHexadecimal(final String hexpattern) { final BitString result = new BitString(4 * hexpattern.length()); for (int i = 0; i < hexpattern.length(); i++) { final int value = Character.digit(hexpattern.charAt(i), 16); if (value >= 0) { result.storeBit((4*i), ((value >> 3) & 1) != 0); result.storeBit((4*i)+1, ((value >> 2) & 1) != 0); result.storeBit((4*i)+2, ((value >> 1) & 1) != 0); result.storeBit((4*i)+3, (value & 1) != 0); } } return result; } /** * Create a new field of bits, with all bits initially * reset and untouched. */ public BitString(final int numberOfBits) { fNumberOfBits = numberOfBits; fBits = new byte[numberOfBits / 8]; } /** * Return the number of bits in the field. */ public int getNumberOfBits() { return fNumberOfBits; } // check that a bit number is in range private void checkBit(final int bit) { if ((bit < 0) || (bit >= fNumberOfBits)) throw new IllegalArgumentException("Invalid bit number: " + bit); //$NON-NLS-1$ } /** * Reset the specified bit. */ public void resetBit(final int bit) { storeBit(bit, false); } /** * Set the specified bit. */ public void setBit(final int bit) { storeBit(bit, true); } /** * Store a bit. */ public void storeBit(final int bit, final boolean value) { checkBit(bit); final int which_byte = bit / 8; final int which_bit = bit % 8; final int mask = (1 << which_bit); // set or reset the bit if (value) fBits[which_byte] = (byte)(fBits[which_byte] | mask); else fBits[which_byte] = (byte)(fBits[which_byte] & ~mask); } /** * Store a range of bits (up to 32 of them). The lowest bits * of the supplied values are used, with the highest of those * bits being stored at startbit, and each lower bit in turn * being stored at successive bits in the field. */ public void storeBits(final int startbit, final int bits, final int values) { for (int bit = 0; bit < bits; bit++) storeBit(startbit + bits - 1 - bit, ((values & (1 << bit)) != 0)); } /** * Fill a range of bits with the specified value. */ public void fillBits(final int startbit, final int bits, final boolean value) { for (int bit = 0; bit < bits; bit++) storeBit(startbit + bit, value); } /** * Fill a range of bits, from the start bit to the end * of the bit field, with the specified value. */ public void fillBits(final int startbit, final boolean value) { for (int bit = 0; bit < fNumberOfBits - startbit; bit++) storeBit(startbit + bit, value); } /** * Extract a bit. */ public boolean getBit(final int bit) { checkBit(bit); final int which_byte = bit / 8; final int which_bit = bit % 8; final int mask = (1 << which_bit); return ((fBits[which_byte] & mask) != 0); } /** * Extract a range of bits (up to 32 of them). The bits are * returned in the lowest bits of the returned value, with the * highest of those being the bit at startbit, and each lower * bit being successive bits from the field. */ public int getBits(final int startbit, final int bits) { int result = 0; for (int bit = 0; bit < bits; bit++) if (getBit(startbit + bits - 1 - bit)) result |= (1 << bit); return result; } /** * Render the bit field as a string of 1s and 0s. The length of the * string is the length of the bit field, and the first character * represents bit 0, with successive characters representing * successive bits from the field. */ public String toBitString() { final StringBuffer result = new StringBuffer(fNumberOfBits); // build the result one bit at a time for (int i = 0; i < fNumberOfBits; i++) result.append(getBit(i) ? '1' : '0'); return result.toString(); } /** * Render the bit field as a string of hexadecimal digits, each * representing four bits (one nybble) from the field. The first * character represents bits 0 through 3, and successive characters * representing successive nybbles from the field. Within each * nybble the first bit is high, so that the fourth bit in the * nybble is the lowest bit. The length of the returned string * is a quarter of the length of the bit field, rounded up to * the next whole number, and the last digit will thus not * represent a whole four bits if the length of the bit field * is not a multiple of four. */ public String toHexString() { final StringBuffer result = new StringBuffer(fNumberOfBits); // build the whole nybbles of the result, one nybble at a time for (int i = 0; i < fNumberOfBits - 3; i += 4) result.append(Character.forDigit(getBits(i, 4), 16)); //$NON-NLS-1$ // add the last incomplete nybble if necessary final int residue = fNumberOfBits % 4; if (residue > 0) result.append(Character.forDigit(getBits(fNumberOfBits - residue, residue) << (4 - residue), 16)); return result.toString(); } /** * Return true if the supplied argument is a bit string * equal to this bit string, ie having the same * number of bits and all matching bit settings. */ public boolean equals(final Object obj) { if (obj instanceof BitString) { final BitString other = (BitString)obj; if (this.fNumberOfBits != other.fNumberOfBits) return false; for (int i = 0; i < this.fBits.length; i++) if (this.fBits[i] != other.fBits[i]) return false; return true; } else return super.equals(obj); } }