Files
redkale/src/com/wentch/redkale/convert/json/JsonByteBufferWriter.java
kamhung a1dc7e954d
2015-11-11 11:45:31 +08:00

349 lines
12 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.wentch.redkale.convert.json;
import com.wentch.redkale.util.*;
import java.nio.*;
import java.nio.charset.*;
import java.util.*;
import java.util.function.*;
/**
*
* @author zhangjx
*/
public final class JsonByteBufferWriter extends JsonWriter {
private static final Charset UTF8 = Charset.forName("UTF-8");
private final Charset charset;
private final Supplier<ByteBuffer> supplier;
private ByteBuffer[] buffers;
private int index;
protected JsonByteBufferWriter(Supplier<ByteBuffer> supplier) {
this(null, supplier);
}
protected JsonByteBufferWriter(Charset charset, Supplier<ByteBuffer> supplier) {
this.charset = UTF8.equals(charset) ? null : charset;
this.supplier = supplier;
}
@Override
public JsonByteBufferWriter setTiny(boolean tiny) {
this.tiny = tiny;
return this;
}
@Override
protected boolean recycle() {
this.index = 0;
this.buffers = null;
return false;
}
@Override
public ByteBuffer[] toBuffers() {
if (buffers == null) return new ByteBuffer[0];
for (int i = index; i < this.buffers.length; i++) {
ByteBuffer buf = this.buffers[i];
if (buf.position() != 0) buf.flip();
}
return this.buffers;
}
@Override
public int count() {
if (this.buffers == null) return 0;
int len = 0;
for (ByteBuffer buffer : buffers) {
len += buffer.remaining();
}
return len;
}
private int expand(final int byteLength) {
if (this.buffers == null) {
this.index = 0;
this.buffers = new ByteBuffer[]{supplier.get()};
}
ByteBuffer buffer = this.buffers[index];
if (!buffer.hasRemaining()) {
buffer.flip();
buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
this.index++;
}
int len = buffer.remaining();
int size = 0;
while (len < byteLength) {
buffer = supplier.get();
ByteBuffer[] bufs = new ByteBuffer[this.buffers.length + 1];
System.arraycopy(this.buffers, 0, bufs, 0, this.buffers.length);
bufs[this.buffers.length] = buffer;
this.buffers = bufs;
len += buffer.remaining();
size++;
}
return size;
}
@Override
public void writeTo(final char ch) {
if (ch > Byte.MAX_VALUE) throw new RuntimeException("writeTo char(int.value = " + (int) ch + ") must be less 127");
expand(1);
this.buffers[index].put((byte) ch);
}
@Override
public void writeTo(final char[] chs, final int start, final int len) {
writeTo(-1, false, chs, start, len);
}
private void writeTo(int expandsize, final boolean quote, final char[] chs, final int start, final int len) {
int byteLength = quote ? 2 : 0;
ByteBuffer bb = null;
if (charset == null) {
byteLength += encodeUTF8Length(chs, start, len);
} else {
bb = charset.encode(CharBuffer.wrap(chs, start, len));
byteLength += bb.remaining();
}
if (expandsize < 0) expandsize = expand(byteLength);
if (expandsize == 0) { // 只需要一个buffer
final ByteBuffer buffer = this.buffers[index];
if (quote) buffer.put((byte) '"');
if (charset == null) { //UTF-8
final int limit = start + len;
for (int i = start; i < limit; i++) {
char c = chs[i];
if (c < 0x80) {
buffer.put((byte) c);
} else if (c < 0x800) {
buffer.put((byte) (0xc0 | (c >> 6)));
buffer.put((byte) (0x80 | (c & 0x3f)));
} else {
buffer.put((byte) (0xe0 | ((c >> 12))));
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
buffer.put((byte) (0x80 | (c & 0x3f)));
}
}
} else {
buffer.put(bb);
}
if (quote) buffer.put((byte) '"');
return;
}
ByteBuffer buffer = this.buffers[index];
if (quote) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) '"');
}
if (charset == null) { //UTF-8
final int limit = start + len;
for (int i = start; i < limit; i++) {
buffer = putChar(buffer, chs[i]);
}
} else {
while (bb.hasRemaining()) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put(bb.get());
}
}
if (quote) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) '"');
}
}
private ByteBuffer putChar(ByteBuffer buffer, char c) {
if (c < 0x80) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) c);
} else if (c < 0x800) {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xc0 | (c >> 6)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
} else {
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0xe0 | ((c >> 12))));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
if (!buffer.hasRemaining()) buffer = nextByteBuffer();
buffer.put((byte) (0x80 | (c & 0x3f)));
}
return buffer;
}
private ByteBuffer nextByteBuffer() {
this.buffers[this.index].flip();
return this.buffers[++this.index];
}
private static int encodeUTF8Length(final char[] text, final int start, final int len) {
char c;
int size = 0;
final char[] chars = text;
final int limit = start + len;
for (int i = start; i < limit; i++) {
c = chars[i];
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
}
return size;
}
private static int encodeEscapeUTF8Length(final char[] text, final int start, final int len) {
char c;
int size = 0;
final char[] chars = text;
final int limit = start + len;
for (int i = start; i < limit; i++) {
c = chars[i];
switch (c) {
case '\n': size += 2;
break;
case '\r': size += 2;
break;
case '\t': size += 2;
break;
case '\\': size += 2;
break;
case '"': size += 2;
break;
default:
size += (c < 0x80 ? 1 : (c < 0x800 ? 2 : 3));
break;
}
}
return size;
}
/**
* <b>注意:</b> 该String值不能为null且不会进行转义 只用于不含需要转义字符的字符串例如enum、double、BigInteger转换的String
*
* @param quote
* @param value
*/
@Override
public void writeTo(final boolean quote, final String value) {
char[] chs = Utility.charArray(value);
writeTo(-1, quote, chs, 0, chs.length);
}
@Override
public void writeString(String value) {
if (value == null) {
writeNull();
return;
}
final char[] chs = Utility.charArray(value);
int len = 0;
for (char ch : chs) {
switch (ch) {
case '\n': len += 2;
break;
case '\r': len += 2;
break;
case '\t': len += 2;
break;
case '\\': len += 2;
break;
case '"': len += 2;
break;
default: len++;
break;
}
}
if (len == chs.length) {
writeTo(-1, true, chs, 0, len);
return;
}
int expandsize = -1;
if (this.charset == null) { //UTF-8
final int byteLength = 2 + encodeEscapeUTF8Length(chs, 0, chs.length);
expandsize = expand(byteLength);
if (expandsize == 0) { // 只需要一个buffer
final ByteBuffer buffer = this.buffers[index];
buffer.put((byte) '"');
for (char c : chs) {
switch (c) {
case '\n': buffer.put((byte) '\\').put((byte) 'n');
break;
case '\r': buffer.put((byte) '\\').put((byte) 'r');
break;
case '\t': buffer.put((byte) '\\').put((byte) 't');
break;
case '\\': buffer.put((byte) '\\').put((byte) '\\');
break;
case '"': buffer.put((byte) '\\').put((byte) '"');
break;
default:
if (c < 0x80) {
buffer.put((byte) c);
} else if (c < 0x800) {
buffer.put((byte) (0xc0 | (c >> 6)));
buffer.put((byte) (0x80 | (c & 0x3f)));
} else {
buffer.put((byte) (0xe0 | ((c >> 12))));
buffer.put((byte) (0x80 | ((c >> 6) & 0x3f)));
buffer.put((byte) (0x80 | (c & 0x3f)));
}
break;
}
}
buffer.put((byte) '"');
return;
}
}
StringBuilder sb = new StringBuilder(len);
for (char ch : chs) {
switch (ch) {
case '\n': sb.append("\\n");
break;
case '\r': sb.append("\\r");
break;
case '\t': sb.append("\\t");
break;
case '\\': sb.append("\\\\");
break;
case '"': sb.append("\\\"");
break;
default: sb.append(ch);
break;
}
}
char[] cs = Utility.charArray(sb);
writeTo(expandsize, true, cs, 0, sb.length());
}
@Override
public void writeField(boolean comma, Attribute attribute) {
if (comma) writeTo(',');
writeTo(true, attribute.field());
writeTo(':');
}
@Override
public void writeSmallString(String value) {
writeTo(false, value);
}
@Override
public String toString() {
return Objects.toString(this);
}
}