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

org.jgroups.util.Bits Maven / Gradle / Ivy

There is a newer version: 5.3.13.Final
Show newest version
package org.jgroups.util;

import org.jgroups.Global;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Class (similar to (and partly copied from) java.nio.Bits) containing helper methods to encode variables
 * (e.g. ints, long, List<Address> etc) to memory (byte buffer) or output streams and read variables
 * from memory or input streams.

* The write methods write a type (e.g. an int or a char) to a buffer ({@link ByteBuffer} or output stream, using * variable-length encoding. If * there are not enough byte in the buffer to write a type, a {@link java.nio.BufferOverflowException} is thrown. * If the variable cannot be written to the output stream, an IOException is thrown. *

* The read methods read a variable-length encoded type from a buffer or input stream. If there are fewer bytes in * the buffer than needed to read the type, a {@link java.nio.BufferUnderflowException} is thrown. If the read fails, * an IOException is thrown. *

* The size() methods return the number of bytes used to encode the given type with variable-length encoding. *

* There are additional helper methods to write/read custom JGroups types, e.g. address lists, Views etc *

* Note that methods to read/write atomic types (char, int etc) should only be used if variable-length encoding is * desired; otherwise {@link DataOutput#writeInt(int)} or {@link ByteBuffer#putInt(int)} should be used instead. *

* At the time of writing this (Feb 2014), most methods have not yet been implemented. * @author Bela Ban * @author Sanne Grinovero * @since 3.5 */ public final class Bits { private Bits() { throw new InstantiationError( "Must not instantiate this class" ); } // -------------------- char ------------------------ // public static void writeChar(char c, byte[] buf, int offset) { buf[offset+1]=(byte)c; buf[offset]=(byte)(c >>> 8); } public static char readChar(byte[] buf, int offset) { return makeChar(buf[offset+1], buf[offset]); } public static char readChar(ByteBuffer buf) { byte a=buf.get(), b=buf.get(); return makeChar(b, a); } static private char makeChar(byte b1, byte b0) { return (char)((b1 & 0xFF) + (b0 << 8)); } // -------------------- short ----------------------- // public static void writeShort(short s, byte[] buf, int offset) { buf[offset+1]=(byte)s; buf[offset]=(byte)(s >>> 8); } public static short readShort(byte[] buf, int offset) { return makeShort(buf[offset], buf[offset+1]); } public static short readShort(ByteBuffer buf) { return makeShort(buf.get(), buf.get()); } public static short makeShort(byte a, byte b) { return (short)((a << 8) | (b & 0xff)); } // --------------------- int ------------------------ // public static void writeInt(int num, byte[] buf, int offset) { buf[offset+3]=(byte)num; buf[offset+2]=(byte)(num >>> 8); buf[offset+1]=(byte)(num >>> 16); buf[offset]=(byte)(num >>> 24); } public static int readInt(byte[] buf, int offset) { return ((buf[offset+3] & 0xFF)) + ((buf[offset+2] & 0xFF) << 8) + ((buf[offset+1] & 0xFF) << 16) + ((buf[offset]) << 24); } public static int readInt(ByteBuffer buf) { byte a=buf.get(), b=buf.get(), c=buf.get(), d=buf.get(); return (d & 0xFF) + ((c & 0xFF) << 8) + ((b & 0xFF) << 16) + (a << 24); } public static void writeIntCompressed(int num, byte[] buf, int offset) { if(num == 0) { buf[offset]=0; return; } final byte bytes_needed=bytesRequiredFor(num); buf[offset++]=bytes_needed; for(int i=0; i < bytes_needed; i++) buf[offset++]=getByteAt(num, i); } public static int readIntCompressed(byte[] buf, int offset) { byte len=buf[offset++]; if(len == 0) return 0; return makeInt(buf, offset, len); } public static void writeIntCompressed(int num, ByteBuffer buf) { if(num == 0) { buf.put((byte)0); return; } final byte bytes_needed=bytesRequiredFor(num); buf.put(bytes_needed); for(int i=0; i < bytes_needed; i++) buf.put(getByteAt(num, i)); } public static int readIntCompressed(ByteBuffer buf) { byte len=buf.get(); if(len == 0) return 0; return makeInt(buf, len); } public static void writeIntCompressed(int num, DataOutput out) throws IOException { if(num == 0) { out.write(0); return; } final byte bytes_needed=bytesRequiredFor(num); out.write(bytes_needed); for(int i=0; i < bytes_needed; i++) out.write(getByteAt(num, i)); } public static int readIntCompressed(DataInput in) throws IOException { byte len=in.readByte(); if(len == 0) return 0; return makeInt(in, len); } public static int makeInt(ByteBuffer buffer, int bytes_to_read) { int retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=buffer.get(); retval |= ((int)b & 0xff) << (i * 8); } return retval; } public static int makeInt(DataInput in, int bytes_to_read) throws IOException { int retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=in.readByte(); retval |= ((int)b & 0xff) << (i * 8); } return retval; } public static int makeInt(byte[] buf, int offset, int bytes_to_read) { int retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=buf[offset + i]; retval |= ((int)b & 0xff) << (i * 8); } return retval; } /** * Computes the size of a variable-length encoded int * @param num the int * @return the number of bytes needed to variable-length encode num */ public static int size(int num) { return (byte)(num == 0? 1 : bytesRequiredFor(num) +1); } // -------------------- long ------------------------ // public static void writeLong(long num, byte[] buf, int offset) { buf[offset+7]=(byte)num; buf[offset+6]=(byte)(num >>> 8); buf[offset+5]=(byte)(num >>> 16); buf[offset+4]=(byte)(num >>> 24); buf[offset+3]=(byte)(num >>> 32); buf[offset+2]=(byte)(num >>> 40); buf[offset+1]=(byte)(num >>> 48); buf[offset]=(byte)(num >>> 56); } public static long readLong(byte[] buf, int offset) { return ((buf[offset+7] & 0xFFL)) + ((buf[offset+6] & 0xFFL) << 8) + ((buf[offset+5] & 0xFFL) << 16) + ((buf[offset+4] & 0xFFL) << 24) + ((buf[offset+3] & 0xFFL) << 32) + ((buf[offset+2] & 0xFFL) << 40) + ((buf[offset+1] & 0xFFL) << 48) + (((long) buf[offset]) << 56); } public static long readLong(ByteBuffer buf) { byte a=buf.get(), b=buf.get(), c=buf.get(), d=buf.get(), e=buf.get(), f=buf.get(), g=buf.get(), h=buf.get(); return ((h & 0xFFL)) + ((g & 0xFFL) << 8) + ((f & 0xFFL) << 16) + ((e & 0xFFL) << 24) + ((d & 0xFFL) << 32) + ((c & 0xFFL) << 40) + ((b & 0xFFL) << 48) + (((long)a) << 56); } public static void writeLongCompressed(long num, byte[] buf, int offset) { if(num == 0) { buf[offset]=0; return; } final byte bytes_needed=bytesRequiredFor(num); buf[offset++]=bytes_needed; for(int i=0; i < bytes_needed; i++) buf[offset++]=getByteAt(num, i); } public static long readLongCompressed(byte[] buf, int offset) { byte len=buf[offset++]; if(len == 0) return 0; return makeLong(buf, offset, len); } public static void writeLongCompressed(long num, ByteBuffer buf) { if(num == 0) { buf.put((byte)0); return; } final byte bytes_needed=bytesRequiredFor(num); buf.put(bytes_needed); for(int i=0; i < bytes_needed; i++) buf.put(getByteAt(num, i)); } public static long readLongCompressed(ByteBuffer buf) { byte len=buf.get(); if(len == 0) return 0; return makeLong(buf, len); } /** Writes a long to out in variable-length encoding */ public static void writeLongCompressed(final long num, final DataOutput out) throws IOException { if(num == 0) { out.write(0); return; } final byte bytes_needed=bytesRequiredFor(num); out.write(bytes_needed); for(int i=0; i < bytes_needed; i++) out.write(getByteAt(num, i)); } public static long readLongCompressed(DataInput in) throws IOException { byte len=in.readByte(); if(len == 0) return 0; return makeLong(in, len); } /** * Computes the size of a variable-length encoded long. Note that this is not currently using * variable-length encoding (will be implemented later). * @param num the long * @return the number of bytes needed to variable-length encode num */ public static int size(long num) { return (byte)(num == 0? 1 : bytesRequiredFor(num) +1); } public static long makeLong(byte[] buf, int offset, int bytes_to_read) { long retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=buf[offset + i]; retval |= ((long)b & 0xff) << (i * 8); } return retval; } public static long makeLong(ByteBuffer buffer, int bytes_to_read) { long retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=buffer.get(); retval |= ((long)b & 0xff) << (i * 8); } return retval; } public static long makeLong(DataInput in, int bytes_to_read) throws IOException { long retval=0; for(int i=0; i < bytes_to_read; i++) { byte b=in.readByte(); retval |= ((long)b & 0xff) << (i * 8); } return retval; } // ------------------ long seq ---------------------- // /** * Writes 2 sequence numbers (seqnos) in compressed format to buf. * The seqnos are non-negative and hr is guaranteed to be >= hd. *

* Once variable-length encoding has been implemented, this method will probably get dropped as we can simply * write the 2 longs individually. * @param hd the highest delivered seqno. Guaranteed to be a positive number * @param hr the highest received seqno. Guaranteed to be a positive number. Greater than or equal to hd * @param buf the buffer to write to */ public static void writeLongSequence(long hd, long hr, ByteBuffer buf) { if(hr < hd) throw new IllegalArgumentException("hr (" + hr + ") has to be >= hd (" + hd + ")"); if(hd == 0 && hr == 0) { buf.put((byte)0); return; } long delta=hr - hd; // encode highest_delivered followed by delta byte bytes_for_hd=bytesRequiredFor(hd), bytes_for_delta=bytesRequiredFor(delta); byte bytes_needed=encodeLength(bytes_for_hd, bytes_for_delta); buf.put(bytes_needed); for(int i=0; i < bytes_for_hd; i++) buf.put(getByteAt(hd, i)); for(int i=0; i < bytes_for_delta; i++) buf.put(getByteAt(delta, i)); } /** * Writes 2 sequence numbers (seqnos) in compressed format to an output stream. * The seqnos are non-negative and hr is guaranteed to be >= hd. *

* Once variable-length encoding has been implemented, this method will probably get dropped as we can simply * write the 2 longs individually. * @param hd the highest delivered seqno. Guaranteed to be a positive number * @param hr the highest received seqno. Guaranteed to be a positive number. Greater than or equal to hd * @param out the output stream to write to */ public static void writeLongSequence(long hd, long hr, DataOutput out) throws IOException { if(hr < hd) throw new IllegalArgumentException("hr (" + hr + ") has to be >= hd (" + hd + ")"); if(hd == 0 && hr == 0) { out.write(0); return; } long delta=hr - hd; // encode highest_delivered followed by delta byte bytes_for_hd=bytesRequiredFor(hd), bytes_for_delta=bytesRequiredFor(delta); byte bytes_needed=encodeLength(bytes_for_hd, bytes_for_delta); out.write(bytes_needed); for(int i=0; i < bytes_for_hd; i++) out.write(getByteAt(hd, i)); for(int i=0; i < bytes_for_delta; i++) out.write(getByteAt(delta, i)); } /** * Reads 2 compressed longs from buf into seqnos *

* Once variable-length encoding has been implemented, this method will probably get dropped as we can simply * read the 2 longs individually. * @param buf the buffer to read from * @param seqnos the array to read the seqnos into, needs to have a length of 2 */ public static void readLongSequence(ByteBuffer buf, long[] seqnos) { byte len=buf.get(); if(len == 0) { seqnos[0]=seqnos[1]=0; return; } byte len1=firstNibble(len), len2=secondNibble(len); seqnos[0]=makeLong(buf, len1); seqnos[1]=makeLong(buf, len2) + seqnos[0]; } /** * Reads 2 compressed longs into an array of 2 longs. *

* Once variable-length encoding has been implemented, this method will probably get dropped as we can simply * read the 2 longs individually. * @param in the input stream to read from * @param seqnos the array to read the seqnos into, needs to have a length of 2 * @param index the index of the first element to be written; the seqnos are written to seqnos[index] and seqnos[index+1] */ public static void readLongSequence(DataInput in, long[] seqnos, int index) throws IOException { byte len=in.readByte(); if(len == 0) { seqnos[index]=seqnos[index+1]=0; return; } byte len1=firstNibble(len), len2=secondNibble(len); seqnos[index]=makeLong(in, len1); seqnos[index+1]=makeLong(in, len2) + seqnos[index]; } public static byte size(long hd, long hr) { if(hd == 0 && hr == 0) return 1; byte num_bytes_for_hd=bytesRequiredFor(hd), num_bytes_for_delta=bytesRequiredFor(hr - hd); return (byte)(num_bytes_for_hd + num_bytes_for_delta + 1); } // -------------------- float ----------------------- // public static void writeFloat(float num, byte[] buf, int offset) { writeInt(Float.floatToIntBits(num), buf, offset); } public static float readFloat(byte[] buf, int offset) { return Float.intBitsToFloat(readInt(buf, offset)); } public static void writeFloat(float num, ByteBuffer buf) { buf.putInt(Float.floatToIntBits(num)); } public static float readFloat(ByteBuffer buf) { return Float.intBitsToFloat(buf.getInt()); } public static void writeFloat(float num, DataOutput out) throws IOException { out.writeInt(Float.floatToIntBits(num)); } public static float readFloat(DataInput in) throws IOException { return Float.intBitsToFloat(in.readInt()); } public static int size(float ignored) { return Float.BYTES; } // -------------------- double ---------------------- // public static void writeDouble(double num, byte[] buf, int offset) { writeLong(Double.doubleToLongBits(num), buf, offset); } public static double readDouble(byte[] buf, int offset) { return Double.longBitsToDouble(readLong(buf, offset)); } public static void writeDouble(double num, ByteBuffer buf) { buf.putLong(Double.doubleToLongBits(num)); } public static double readDouble(ByteBuffer buf) { return Double.longBitsToDouble(buf.getLong()); } public static void writeDouble(double num, DataOutput out) throws IOException { out.writeLong(Double.doubleToLongBits(num)); } public static double readDouble(DataInput in) throws IOException { return Double.longBitsToDouble(in.readLong()); } /** * Computes the size of a variable-length encoded double * @param num the double * @return the number of bytes needed to variable-length encode num */ public static int size(double num) { return Double.BYTES; } // -------------------- String ---------------------- // /** * Writes a string to buf. The length of the string is written first, followed by the chars (as single-byte values). * Multi-byte values are truncated: only the lower byte of each multi-byte char is written, similar to * {@link DataOutput#writeChars(String)}. * @param s the string * @param buf the buffer */ public static void writeString(String s, ByteBuffer buf) { buf.put((byte)(s != null? 1 : 0)); if(s != null) { byte[] bytes=s.getBytes(); Bits.writeIntCompressed(bytes.length, buf); buf.put(bytes); } } /** * Writes a string to buf. The length of the string is written first, followed by the chars (as single-byte values). * Multi-byte values are truncated: only the lower byte of each multi-byte char is written, similar to * {@link DataOutput#writeChars(String)}. * @param s the string * @param out the output stream */ public static void writeString(String s, DataOutput out) throws IOException { if(s != null) { out.write(1); out.writeUTF(s); } else out.write(0); } /** * Reads a string from buf. The length is read first, followed by the chars. Each char is a single byte * @param buf the buffer * @return the string read from buf */ public static String readString(ByteBuffer buf) { if(buf.get() == 0) return null; int len=readIntCompressed(buf); if(buf.isDirect()) { byte[] bytes=new byte[len]; buf.get(bytes); return new String(bytes); } else { byte[] bytes=buf.array(); return new String(bytes, buf.arrayOffset() + buf.position(), len); } } /** * Reads a string from buf. The length is read first, followed by the chars. Each char is a single byte * @param in the input stream * @return the string read from buf */ public static String readString(DataInput in) throws IOException { int b=in.readByte(); if(b == 1) return in.readUTF(); return null; } /** * Measures the number of bytes required to encode a string, taking multibyte characters into account. Measures * strings written by {@link DataOutput#writeUTF(String)}. * @param str the string * @return the number of bytes required for encoding str */ public static int sizeUTF(String str) { int len=str != null? str.length() : 0, utflen=2; if(len == 0) return utflen; for(int i = 0; i < len; i++) { int c=str.charAt(i); if((c >= 0x0001) && (c <= 0x007F)) utflen++; else if (c > 0x07FF) utflen += 3; else utflen += 2; } return utflen; } public static int size(String str) { if(str == null) return Global.BYTE_SIZE; byte[] bytes=str.getBytes(); return Global.BYTE_SIZE + size(bytes.length) + bytes.length; } // ------------------ AsciiString ------------------- // /** * Writes an AsciiString to buf. The length of the string is written first, followed by the chars (as single-byte values). * @param s the string * @param buf the buffer */ public static void writeAsciiString(AsciiString s, ByteBuffer buf) { short length=(short)(s != null? s.length() : -1); buf.putShort(length); if(s != null) buf.put(s.chars()); } /** * Writes an AsciiString to buf. The length of the string is written first, followed by the chars (as single-byte values). * @param s the string * @param out the output stream */ public static void writeAsciiString(AsciiString s, DataOutput out) throws IOException { short length=(short)(s != null? s.length() : -1); out.writeShort(length); if(s != null) out.write(s.chars()); } /** * Reads an AsciiString from buf. The length is read first, followed by the chars. Each char is a single byte * @param buf the buffer * @return the string read from buf */ public static AsciiString readAsciiString(ByteBuffer buf) { short len=buf.getShort(); if(len < 0) return null; AsciiString retval=new AsciiString(len); buf.get(retval.chars()); return retval; } /** * Reads an AsciiString from buf. The length is read first, followed by the chars. Each char is a single byte * @param in the input stream * @return the string read from buf */ public static AsciiString readAsciiString(DataInput in) throws IOException { short len=in.readShort(); if(len < 0) return null; AsciiString retval=new AsciiString(len); in.readFully(retval.chars()); return retval; } /** * Measures the number of bytes required to encode an AsciiSring. * @param str the string * @return the number of bytes required for encoding str */ public static int size(AsciiString str) { return str == null? Global.SHORT_SIZE : Global.SHORT_SIZE + str.length(); } /** * Encodes the number of bytes needed into a single byte. The first number is encoded in the first nibble (the * first 4 bits), the second number in the second nibble * @param len1 The number of bytes needed to store a long. Must be between 0 and 8 * @param len2 The number of bytes needed to store a long. Must be between 0 and 8 * @return The byte storing the 2 numbers len1 and len2 */ protected static byte encodeLength(byte len1, byte len2) { byte retval=len2; retval |= (len1 << 4); return retval; } /* protected static byte[] decodeLength(byte len) { return new byte[]{(byte)((len & 0xff) >> 4),(byte)(len & ~0xf0)}; // 0xf0 is the first nibble set (11110000) }*/ protected static byte firstNibble(byte len) { return (byte)((len & 0xff) >> 4); // 0xf0 is the first nibble set (11110000) } protected static byte secondNibble(byte len) { return (byte)(len & ~0xf0); // 0xf0 is the first nibble set (11110000) } protected static byte bytesRequiredFor(long number) { if(number >> 56 != 0) return 8; if(number >> 48 != 0) return 7; if(number >> 40 != 0) return 6; if(number >> 32 != 0) return 5; if(number >> 24 != 0) return 4; if(number >> 16 != 0) return 3; if(number >> 8 != 0) return 2; return 1; } protected static byte bytesRequiredFor(int number) { if(number >> 24 != 0) return 4; if(number >> 16 != 0) return 3; if(number >> 8 != 0) return 2; return 1; } static protected byte getByteAt(long num, int index) { return (byte)((num >> (index * 8))); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy