mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add FastCharBuffer
This commit is contained in:
parent
9dfcac028e
commit
5f45b2275e
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* 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 org.dromara.hutool.core.io.buffer;
|
||||
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
|
||||
/**
|
||||
* 快速缓冲抽象类,用于快速读取、写入数据到缓冲区,减少内存复制<br>
|
||||
* 相对于普通Buffer,使用二维数组扩展长度,减少内存复制,提升性能
|
||||
*
|
||||
* @author Looly
|
||||
*/
|
||||
public abstract class FastBuffer {
|
||||
|
||||
/**
|
||||
* 一个缓冲区的最小字节数
|
||||
*/
|
||||
protected final int minChunkLen;
|
||||
|
||||
/**
|
||||
* 缓冲数
|
||||
*/
|
||||
protected int buffersCount;
|
||||
/**
|
||||
* 当前缓冲索引
|
||||
*/
|
||||
protected int currentBufferIndex = -1;
|
||||
/**
|
||||
* 当前缓冲偏移量
|
||||
*/
|
||||
protected int offset;
|
||||
/**
|
||||
* 缓冲字节数
|
||||
*/
|
||||
protected int size;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param size 一个缓冲区的最小字节数
|
||||
*/
|
||||
public FastBuffer(int size) {
|
||||
if (size <= 0) {
|
||||
size = IoUtil.DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
this.minChunkLen = Math.abs(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前缓冲位于缓冲区的索引位
|
||||
*
|
||||
* @return {@link #currentBufferIndex}
|
||||
*/
|
||||
public int index() {
|
||||
return currentBufferIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前缓冲偏移量
|
||||
*
|
||||
* @return 当前缓冲偏移量
|
||||
*/
|
||||
public int offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 复位缓冲
|
||||
*/
|
||||
public void reset() {
|
||||
size = 0;
|
||||
offset = 0;
|
||||
currentBufferIndex = -1;
|
||||
buffersCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓冲总长度
|
||||
*
|
||||
* @return 缓冲总长度
|
||||
*/
|
||||
public int length() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
*
|
||||
* @return 是否为空
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配下一个缓冲区,不会小于1024
|
||||
*
|
||||
* @param newSize 理想缓冲区字节数
|
||||
*/
|
||||
abstract protected void needNewBuffer(final int newSize);
|
||||
}
|
@ -26,37 +26,16 @@ import org.dromara.hutool.core.lang.Assert;
|
||||
* @author biezhi, looly
|
||||
* @since 1.0
|
||||
*/
|
||||
public class FastByteBuffer {
|
||||
public class FastByteBuffer extends FastBuffer {
|
||||
|
||||
/**
|
||||
* 缓冲集
|
||||
*/
|
||||
private byte[][] buffers = new byte[16][];
|
||||
/**
|
||||
* 缓冲数
|
||||
*/
|
||||
private int buffersCount;
|
||||
/**
|
||||
* 当前缓冲索引
|
||||
*/
|
||||
private int currentBufferIndex = -1;
|
||||
/**
|
||||
* 当前缓冲
|
||||
*/
|
||||
private byte[] currentBuffer;
|
||||
/**
|
||||
* 当前缓冲偏移量
|
||||
*/
|
||||
private int offset;
|
||||
/**
|
||||
* 缓冲字节数
|
||||
*/
|
||||
private int size;
|
||||
|
||||
/**
|
||||
* 一个缓冲区的最小字节数
|
||||
*/
|
||||
private final int minChunkLen;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@ -70,35 +49,8 @@ public class FastByteBuffer {
|
||||
*
|
||||
* @param size 一个缓冲区的最小字节数
|
||||
*/
|
||||
public FastByteBuffer(int size) {
|
||||
if (size <= 0) {
|
||||
size = IoUtil.DEFAULT_BUFFER_SIZE;
|
||||
}
|
||||
this.minChunkLen = Math.abs(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 分配下一个缓冲区,不会小于1024
|
||||
*
|
||||
* @param newSize 理想缓冲区字节数
|
||||
*/
|
||||
private void needNewBuffer(final int newSize) {
|
||||
final int delta = newSize - size;
|
||||
final int newBufferSize = Math.max(minChunkLen, delta);
|
||||
|
||||
currentBufferIndex++;
|
||||
currentBuffer = new byte[newBufferSize];
|
||||
offset = 0;
|
||||
|
||||
// add buffer
|
||||
if (currentBufferIndex >= buffers.length) {
|
||||
final int newLen = buffers.length << 1;
|
||||
final byte[][] newBuffers = new byte[newLen][];
|
||||
System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
|
||||
buffers = newBuffers;
|
||||
}
|
||||
buffers[currentBufferIndex] = currentBuffer;
|
||||
buffersCount++;
|
||||
public FastByteBuffer(final int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -190,42 +142,6 @@ public class FastByteBuffer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 长度
|
||||
*
|
||||
* @return 长度
|
||||
*/
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
*
|
||||
* @return 是否为空
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前缓冲位于缓冲区的索引位
|
||||
*
|
||||
* @return {@link #currentBufferIndex}
|
||||
*/
|
||||
public int index() {
|
||||
return currentBufferIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前缓冲偏移量
|
||||
*
|
||||
* @return 当前缓冲偏移量
|
||||
*/
|
||||
public int offset() {
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引位返回缓冲集中的缓冲
|
||||
*
|
||||
@ -236,15 +152,10 @@ public class FastByteBuffer {
|
||||
return buffers[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* 复位缓冲
|
||||
*/
|
||||
@Override
|
||||
public void reset() {
|
||||
size = 0;
|
||||
offset = 0;
|
||||
currentBufferIndex = -1;
|
||||
super.reset();
|
||||
currentBuffer = null;
|
||||
buffersCount = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -254,9 +165,9 @@ public class FastByteBuffer {
|
||||
* @return 快速缓冲中的数据
|
||||
*/
|
||||
public byte[] toArrayZeroCopyIfPossible() {
|
||||
if(1 == currentBufferIndex){
|
||||
if (1 == currentBufferIndex) {
|
||||
final int len = buffers[0].length;
|
||||
if(len == size){
|
||||
if (len == size) {
|
||||
return buffers[0];
|
||||
}
|
||||
}
|
||||
@ -284,10 +195,10 @@ public class FastByteBuffer {
|
||||
Assert.isTrue(start >= 0, "Start must be greater than zero!");
|
||||
Assert.isTrue(len >= 0, "Length must be greater than zero!");
|
||||
|
||||
if(start >= this.size || len == 0){
|
||||
if (start >= this.size || len == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
if(len > (this.size - start)){
|
||||
if (len > (this.size - start)) {
|
||||
len = this.size - start;
|
||||
}
|
||||
int remaining = len;
|
||||
@ -301,8 +212,9 @@ public class FastByteBuffer {
|
||||
}
|
||||
|
||||
while (i < buffersCount) {
|
||||
final int bufLen = Math.min(buffers[i].length - start, remaining);
|
||||
System.arraycopy(buffers[i], start, result, pos, bufLen);
|
||||
final byte[] buf = buffers[i];
|
||||
final int bufLen = Math.min(buf.length - start, remaining);
|
||||
System.arraycopy(buf, start, result, pos, bufLen);
|
||||
pos += bufLen;
|
||||
remaining -= bufLen;
|
||||
if (remaining == 0) {
|
||||
@ -335,4 +247,23 @@ public class FastByteBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void needNewBuffer(final int newSize) {
|
||||
final int delta = newSize - size;
|
||||
final int newBufferSize = Math.max(minChunkLen, delta);
|
||||
|
||||
currentBufferIndex++;
|
||||
currentBuffer = new byte[newBufferSize];
|
||||
offset = 0;
|
||||
|
||||
// add buffer
|
||||
if (currentBufferIndex >= buffers.length) {
|
||||
final int newLen = buffers.length << 1;
|
||||
final byte[][] newBuffers = new byte[newLen][];
|
||||
System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
|
||||
buffers = newBuffers;
|
||||
}
|
||||
buffers[currentBufferIndex] = currentBuffer;
|
||||
buffersCount++;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2024 Hutool Team and hutool.cn
|
||||
*
|
||||
* 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 org.dromara.hutool.core.io.buffer;
|
||||
|
||||
import org.dromara.hutool.core.io.IoUtil;
|
||||
import org.dromara.hutool.core.lang.Assert;
|
||||
|
||||
/**
|
||||
* 代码移植自jetbrick<br>
|
||||
* 快速字符缓冲,将数据存放在缓冲集中,取代以往的单一数组
|
||||
*
|
||||
* @author jetbrick, looly
|
||||
*/
|
||||
public class FastCharBuffer extends FastBuffer implements CharSequence, Appendable {
|
||||
|
||||
/**
|
||||
* 缓冲集
|
||||
*/
|
||||
private char[][] buffers = new char[16][];
|
||||
/**
|
||||
* 当前缓冲
|
||||
*/
|
||||
private char[] currentBuffer;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public FastCharBuffer() {
|
||||
this(IoUtil.DEFAULT_BUFFER_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param size 一个缓冲区的最小字节数
|
||||
*/
|
||||
public FastCharBuffer(final int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向快速缓冲加入数据
|
||||
*
|
||||
* @param array 数据
|
||||
* @param off 偏移量
|
||||
* @param len 字节数
|
||||
* @return 快速缓冲自身 @see FastByteBuffer
|
||||
*/
|
||||
public FastCharBuffer append(final char[] array, final int off, final int len) {
|
||||
final int end = off + len;
|
||||
if ((off < 0) || (len < 0) || (end > array.length)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
if (len == 0) {
|
||||
return this;
|
||||
}
|
||||
final int newSize = size + len;
|
||||
int remaining = len;
|
||||
|
||||
if (currentBuffer != null) {
|
||||
// first try to fill current buffer
|
||||
final int part = Math.min(remaining, currentBuffer.length - offset);
|
||||
System.arraycopy(array, end - remaining, currentBuffer, offset, part);
|
||||
remaining -= part;
|
||||
offset += part;
|
||||
size += part;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
// still some data left
|
||||
// ask for new buffer
|
||||
needNewBuffer(newSize);
|
||||
|
||||
// then copy remaining
|
||||
// but this time we are sure that it will fit
|
||||
final int part = Math.min(remaining, currentBuffer.length - offset);
|
||||
System.arraycopy(array, end - remaining, currentBuffer, offset, part);
|
||||
offset += part;
|
||||
size += part;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向快速缓冲加入数据
|
||||
*
|
||||
* @param array 数据
|
||||
* @return 快速缓冲自身 @see FastByteBuffer
|
||||
*/
|
||||
public FastCharBuffer append(final char[] array) {
|
||||
return append(array, 0, array.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* 向快速缓冲加入一个字节
|
||||
*
|
||||
* @param element 一个字节的数据
|
||||
* @return 快速缓冲自身 @see FastByteBuffer
|
||||
*/
|
||||
public FastCharBuffer append(final char element) {
|
||||
if ((currentBuffer == null) || (offset == currentBuffer.length)) {
|
||||
needNewBuffer(size + 1);
|
||||
}
|
||||
|
||||
currentBuffer[offset] = element;
|
||||
offset++;
|
||||
size++;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将另一个快速缓冲加入到自身
|
||||
*
|
||||
* @param buff 快速缓冲
|
||||
* @return 快速缓冲自身 @see FastByteBuffer
|
||||
*/
|
||||
public FastCharBuffer append(final FastCharBuffer buff) {
|
||||
if (buff.size == 0) {
|
||||
return this;
|
||||
}
|
||||
for (int i = 0; i < buff.currentBufferIndex; i++) {
|
||||
append(buff.buffers[i]);
|
||||
}
|
||||
append(buff.currentBuffer, 0, buff.offset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引位返回缓冲集中的缓冲
|
||||
*
|
||||
* @param index 索引位
|
||||
* @return 缓冲
|
||||
*/
|
||||
public char[] array(final int index) {
|
||||
return buffers[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
currentBuffer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回快速缓冲中的数据,如果缓冲区中的数据长度固定,则直接返回原始数组<br>
|
||||
* 注意此方法共享数组,不能修改数组内容!
|
||||
*
|
||||
* @return 快速缓冲中的数据
|
||||
*/
|
||||
public char[] toArrayZeroCopyIfPossible() {
|
||||
if (1 == currentBufferIndex) {
|
||||
final int len = buffers[0].length;
|
||||
if (len == size) {
|
||||
return buffers[0];
|
||||
}
|
||||
}
|
||||
|
||||
return toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回快速缓冲中的数据
|
||||
*
|
||||
* @return 快速缓冲中的数据
|
||||
*/
|
||||
public char[] toArray() {
|
||||
return toArray(0, this.size);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回快速缓冲中的数据
|
||||
*
|
||||
* @param start 逻辑起始位置
|
||||
* @param len 逻辑字节长
|
||||
* @return 快速缓冲中的数据
|
||||
*/
|
||||
public char[] toArray(int start, int len) {
|
||||
Assert.isTrue(start >= 0, "Start must be greater than zero!");
|
||||
Assert.isTrue(len >= 0, "Length must be greater than zero!");
|
||||
|
||||
if (start >= this.size || len == 0) {
|
||||
return new char[0];
|
||||
}
|
||||
if (len > (this.size - start)) {
|
||||
len = this.size - start;
|
||||
}
|
||||
int remaining = len;
|
||||
int pos = 0;
|
||||
final char[] result = new char[len];
|
||||
|
||||
int i = 0;
|
||||
while (start >= buffers[i].length) {
|
||||
start -= buffers[i].length;
|
||||
i++;
|
||||
}
|
||||
|
||||
while (i < buffersCount) {
|
||||
final char[] buf = buffers[i];
|
||||
final int bufLen = Math.min(buf.length - start, remaining);
|
||||
System.arraycopy(buf, start, result, pos, bufLen);
|
||||
pos += bufLen;
|
||||
remaining -= bufLen;
|
||||
if (remaining == 0) {
|
||||
break;
|
||||
}
|
||||
start = 0;
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引位返回一个字节
|
||||
*
|
||||
* @param index 索引位
|
||||
* @return 一个字节
|
||||
*/
|
||||
public char get(int index) {
|
||||
if ((index >= size) || (index < 0)) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
int ndx = 0;
|
||||
while (true) {
|
||||
final char[] b = buffers[ndx];
|
||||
if (index < b.length) {
|
||||
return b[index];
|
||||
}
|
||||
ndx++;
|
||||
index -= b.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new String(toArray());
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(final int index) {
|
||||
return get(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(final int start, final int end) {
|
||||
final int len = end - start;
|
||||
return new StringBuilder(len).append(toArray(start, len));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FastCharBuffer append(final CharSequence csq) {
|
||||
if(csq instanceof String){
|
||||
return append((String)csq);
|
||||
}
|
||||
return append(csq, 0, csq.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends character sequence to buffer.
|
||||
*/
|
||||
@Override
|
||||
public FastCharBuffer append(final CharSequence csq, final int start, final int end) {
|
||||
for (int i = start; i < end; i++) {
|
||||
append(csq.charAt(i));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 追加字符串
|
||||
*
|
||||
* @param string String
|
||||
* @return this
|
||||
*/
|
||||
public FastCharBuffer append(final String string) {
|
||||
final int len = string.length();
|
||||
if (len == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
final int newSize = size + len;
|
||||
int remaining = len;
|
||||
int start = 0;
|
||||
|
||||
if (currentBuffer != null) {
|
||||
// first try to fill current buffer
|
||||
final int part = Math.min(remaining, currentBuffer.length - offset);
|
||||
string.getChars(0, part, currentBuffer, offset);
|
||||
remaining -= part;
|
||||
offset += part;
|
||||
size += part;
|
||||
start += part;
|
||||
}
|
||||
|
||||
if (remaining > 0) {
|
||||
// still some data left
|
||||
// ask for new buffer
|
||||
needNewBuffer(newSize);
|
||||
|
||||
// then copy remaining
|
||||
// but this time we are sure that it will fit
|
||||
final int part = Math.min(remaining, currentBuffer.length - offset);
|
||||
string.getChars(start, start + part, currentBuffer, offset);
|
||||
offset += part;
|
||||
size += part;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void needNewBuffer(final int newSize) {
|
||||
final int delta = newSize - size;
|
||||
final int newBufferSize = Math.max(minChunkLen, delta);
|
||||
|
||||
currentBufferIndex++;
|
||||
currentBuffer = new char[newBufferSize];
|
||||
offset = 0;
|
||||
|
||||
// add buffer
|
||||
if (currentBufferIndex >= buffers.length) {
|
||||
final int newLen = buffers.length << 1;
|
||||
final char[][] newBuffers = new char[newLen][];
|
||||
System.arraycopy(buffers, 0, newBuffers, 0, buffers.length);
|
||||
buffers = newBuffers;
|
||||
}
|
||||
buffers[currentBufferIndex] = currentBuffer;
|
||||
buffersCount++;
|
||||
}
|
||||
|
||||
}
|
@ -93,7 +93,7 @@ public class FastByteArrayOutputStream extends OutputStream {
|
||||
* @return 长度
|
||||
*/
|
||||
public int size() {
|
||||
return buffer.size();
|
||||
return buffer.length();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,7 +128,7 @@ public class LineInputStream extends FilterInputStream implements Iterable<byte[
|
||||
} else if (CharUtil.LF == c) {
|
||||
// 非转义状态下,表示行的结束
|
||||
// 如果换行符是`\r\n`,删除末尾的`\r`
|
||||
final int lastIndex = out.size() - 1;
|
||||
final int lastIndex = out.length() - 1;
|
||||
if (lastIndex >= 0 && CharUtil.CR == out.get(lastIndex)) {
|
||||
return out.toArray(0, lastIndex);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)//每次执行平均花费时间
|
||||
@Warmup(iterations = 1, time = 1) //预热5次调用
|
||||
@Measurement(iterations = 1, time = 5, timeUnit = TimeUnit.SECONDS) // 执行5此,每次1秒
|
||||
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 执行5此,每次1秒
|
||||
@Threads(1) //单线程
|
||||
@Fork(1) //
|
||||
@OutputTimeUnit(TimeUnit.NANOSECONDS) // 单位:纳秒
|
||||
@ -38,6 +38,12 @@ public class JsonToStringJmh {
|
||||
Assertions.assertNotNull(jsonStr);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void gsonAppendJmh() {
|
||||
final String jsonStr = gson.toString();
|
||||
Assertions.assertNotNull(jsonStr);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void hutoolJmh() {
|
||||
final String jsonStr = hutoolJSON.toString();
|
||||
|
Loading…
x
Reference in New Issue
Block a user