package org.mapdb; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.util.Arrays; /** * Various IO classes and utilities.. */ public final class DataIO { private DataIO(){} /** * Unpack int value from the input stream. * * @param in The input stream. * @return The long value. * * @throws java.io.IOException in case of IO error */ static public int unpackInt(DataInput in) throws IOException { int ret = 0; byte v; do{ v = in.readByte(); ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } /** * Unpack long value from the input stream. * * @param in The input stream. * @return The long value. * * @throws java.io.IOException in case of IO error */ static public long unpackLong(DataInput in) throws IOException { long ret = 0; byte v; do{ v = in.readByte(); ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } /** * Unpack int value from the input stream. * * @param in The input stream. * @return The long value. * * @throws java.io.IOException in case of IO error */ static public int unpackInt(InputStream in) throws IOException { int ret = 0; int v; do{ v = in.read(); if(v==-1) throw new EOFException(); ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } /** * Unpack long value from the input stream. * * @param in The input stream. * @return The long value. * * @throws java.io.IOException in case of IO error */ static public long unpackLong(InputStream in) throws IOException { long ret = 0; int v; do{ v = in.read(); if(v==-1) throw new EOFException(); ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } /** * Pack long into output. * It will occupy 1-10 bytes depending on value (lower values occupy smaller space) * * @param out DataOutput to put value into * @param value to be serialized, must be non-negative * * @throws java.io.IOException in case of IO error */ static public void packLong(DataOutput out, long value) throws IOException { //$DELAY$ int shift = 63-Long.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ out.writeByte((byte) ((value>>>shift) & 0x7F) ); //$DELAY$ shift-=7; } out.writeByte((byte) ((value & 0x7F)|0x80)); } /** * Pack long into output. * It will occupy 1-10 bytes depending on value (lower values occupy smaller space) * * @param out OutputStream to put value into * @param value to be serialized, must be non-negative * * @throws java.io.IOException in case of IO error */ static public void packLong(OutputStream out, long value) throws IOException { //$DELAY$ int shift = 63-Long.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ out.write((int) ((value>>>shift) & 0x7F)); //$DELAY$ shift-=7; } out.write((int) ((value & 0x7F)|0x80)); } /** * Calculate how much bytes packed long consumes. * * @param value to calculate * @return number of bytes used in packed form */ public static int packLongSize(long value) { int shift = 63-Long.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 int ret = 1; while(shift!=0){ //PERF remove cycle, just count zeroes shift-=7; ret++; } return ret; } /** * Unpack RECID value from the input stream with 3 bit checksum. * * @param in The input stream. * @return The long value. * @throws java.io.IOException in case of IO error */ static public long unpackRecid(DataInput2 in) throws IOException { long val = in.unpackLong(); val = DataIO.parity1Get(val); return val >>> 1; } /** * Pack RECID into output stream with 3 bit checksum. * It will occupy 1-10 bytes depending on value (lower values occupy smaller space) * * @param out String to put value into * @param value to be serialized, must be non-negative * @throws java.io.IOException in case of IO error */ static public void packRecid(DataOutput2 out, long value) throws IOException { value = DataIO.parity1Set(value<<1); out.packLong(value); } /** * Pack int into an output stream. * It will occupy 1-5 bytes depending on value (lower values occupy smaller space) * * @param out DataOutput to put value into * @param value to be serialized, must be non-negative * @throws java.io.IOException in case of IO error */ static public void packInt(DataOutput out, int value) throws IOException { // Optimize for the common case where value is small. This is particular important where our caller // is SerializerBase.SER_STRING.serialize because most chars will be ASCII characters and hence in this range. // credit Max Bolingbroke https://github.com/jankotek/MapDB/pull/489 int shift = (value & ~0x7F); //reuse variable if (shift != 0) { //$DELAY$ shift = 31-Integer.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ out.writeByte((byte) ((value>>>shift) & 0x7F)); //$DELAY$ shift-=7; } } //$DELAY$ out.writeByte((byte) ((value & 0x7F)|0x80)); } /** * Pack int into an output stream. * It will occupy 1-5 bytes depending on value (lower values occupy smaller space) * * This method is same as {@link #packInt(DataOutput, int)}, * but is optimized for values larger than 127. Usually it is recids. * * @param out String to put value into * @param value to be serialized, must be non-negative * @throws java.io.IOException in case of IO error */ static public void packIntBigger(DataOutput out, int value) throws IOException { //$DELAY$ int shift = 31-Integer.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ out.writeByte((byte) ((value>>>shift) & 0x7F)); //$DELAY$ shift-=7; } //$DELAY$ out.writeByte((byte) ((value & 0x7F)|0x80)); } public static int longHash(long h) { //$DELAY$ h = h * -7046029254386353131L; h ^= h >> 32; return (int)(h ^ h >> 16); } public static int intHash(int h) { //$DELAY$ h = h * -1640531527; return h ^ h >> 16; } public static final long PACK_LONG_RESULT_MASK = 0xFFFFFFFFFFFFFFFL; public static int getInt(byte[] buf, int pos) { return (((int)buf[pos++]) << 24) | (((int)buf[pos++] & 0xFF) << 16) | (((int)buf[pos++] & 0xFF) << 8) | (((int)buf[pos] & 0xFF)); } public static void putInt(byte[] buf, int pos,int v) { buf[pos++] = (byte) (0xff & (v >> 24)); //TODO PERF is >>> faster here? Also invert 0xFF &? buf[pos++] = (byte) (0xff & (v >> 16)); buf[pos++] = (byte) (0xff & (v >> 8)); buf[pos] = (byte) (0xff & (v)); } public static long getLong(byte[] buf, int pos) { return ((((long)buf[pos++]) << 56) | (((long)buf[pos++] & 0xFF) << 48) | (((long)buf[pos++] & 0xFF) << 40) | (((long)buf[pos++] & 0xFF) << 32) | (((long)buf[pos++] & 0xFF) << 24) | (((long)buf[pos++] & 0xFF) << 16) | (((long)buf[pos++] & 0xFF) << 8) | (((long)buf[pos] & 0xFF))); } public static void putLong(byte[] buf, int pos,long v) { buf[pos++] = (byte) (0xff & (v >> 56)); buf[pos++] = (byte) (0xff & (v >> 48)); buf[pos++] = (byte) (0xff & (v >> 40)); buf[pos++] = (byte) (0xff & (v >> 32)); buf[pos++] = (byte) (0xff & (v >> 24)); buf[pos++] = (byte) (0xff & (v >> 16)); buf[pos++] = (byte) (0xff & (v >> 8)); buf[pos] = (byte) (0xff & (v)); } public static void putLong(byte[] buf, int pos,long v, int vSize) { for(int i=vSize-1; i>=0; i--){ buf[i+pos] = (byte) (0xff & v); v >>>=8; } } public static int packInt(byte[] buf, int pos, int value){ int pos2 = pos; int shift = 31-Integer.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ buf[pos++] = (byte) ((value>>>shift) & 0x7F); shift-=7; } buf[pos++] = (byte) ((value & 0x7F)|0x80); return pos-pos2; } public static int packLong(byte[] buf, int pos, long value){ int pos2 = pos; int shift = 63-Long.numberOfLeadingZeros(value); shift -= shift%7; // round down to nearest multiple of 7 while(shift!=0){ buf[pos++] = (byte) ((value>>>shift) & 0x7F); shift-=7; } buf[pos++] = (byte) ((value & 0x7F) | 0x80); return pos - pos2; } public static int unpackInt(byte[] buf, int pos){ int ret = 0; byte v; do{ //$DELAY$ v = buf[pos++]; ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } public static long unpackLong(byte[] buf, int pos){ long ret = 0; byte v; do{ //$DELAY$ v = buf[pos++]; ret = (ret<<7 ) | (v & 0x7F); }while((v&0x80)==0); return ret; } public static long getSixLong(byte[] buf, int pos) { return ((long) (buf[pos++] & 0xff) << 40) | ((long) (buf[pos++] & 0xff) << 32) | ((long) (buf[pos++] & 0xff) << 24) | ((long) (buf[pos++] & 0xff) << 16) | ((long) (buf[pos++] & 0xff) << 8) | ((long) (buf[pos] & 0xff)); } public static void putSixLong(byte[] buf, int pos, long value) { if(CC.ASSERT && (value>>>48!=0)) throw new AssertionError(); buf[pos++] = (byte) (0xff & (value >> 40)); buf[pos++] = (byte) (0xff & (value >> 32)); buf[pos++] = (byte) (0xff & (value >> 24)); buf[pos++] = (byte) (0xff & (value >> 16)); buf[pos++] = (byte) (0xff & (value >> 8)); buf[pos] = (byte) (0xff & (value)); } public static long nextPowTwo(final long a) { return 1L << (64 - Long.numberOfLeadingZeros(a - 1L)); } public static int nextPowTwo(final int a) { return 1 << (32 - Integer.numberOfLeadingZeros(a - 1)); } public static void readFully(InputStream in, byte[] data, int offset, int len) throws IOException { len+=offset; for(; offset<len;){ int c = in.read(data, offset, len - offset); if(c<0) throw new EOFException(); offset+=c; } } public static void readFully(InputStream in, byte[] data) throws IOException { readFully(in, data, 0, data.length); } public static void writeFully(FileChannel f, ByteBuffer buf ) throws IOException { int rem = buf.remaining(); while(rem>0) rem-=f.write(buf); } public static void skipFully(InputStream in, long length) throws IOException { while ((length -= in.skip(length)) > 0); } public static long fillLowBits(int bitCount) { long ret = 0; for(;bitCount>0;bitCount--){ ret = (ret<<1)|1; } return ret; } public static long parity1Set(long i) { if(CC.ASSERT && (i&1)!=0) throw new DBException.PointerChecksumBroken(); return i | ((Long.bitCount(i)+1)%2); } public static int parity1Set(int i) { if(CC.ASSERT && (i&1)!=0) throw new DBException.PointerChecksumBroken(); return i | ((Integer.bitCount(i)+1)%2); } public static long parity1Get(long i) { if(Long.bitCount(i)%2!=1){ throw new DBException.PointerChecksumBroken(); } return i&0xFFFFFFFFFFFFFFFEL; } public static int parity1Get(int i) { if(Integer.bitCount(i)%2!=1){ throw new DBException.PointerChecksumBroken(); } return i&0xFFFFFFFE; } public static long parity3Set(long i) { if(CC.ASSERT && (i&0x7)!=0) throw new DBException.PointerChecksumBroken(); return i | ((Long.bitCount(i)+1)%8); } public static long parity3Get(long i) { long ret = i&0xFFFFFFFFFFFFFFF8L; if((Long.bitCount(ret)+1)%8!=(i&0x7)){ throw new DBException.PointerChecksumBroken(); } return ret; } public static long parity4Set(long i) { if(CC.ASSERT && (i&0xF)!=0) throw new DBException.PointerChecksumBroken(); return i | ((Long.bitCount(i)+1)%16); } public static long parity4Get(long i) { long ret = i&0xFFFFFFFFFFFFFFF0L; if((Long.bitCount(ret)+1)%16!=(i&0xF)){ throw new DBException.PointerChecksumBroken(); } return ret; } public static long parity16Set(long i) { if(CC.ASSERT && (i&0xFFFF)!=0) throw new DBException.PointerChecksumBroken(); return i | (DataIO.longHash(i+1)&0xFFFFL); } public static long parity16Get(long i) { long ret = i&0xFFFFFFFFFFFF0000L; if((DataIO.longHash(ret+1)&0xFFFFL) != (i&0xFFFFL)){ throw new DBException.PointerChecksumBroken(); } return ret; } /** * Converts binary array into its hexadecimal representation. * * @param bb binary data * @return hexadecimal string */ public static String toHexa( byte [] bb ) { char[] HEXA_CHARS = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; char[] ret = new char[bb.length*2]; for(int i=0;i<bb.length;i++){ ret[i*2] =HEXA_CHARS[((bb[i]& 0xF0) >> 4)]; ret[i*2+1] = HEXA_CHARS[((bb[i] & 0x0F))]; } return new String(ret); } /** * Converts hexadecimal string into binary data * @param s hexadecimal string * @return binary data * @throws NumberFormatException in case of string format error */ public static byte[] fromHexa(String s ) { byte[] ret = new byte[s.length()/2]; for(int i=0;i<ret.length;i++){ ret[i] = (byte) Integer.parseInt(s.substring(i*2,i*2+2),16); } return ret; } /* expand array size by 1, and put value at given position. No items from original array are lost*/ public static Object[] arrayPut(final Object[] array, final int pos, final Object value){ final Object[] ret = Arrays.copyOf(array, array.length+1); if(pos<array.length){ System.arraycopy(array, pos, ret, pos+1, array.length-pos); } ret[pos] = value; return ret; } public static Object[] arrayDelete(Object[] vals, int pos, int len) { Object[] vals2 = new Object[vals.length-len]; System.arraycopy(vals,0,vals2, 0, pos-len); System.arraycopy(vals, pos, vals2, pos-len, vals2.length-(pos-len)); return vals2; } public static long[] arrayDelete(long[] vals, int pos, int len) { long[] vals2 = new long[vals.length - len]; System.arraycopy(vals, 0, vals2, 0, pos - len); System.arraycopy(vals, pos, vals2, pos - len, vals2.length - (pos - len)); return vals2; } /** converts 4 int bytes to lowest 4 long bytes. Does not preserve negative flag */ public static long intToLong(int i) { return i & 0xffffffffL; } public static long roundUp(long number, long roundUpToMultipleOf) { return ((number+roundUpToMultipleOf-1)/(roundUpToMultipleOf))*roundUpToMultipleOf; } public static long roundDown(long number, long roundDownToMultipleOf) { return number - number % roundDownToMultipleOf; } public static int roundUp(int number, int roundUpToMultipleOf) { return ((number+roundUpToMultipleOf-1)/(roundUpToMultipleOf))*roundUpToMultipleOf; } public static int roundDown(int number, int roundDownToMultipleOf) { return number - number % roundDownToMultipleOf; } public static int shift(int value) { return 31-Integer.numberOfLeadingZeros(value); } /** return true if operating system is Windows*/ static boolean isWindows(){ String os = System.getProperty("os.name"); return os!=null && os.toLowerCase().startsWith("windows"); } /** * Check if large files can be mapped into memory. * For example 32bit JVM can only address 2GB and large files can not be mapped, * so for 32bit JVM this function returns false. * */ static boolean JVMSupportsLargeMappedFiles() { String arch = System.getProperty("os.arch"); if(arch==null || !arch.contains("64")) { return false; } if(isWindows()) return false; //TODO better check for 32bit JVM return true; } }