package org.voovan.tools; import org.voovan.tools.Exception.MemoryReleasedException; import org.voovan.tools.log.Logger; import org.voovan.tools.reflect.TReflect; import sun.misc.Cleaner; import sun.misc.Unsafe; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Constructor; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; /** * ByteBuffer双向通道 * * @author helyho * * Voovan Framework. * WebSite: https://github.com/helyho/Voovan * Licence: Apache v2 License */ public class ByteBufferChannel { private long address; private Unsafe unsafe = TUnsafe.getUnsafe(); private ByteBuffer byteBuffer; private int size; private ReentrantLock lock ; private Deallocator deallocator; private Cleaner cleaner; /** * 构造函数 * @param capacity 分配的容量 */ public ByteBufferChannel(int capacity) { init(capacity); } /** * 构造函数 */ public ByteBufferChannel() { init(256); } /** * 初始化函数 * @param capacity 分配的容量 */ private void init(int capacity){ lock = new ReentrantLock(); this.byteBuffer = newByteBuffer(capacity); byteBuffer.limit(0); resetAddress(); this.size = 0; } /** * 构造一个ByteBuffer * @param capacity 分配的容量 * @return ByteBuffer 对象 */ private ByteBuffer newByteBuffer(int capacity){ try { ByteBuffer template = ByteBuffer.allocateDirect(0); Constructor c = template.getClass().getDeclaredConstructor(long.class, int.class); c.setAccessible(true); address = TUnsafe.getUnsafe().allocateMemory(capacity); ByteBuffer instance = (ByteBuffer) c.newInstance(address, capacity); deallocator = new Deallocator(address, capacity); cleaner = Cleaner.create(this, deallocator); return instance; }catch(Exception e){ Logger.error("Create ByteBufferChannel error. ", e); return null; } } /** * 是否已经释放 * @return true 已释放, false: 未释放 */ public boolean isReleased(){ if(address == 0){ return true; }else{ return false; } } /** * 立刻释放内存 */ public void release(){ lock.lock(); try{ if(address != 0) { cleaner.clean(); address = 0; } }finally { lock.unlock(); } } private static class Deallocator implements Runnable { private long address; private int capacity; private Deallocator(long address, int capacity) { this.address = address; this.capacity = capacity; } public void setAddress(long address){ this.address = address; } public void run() { if (this.address == 0) { return; } TUnsafe.getUnsafe().freeMemory(address); address = 0; } } /** * 重新设置当前内存地址 */ private void resetAddress(){ lock.lock(); try { this.address = TReflect.getFieldValue(byteBuffer, "address"); deallocator.setAddress(address); }catch (ReflectiveOperationException e){ Logger.error("ByteBufferChannel resetAddress() Error: "+e.getMessage(), e); } finally { lock.unlock(); } } /** * 当前数组空闲的大小 * @return 当前数组空闲的大小. -1: 已释放 */ public int available(){ if(isReleased()){ return -1; } return byteBuffer.capacity() - size; } /** * 返回当前分配的容量 * @return 当前分配的容量. -1: 已释放 */ public int capacity(){ if(isReleased()){ return -1; } return byteBuffer.capacity(); } /** * 当前数据大小 * @return 数据大小 . -1: 已释放 */ public int size(){ if(isReleased()){ return -1; } return size; } /** * 获取缓冲区有效字节数组的一个拷贝 * 修改这个数组将不会影响当前对象 * 返回 0 到 size 的有效数据 * 从堆外复制到堆内 * @return 缓冲区有效字节数组. null: 已释放 */ public byte[] array(){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } lock.lock(); try { byte[] temp = new byte[size()]; unsafe.copyMemory(null, address, temp, Unsafe.ARRAY_BYTE_BASE_OFFSET, size()); return temp; } finally { lock.unlock(); } } /** * 清空通道 */ public void clear(){ if(isReleased()){ return; } lock.lock(); try{ byteBuffer.clear(); size = 0; } finally { lock.unlock(); } } /** * 收缩通道内的数据 * * @param shrinkSize 收缩的偏移量, 大于0,从尾部收缩数据,小于0 从头部收缩数据 * @return true: 成功, false: 失败 */ public boolean shrink(int shrinkSize){ if(isReleased()){ return false; } if(Math.abs(shrinkSize) > size){ return true; } lock.lock(); try{ if(shrinkSize>0){ size = size - shrinkSize; byteBuffer.limit(size); return true; }else if(shrinkSize < 0 ){ int position = byteBuffer.position(); byteBuffer.position(shrinkSize * -1); if (TByteBuffer.moveData(byteBuffer, shrinkSize)) { size = size + shrinkSize; byteBuffer.limit(size); int newPosition = position+shrinkSize; newPosition = newPosition < 0 ? 0 : newPosition; byteBuffer.position(newPosition); return true; }else{ //收缩失败了,重置原 position 的位置 byteBuffer.position(position); return false; } } else{ return true; } } finally { lock.unlock(); } } /** * 获取某个偏移量位置的 byte 数据 * 该操作不会导致通道内的数据发生变化 * @param offset 偏移量 * @return byte 数据 */ public byte get(int offset) throws IndexOutOfBoundsException { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(offset >= 0 && offset <= size) { lock.lock(); try { byte result = unsafe.getByte(address + offset); return result; } finally { lock.unlock(); } } else { throw new IndexOutOfBoundsException(); } } /** * 获取某个偏移量位置的 byte 数据数组 * 该操作不会导致通道内的数据发生变化 * @param offset 偏移量 * @param dst 目标数组 * @param length 长度 * @return 获取数据的长度 */ public int get(int offset, byte[] dst, int length) throws IndexOutOfBoundsException { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(offset >= 0 && length <= size - offset) { lock.lock(); try { int arrSize = length; if(length > size){ arrSize = size; } unsafe.copyMemory(null, address + offset, dst, Unsafe.ARRAY_BYTE_BASE_OFFSET, length); return arrSize; } finally { lock.unlock(); } } else { throw new IndexOutOfBoundsException(); } } /** * 获取某个偏移量位置的 byte 数据数组 * 该操作不会导致通道内的数据发生变化 * @param dst 目标数组 * @return 获取数据的长度 */ public int get(byte[] dst){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } lock.lock(); try { int arrSize = dst.length; if(dst.length > size){ arrSize = size; } unsafe.copyMemory(null, address, dst, Unsafe.ARRAY_BYTE_BASE_OFFSET, arrSize); return arrSize; }finally { lock.unlock(); } } /** * 获取缓冲区 * 返回 0 到 size 的有效数据 * 为了保证数据一致性, 这里会加锁, 在调用getByteBuffer()方法后,所有读写操作都会被阻塞 * 所以必须配合 compact() 方法使用,否则会导致锁死. * @return ByteBuffer 对象 */ public ByteBuffer getByteBuffer(){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } lock.lock(); return byteBuffer; } /** * 收缩通道 * 将通过 getByteBuffer() 方法获得 ByteBuffer 对象的操作同步到 ByteBufferChannel * 如果之前最后一次通过 getByteBuffer() 方法获得过 ByteBuffer,则使用这个 ByteBuffer 来收缩通道 * 将 (position 到 limit) 之间的数据 移动到 (0 到 limit - position) 其他情形将不做任何操作 * 所以 建议 getByteBuffer() 和 compact() 成对操作 * @return 是否compact成功,true:成功, false:失败 */ public boolean compact(){ if(isReleased()){ return false; } if(!lock.isLocked()){ lock.lock(); } try{ if(byteBuffer.position() == 0){ return true; } int position = byteBuffer.position(); boolean result = false; if(TByteBuffer.moveData(byteBuffer, position*-1)) { byteBuffer.position(0); size = size - position; byteBuffer.limit(size); result = true; } return result; } finally { if(lock.isLocked()) { lock.unlock(); } } } /** * 等待期望的数据长度 * @param length 期望的数据长度 * @param timeout 超时时间,单位: 秒 * @return true: 具备期望长度的数据, false: 等待数据超时 */ public boolean waitData(int length,int timeout){ while(timeout > 0){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(size >= length){ return true; } timeout -- ; TEnv.sleep(1); } return false; } /** * 等待收到期望的数据 * @param mark 期望出现的数据 * @param timeout 超时时间,单位: 秒 * @return true: 具备期望长度的数据, false: 等待数据超时 */ public boolean waitData(byte[] mark, int timeout){ while(timeout > 0){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(indexOf(mark) != -1){ return true; } timeout -- ; TEnv.sleep(1); } return false; } /** * 缓冲区头部写入 * @param src 需要写入的缓冲区 ByteBuffer 对象 * @return 写入的数据大小 */ public int writeEnd(ByteBuffer src) { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(src==null){ return -1; } lock.lock(); try { int writeSize = src.limit() - src.position(); int limit = byteBuffer.limit(); if (writeSize > 0) { //是否扩容 if (available() < writeSize) { int newSize = byteBuffer.capacity() + writeSize; if (TByteBuffer.reallocate(byteBuffer, newSize)) { resetAddress(); } } int position = byteBuffer.position(); byteBuffer.position(size); int old = byteBuffer.limit(); size = size + writeSize; byteBuffer.limit(size); byteBuffer.put(src); byteBuffer.position(position); } // Logger.simple("W: " + writeSize + " \t" + limit + " \t" + byteBuffer.limit()); return writeSize; } finally { lock.unlock(); } } /** * 缓冲区尾部写入 * @param src 需要写入的缓冲区 ByteBuffer 对象 * @return 读出的数据大小 */ public int writeHead(ByteBuffer src) { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if (src == null) { return -1; } lock.lock(); try { byte[] srcByte = src.array(); int writeSize = src.limit() - src.position(); if (writeSize > 0) { //是否扩容 if (available() < writeSize) { int newSize = byteBuffer.capacity() + writeSize; if (TByteBuffer.reallocate(byteBuffer, newSize)) { resetAddress(); } } int position = byteBuffer.position(); byteBuffer.position(0); //内容移动到 writeSize 之后 if (TByteBuffer.moveData(byteBuffer, writeSize)) { byteBuffer.position(0); byteBuffer.put(src); size = size + writeSize; byteBuffer.limit(size); position = position + writeSize; position = position > size ? size : position; byteBuffer.position(position); } } return writeSize; } finally { lock.unlock(); } } /** * 从缓冲区头部读取数据 * @param dst 需要读入数据的缓冲区ByteBuffer 对象 * @return 读出的数据大小 */ public int readHead(ByteBuffer dst) { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(dst==null){ return -1; } lock.lock(); try { int readSize = 0; //确定读取大小 if (dst.remaining() > size) { readSize = size; } else { readSize = dst.remaining(); } int limit = byteBuffer.limit(); if (readSize != 0) { int position = byteBuffer.position(); byteBuffer.position(0); for (int i = 0; i < readSize; i++) { dst.put(byteBuffer.get()); } if (TByteBuffer.moveData(byteBuffer, (readSize*-1))) { size = size - readSize; byteBuffer.limit(size); position = position+ (readSize*-1); position = position < 0 ? 0 : position; byteBuffer.position(position); dst.flip(); } else { dst.reset(); } } return readSize; } finally { lock.unlock(); } } /** * 从缓冲区尾部读取数据 * @param dst 需要读入数据的缓冲区ByteBuffer 对象 * @return 读出的数据大小 */ public int readEnd(ByteBuffer dst) { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(dst==null){ return -1; } lock.lock(); try { int readSize = 0; //确定读取大小 if (dst.remaining() > size) { readSize = size; } else { readSize = dst.remaining(); } if (readSize != 0) { int position = byteBuffer.position(); byteBuffer.position(size - readSize); for (int i = 0; i < readSize; i++) { dst.put(byteBuffer.get()); } size = size - readSize; byteBuffer.limit(size); position = position > size ? size : position; byteBuffer.position(position); } dst.flip(); return readSize; } finally { lock.unlock(); } } /** * 查找特定 byte 标识的位置 * byte 标识数组第一个字节的索引位置 * @param mark byte 标识数组 * @return 第一个字节的索引位置 */ public int indexOf(byte[] mark){ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(size == 0){ return -1; } int index = -1; byte[] tmp = new byte[mark.length]; for(int offset = 0;offset <= size - mark.length; offset++){ get(offset, tmp, tmp.length); if(Arrays.equals(mark, tmp)){ index = offset; break; } } return index; } /** * 读取一行 * @return 字符串 */ public String readLine() { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } if(size == 0){ return null; } String lineStr = ""; int index = indexOf("\n".getBytes()); if(index > 0) { byteBuffer.position(0); ByteBuffer lineBuffer = ByteBuffer.allocateDirect(index + 1); int readSize = readHead(lineBuffer); if (readSize == index + 1) { lineStr = TByteBuffer.toString(lineBuffer); } TByteBuffer.release(lineBuffer); } return lineStr.isEmpty()?null:lineStr; } /** * 从 InputStream 读取一段,使用 byte数组 分割 * 返回的 byte数组中不包含分割 byte 数组的内容 * @param splitByte 分割字节数组 * @return 字节数组 */ public ByteBuffer readWithSplit(byte[] splitByte) { if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } int index = indexOf(splitByte); if(size == 0){ return ByteBuffer.allocate(0); } if(index == 0){ byteBuffer.position(splitByte.length); compact(); index = indexOf(splitByte); } if(index == -1){ index = size; } ByteBuffer resultBuffer = ByteBuffer.allocateDirect(index); int readSize = readHead(resultBuffer); TByteBuffer.release(resultBuffer); //跳过分割符 shrink(splitByte.length*-1); // readHead(ByteBuffer.allocateDirect()); return resultBuffer; } public void saveToFile(String filePath, long length) throws IOException{ if(isReleased()){ throw new MemoryReleasedException("ByteBufferChannel is released."); } int bufferSize = 1024*1024; if(length < bufferSize){ bufferSize = Long.valueOf(length).intValue(); } new File(TFile.getFileFolderPath(filePath)).mkdirs(); RandomAccessFile randomAccessFile = null; File file = new File(filePath); byte[] buffer = new byte[bufferSize]; try { randomAccessFile = new RandomAccessFile(file, "rwd"); //追加形式 randomAccessFile.seek(randomAccessFile.length()); int loadSize = bufferSize; while(length > 0){ loadSize = length > bufferSize ? bufferSize : new Long(length).intValue(); byteBuffer.get(buffer, 0, loadSize); randomAccessFile.write(buffer, 0, loadSize); length = length - loadSize; } compact(); }catch(IOException e){ throw e; }finally { randomAccessFile.close(); } } }