blob: c774da937d6248babe95ac91c0dbdb0651075bd8 [file] [log] [blame]
/*
* Copyright 1999-2011 Alibaba Group.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.common.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import com.alibaba.dubbo.common.utils.IOUtils;
/**
* CodecUtils.
*
* @author qian.lei
*/
public class Bytes
{
private static final String C64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; //default base64.
private static final char[] BASE16 = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}, BASE64 = C64.toCharArray();
private static final int MASK4 = 0x0f, MASK6 = 0x3f, MASK8 = 0xff;
private static final Map<Integer, byte[]> DECODE_TABLE_MAP = new ConcurrentHashMap<Integer, byte[]>();
private static ThreadLocal<MessageDigest> MD = new ThreadLocal<MessageDigest>();
/**
* byte array copy.
*
* @param src src.
* @param length new length.
* @return new byte array.
*/
public static byte[] copyOf(byte[] src, int length)
{
byte[] dest = new byte[length];
System.arraycopy(src, 0, dest, 0, Math.min(src.length, length));
return dest;
}
/**
* to byte array.
*
* @param v value.
* @return byte[].
*/
public static byte[] short2bytes(short v)
{
byte[] ret = { 0, 0 };
short2bytes(v, ret);
return ret;
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void short2bytes(short v, byte[] b)
{
short2bytes(v, b, 0);
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void short2bytes(short v, byte[] b, int off)
{
b[off + 1] = (byte) v;
b[off + 0] = (byte) (v >>> 8);
}
/**
* to byte array.
*
* @param v value.
* @return byte[].
*/
public static byte[] int2bytes(int v)
{
byte[] ret = { 0, 0, 0, 0 };
int2bytes(v, ret);
return ret;
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void int2bytes(int v, byte[] b)
{
int2bytes(v, b, 0);
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
* @param off array offset.
*/
public static void int2bytes(int v, byte[] b, int off)
{
b[off + 3] = (byte) v;
b[off + 2] = (byte) (v >>> 8);
b[off + 1] = (byte) (v >>> 16);
b[off + 0] = (byte) (v >>> 24);
}
/**
* to byte array.
*
* @param v value.
* @return byte[].
*/
public static byte[] float2bytes(float v)
{
byte[] ret = { 0, 0, 0, 0 };
float2bytes(v, ret);
return ret;
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void float2bytes(float v, byte[] b)
{
float2bytes(v, b, 0);
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
* @param off array offset.
*/
public static void float2bytes(float v, byte[] b, int off)
{
int i = Float.floatToIntBits(v);
b[off + 3] = (byte) i;
b[off + 2] = (byte) (i >>> 8);
b[off + 1] = (byte) (i >>> 16);
b[off + 0] = (byte) (i >>> 24);
}
/**
* to byte array.
*
* @param v value.
* @return byte[].
*/
public static byte[] long2bytes(long v)
{
byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
long2bytes(v, ret);
return ret;
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void long2bytes(long v, byte[] b)
{
long2bytes(v, b, 0);
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
* @param off array offset.
*/
public static void long2bytes(long v, byte[] b, int off)
{
b[off + 7] = (byte) v;
b[off + 6] = (byte) (v >>> 8);
b[off + 5] = (byte) (v >>> 16);
b[off + 4] = (byte) (v >>> 24);
b[off + 3] = (byte) (v >>> 32);
b[off + 2] = (byte) (v >>> 40);
b[off + 1] = (byte) (v >>> 48);
b[off + 0] = (byte) (v >>> 56);
}
/**
* to byte array.
*
* @param v value.
* @return byte[].
*/
public static byte[] double2bytes(double v)
{
byte[] ret = { 0, 0, 0, 0, 0, 0, 0, 0 };
double2bytes(v, ret);
return ret;
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
*/
public static void double2bytes(double v, byte[] b)
{
double2bytes(v, b, 0);
}
/**
* to byte array.
*
* @param v value.
* @param b byte array.
* @param off array offset.
*/
public static void double2bytes(double v, byte[] b, int off)
{
long j = Double.doubleToLongBits(v);
b[off + 7] = (byte) j;
b[off + 6] = (byte) (j >>> 8);
b[off + 5] = (byte) (j >>> 16);
b[off + 4] = (byte) (j >>> 24);
b[off + 3] = (byte) (j >>> 32);
b[off + 2] = (byte) (j >>> 40);
b[off + 1] = (byte) (j >>> 48);
b[off + 0] = (byte) (j >>> 56);
}
/**
* to short.
*
* @param b byte array.
* @return short.
*/
public static short bytes2short(byte[] b)
{
return bytes2short(b, 0);
}
/**
* to short.
*
* @param b byte array.
* @param off offset.
* @return short.
*/
public static short bytes2short(byte[] b, int off)
{
return (short) (((b[off + 1] & 0xFF) << 0) +
((b[off + 0]) << 8));
}
/**
* to int.
*
* @param b byte array.
* @return int.
*/
public static int bytes2int(byte[] b)
{
return bytes2int(b, 0);
}
/**
* to int.
*
* @param b byte array.
* @param off offset.
* @return int.
*/
public static int bytes2int(byte[] b, int off)
{
return ((b[off + 3] & 0xFF) << 0) +
((b[off + 2] & 0xFF) << 8) +
((b[off + 1] & 0xFF) << 16) +
((b[off + 0]) << 24);
}
/**
* to int.
*
* @param b byte array.
* @return int.
*/
public static float bytes2float(byte[] b)
{
return bytes2float(b, 0);
}
/**
* to int.
*
* @param b byte array.
* @param off offset.
* @return int.
*/
public static float bytes2float(byte[] b, int off)
{
int i = ((b[off + 3] & 0xFF) << 0) +
((b[off + 2] & 0xFF) << 8) +
((b[off + 1] & 0xFF) << 16) +
((b[off + 0]) << 24);
return Float.intBitsToFloat(i);
}
/**
* to long.
*
* @param b byte array.
* @return long.
*/
public static long bytes2long(byte[] b)
{
return bytes2long(b,0);
}
/**
* to long.
*
* @param b byte array.
* @param off offset.
* @return long.
*/
public static long bytes2long(byte[] b,int off)
{
return ((b[off + 7] & 0xFFL) << 0) +
((b[off + 6] & 0xFFL) << 8) +
((b[off + 5] & 0xFFL) << 16) +
((b[off + 4] & 0xFFL) << 24) +
((b[off + 3] & 0xFFL) << 32) +
((b[off + 2] & 0xFFL) << 40) +
((b[off + 1] & 0xFFL) << 48) +
(((long) b[off + 0]) << 56);
}
/**
* to long.
*
* @param b byte array.
* @return double.
*/
public static double bytes2double(byte[] b)
{
return bytes2double(b,0);
}
/**
* to long.
*
* @param b byte array.
* @param off offset.
* @return double.
*/
public static double bytes2double(byte[] b, int off)
{
long j = ((b[off + 7] & 0xFFL) << 0) +
((b[off + 6] & 0xFFL) << 8) +
((b[off + 5] & 0xFFL) << 16) +
((b[off + 4] & 0xFFL) << 24) +
((b[off + 3] & 0xFFL) << 32) +
((b[off + 2] & 0xFFL) << 40) +
((b[off + 1] & 0xFFL) << 48) +
(((long) b[off + 0]) << 56);
return Double.longBitsToDouble(j);
}
/**
* to hex string.
*
* @param bs byte array.
* @return hex string.
*/
public static String bytes2hex(byte[] bs)
{
return bytes2hex(bs, 0, bs.length);
}
/**
* to hex string.
*
* @param bs byte array.
* @param off offset.
* @param len length.
* @return hex string.
*/
public static String bytes2hex(byte[] bs, int off, int len)
{
if( off < 0 )
throw new IndexOutOfBoundsException("bytes2hex: offset < 0, offset is " + off );
if( len < 0 )
throw new IndexOutOfBoundsException("bytes2hex: length < 0, length is " + len );
if( off + len > bs.length )
throw new IndexOutOfBoundsException("bytes2hex: offset + length > array length.");
byte b;
int r = off, w = 0;
char[] cs = new char[len*2];
for(int i=0;i<len;i++)
{
b = bs[r++];
cs[w++] = BASE16[ b >> 4 & MASK4 ];
cs[w++] = BASE16[ b & MASK4 ];
}
return new String(cs);
}
/**
* from hex string.
*
* @param str hex string.
* @return byte array.
*/
public static byte[] hex2bytes(String str)
{
return hex2bytes(str, 0, str.length());
}
/**
* from hex string.
*
* @param str hex string.
* @param off offset.
* @param len length.
* @return byte array.
*/
public static byte[] hex2bytes(final String str, final int off, int len)
{
if( ( len & 1 ) == 1 )
throw new IllegalArgumentException("hex2bytes: ( len & 1 ) == 1.");
if( off < 0 )
throw new IndexOutOfBoundsException("hex2bytes: offset < 0, offset is " + off );
if( len < 0 )
throw new IndexOutOfBoundsException("hex2bytes: length < 0, length is " + len );
if( off + len > str.length() )
throw new IndexOutOfBoundsException("hex2bytes: offset + length > array length.");
int num = len / 2, r = off, w = 0;
byte[] b = new byte[num];
for(int i=0;i<num;i++)
b[w++] = (byte)( hex(str.charAt(r++)) << 4 | hex(str.charAt(r++)) );
return b;
}
/**
* to base64 string.
*
* @param b byte array.
* @return base64 string.
*/
public static String bytes2base64(byte[] b)
{
return bytes2base64(b, 0, b.length, BASE64);
}
/**
* to base64 string.
*
* @param b byte array.
* @return base64 string.
*/
public static String bytes2base64(byte[] b, int offset, int length)
{
return bytes2base64(b, offset, length, BASE64);
}
/**
* to base64 string.
*
* @param b byte array.
* @param code base64 code string(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
public static String bytes2base64(byte[] b, String code)
{
return bytes2base64(b, 0, b.length, code);
}
/**
* to base64 string.
*
* @param b byte array.
* @param code base64 code string(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
public static String bytes2base64(byte[] b, int offset, int length, String code)
{
if( code.length() < 64 )
throw new IllegalArgumentException("Base64 code length < 64.");
return bytes2base64(b, offset, length, code.toCharArray());
}
/**
* to base64 string.
*
* @param b byte array.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
public static String bytes2base64(byte[] b, char[] code)
{
return bytes2base64(b, 0, b.length, code);
}
/**
* to base64 string.
*
* @param bs byte array.
* @param off offset.
* @param len length.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return base64 string.
*/
public static String bytes2base64(final byte[] bs, final int off, final int len, final char[] code)
{
if( off < 0 )
throw new IndexOutOfBoundsException("bytes2base64: offset < 0, offset is " + off );
if( len < 0 )
throw new IndexOutOfBoundsException("bytes2base64: length < 0, length is " + len );
if( off + len > bs.length )
throw new IndexOutOfBoundsException("bytes2base64: offset + length > array length.");
if( code.length < 64 )
throw new IllegalArgumentException("Base64 code length < 64.");
boolean pad = code.length > 64; // has pad char.
int num = len / 3, rem = len % 3, r = off, w = 0;
char[] cs = new char[ num * 4 + ( rem == 0 ? 0 : pad ? 4 : rem + 1 ) ];
for(int i=0;i<num;i++)
{
int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8, b3 = bs[r++] & MASK8;
cs[w++] = code[ b1 >> 2 ];
cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
cs[w++] = code[ ( b2 << 2 ) & MASK6 | ( b3 >> 6 ) ];
cs[w++] = code[ b3 & MASK6 ];
}
if( rem == 1 )
{
int b1 = bs[r++] & MASK8;
cs[w++] = code[ b1 >> 2 ];
cs[w++] = code[ ( b1 << 4 ) & MASK6 ];
if( pad )
{
cs[w++] = code[64];
cs[w++] = code[64];
}
}
else if( rem == 2 )
{
int b1 = bs[r++] & MASK8, b2 = bs[r++] & MASK8;
cs[w++] = code[ b1 >> 2 ];
cs[w++] = code[ ( b1 << 4 ) & MASK6 | ( b2 >> 4 ) ];
cs[w++] = code[ ( b2 << 2 ) & MASK6 ];
if( pad )
cs[w++] = code[64];
}
return new String(cs);
}
/**
* from base64 string.
*
* @param str base64 string.
* @return byte array.
*/
public static byte[] base642bytes(String str)
{
return base642bytes(str, 0, str.length());
}
/**
* from base64 string.
*
* @param str base64 string.
* @param offset offset.
* @param length length.
* @return byte array.
*/
public static byte[] base642bytes(String str, int offset, int length)
{
return base642bytes(str, offset, length, C64);
}
/**
* from base64 string.
*
* @param str base64 string.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
public static byte[] base642bytes(String str, String code)
{
return base642bytes(str, 0, str.length(), code);
}
/**
* from base64 string.
*
* @param str base64 string.
* @param off offset.
* @param len length.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
public static byte[] base642bytes(final String str, final int off, final int len, final String code)
{
if( off < 0 )
throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
if( len < 0 )
throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
if( off + len > str.length() )
throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
if( code.length() < 64 )
throw new IllegalArgumentException("Base64 code length < 64.");
int rem = len % 4;
if( rem == 1 )
throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
int num = len / 4, size = num * 3;
if( code.length() > 64 )
{
if( rem != 0 )
throw new IllegalArgumentException("base642bytes: base64 string length error.");
char pc = code.charAt(64);
if( str.charAt(off+len-2) == pc )
{
size -= 2;
--num;
rem = 2;
}
else if( str.charAt(off+len-1) == pc )
{
size--;
--num;
rem = 3;
}
}
else
{
if( rem == 2 )
size++;
else if( rem == 3 )
size += 2;
}
int r = off, w = 0;
byte[] b = new byte[size], t = decodeTable(code);
for(int i=0;i<num;i++)
{
int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
int c3 = t[str.charAt(r++)], c4 = t[str.charAt(r++)];
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
b[w++] = (byte)( ( c3 << 6 ) | c4 );
}
if( rem == 2 )
{
int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)];
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
}
else if( rem == 3 )
{
int c1 = t[str.charAt(r++)], c2 = t[str.charAt(r++)], c3 = t[str.charAt(r++)];
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
}
return b;
}
/**
* from base64 string.
*
* @param str base64 string.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
public static byte[] base642bytes(String str, char[] code)
{
return base642bytes(str, 0, str.length(), code);
}
/**
* from base64 string.
*
* @param str base64 string.
* @param off offset.
* @param len length.
* @param code base64 code(0-63 is base64 char,64 is pad char).
* @return byte array.
*/
public static byte[] base642bytes(final String str, final int off, final int len, final char[] code)
{
if( off < 0 )
throw new IndexOutOfBoundsException("base642bytes: offset < 0, offset is " + off );
if( len < 0 )
throw new IndexOutOfBoundsException("base642bytes: length < 0, length is " + len );
if( off + len > str.length() )
throw new IndexOutOfBoundsException("base642bytes: offset + length > string length.");
if( code.length < 64 )
throw new IllegalArgumentException("Base64 code length < 64.");
int rem = len % 4;
if( rem == 1 )
throw new IllegalArgumentException("base642bytes: base64 string length % 4 == 1.");
int num = len / 4, size = num * 3;
if( code.length > 64 )
{
if( rem != 0 )
throw new IllegalArgumentException("base642bytes: base64 string length error.");
char pc = code[64];
if( str.charAt(off+len-2) == pc )
size -= 2;
else if( str.charAt(off+len-1) == pc )
size--;
}
else
{
if( rem == 2 )
size++;
else if( rem == 3 )
size += 2;
}
int r = off, w = 0;
byte[] b = new byte[size];
for(int i=0;i<num;i++)
{
int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
int c3 = indexOf(code, str.charAt(r++)), c4 = indexOf(code, str.charAt(r++));
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
b[w++] = (byte)( ( c3 << 6 ) | c4 );
}
if( rem == 2 )
{
int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++));
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
}
else if( rem == 3 )
{
int c1 = indexOf(code, str.charAt(r++)), c2 = indexOf(code, str.charAt(r++)), c3 = indexOf(code, str.charAt(r++));
b[w++] = (byte)( ( c1 << 2 ) | ( c2 >> 4 ) );
b[w++] = (byte)( ( c2 << 4 ) | ( c3 >> 2 ) );
}
return b;
}
/**
* zip.
*
* @param bytes source.
* @return compressed byte array.
* @throws IOException.
*/
public static byte[] zip(byte[] bytes) throws IOException
{
UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
OutputStream os = new DeflaterOutputStream(bos);
try
{
os.write(bytes);
}
finally
{
os.close();
bos.close();
}
return bos.toByteArray();
}
/**
* unzip.
*
* @param bytes compressed byte array.
* @return byte uncompressed array.
* @throws IOException
*/
public static byte[] unzip(byte[] bytes) throws IOException
{
UnsafeByteArrayInputStream bis = new UnsafeByteArrayInputStream(bytes);
UnsafeByteArrayOutputStream bos = new UnsafeByteArrayOutputStream();
InputStream is = new InflaterInputStream(bis);
try
{
IOUtils.write(is, bos);
return bos.toByteArray();
}
finally
{
is.close();
bis.close();
bos.close();
}
}
/**
* get md5.
*
* @param str input string.
* @return MD5 byte array.
*/
public static byte[] getMD5(String str)
{
return getMD5(str.getBytes());
}
/**
* get md5.
*
* @param source byte array source.
* @return MD5 byte array.
*/
public static byte[] getMD5(byte[] source)
{
MessageDigest md = getMessageDigest();
return md.digest(source);
}
/**
* get md5.
*
* @param file file source.
* @return MD5 byte array.
*/
public static byte[] getMD5(File file) throws IOException
{
InputStream is = new FileInputStream(file);
try{ return getMD5(is); }
finally{ is.close(); }
}
/**
* get md5.
*
* @param is input stream.
* @return MD5 byte array.
*/
public static byte[] getMD5(InputStream is) throws IOException
{
return getMD5(is, 1024 * 8);
}
private static byte hex(char c)
{
if( c <= '9' ) return (byte)( c - '0' );
if( c >= 'a' && c <= 'f' ) return (byte)( c - 'a' + 10 );
if( c >= 'A' && c <= 'F' ) return (byte)( c - 'A' + 10 );
throw new IllegalArgumentException("hex string format error [" + c + "].");
}
private static int indexOf(char[] cs, char c)
{
for(int i=0,len=cs.length;i<len;i++)
if( cs[i] == c ) return i;
return -1;
}
private static byte[] decodeTable(String code)
{
int hash = code.hashCode();
byte[] ret = DECODE_TABLE_MAP.get(hash);
if( ret == null )
{
if( code.length() < 64 )
throw new IllegalArgumentException("Base64 code length < 64.");
// create new decode table.
ret = new byte[128];
for(int i=0;i<128;i++) // init table.
ret[i] = -1;
for(int i=0;i<64;i++)
ret[code.charAt(i)] = (byte)i;
DECODE_TABLE_MAP.put(hash, ret);
}
return ret;
}
private static byte[] getMD5(InputStream is, int bs) throws IOException
{
MessageDigest md = getMessageDigest();
byte[] buf = new byte[bs];
while( is.available() > 0 )
{
int read, total = 0;
do
{
if( ( read = is.read(buf, total, bs-total) ) <= 0 )
break;
total += read;
}
while( total < bs );
md.update(buf);
}
return md.digest();
}
private static MessageDigest getMessageDigest()
{
MessageDigest ret = MD.get();
if( ret == null )
{
try
{
ret = MessageDigest.getInstance("MD5");
MD.set(ret);
}
catch(NoSuchAlgorithmException e)
{
throw new RuntimeException(e);
}
}
return ret;
}
private Bytes(){}
}