package com.jsoniter.output; import com.jsoniter.spi.JsonException; import com.jsoniter.any.Any; import com.jsoniter.spi.Encoder; import com.jsoniter.spi.TypeLiteral; import java.io.IOException; import java.io.OutputStream; public class JsonStream extends OutputStream { public static int defaultIndentionStep = 0; public int indentionStep = defaultIndentionStep; private int indention = 0; private OutputStream out; byte buf[]; int count; public JsonStream(OutputStream out, int bufSize) { if (bufSize < 32) { throw new JsonException("buffer size must be larger than 32: " + bufSize); } this.out = out; this.buf = new byte[bufSize]; } public void reset(OutputStream out) { this.out = out; this.count = 0; } public final void write(int b) throws IOException { if (count == buf.length) { flushBuffer(); } buf[count++] = (byte) b; } public final void write(byte b1, byte b2) throws IOException { if (count >= buf.length - 1) { flushBuffer(); } buf[count++] = b1; buf[count++] = b2; } public final void write(byte b1, byte b2, byte b3) throws IOException { if (count >= buf.length - 2) { flushBuffer(); } buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; } public final void write(byte b1, byte b2, byte b3, byte b4) throws IOException { if (count >= buf.length - 3) { flushBuffer(); } buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; buf[count++] = b4; } public final void write(byte b1, byte b2, byte b3, byte b4, byte b5) throws IOException { if (count >= buf.length - 4) { flushBuffer(); } buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; buf[count++] = b4; buf[count++] = b5; } public final void write(byte b1, byte b2, byte b3, byte b4, byte b5, byte b6) throws IOException { if (count >= buf.length - 5) { flushBuffer(); } buf[count++] = b1; buf[count++] = b2; buf[count++] = b3; buf[count++] = b4; buf[count++] = b5; buf[count++] = b6; } public final void write(byte b[], int off, int len) throws IOException { if (len >= buf.length - count) { if (len >= buf.length) { /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ flushBuffer(); out.write(b, off, len); return; } flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; } public void flush() throws IOException { flushBuffer(); out.flush(); } @Override public void close() throws IOException { if (count > 0) { flushBuffer(); } out.close(); this.out = null; count = 0; } final void flushBuffer() throws IOException { out.write(buf, 0, count); count = 0; } public final void writeVal(String val) throws IOException { if (val == null) { writeNull(); } else { StreamImplString.writeString(this, val); } } public final void writeRaw(String val) throws IOException { writeRaw(val, val.length()); } public final void writeRaw(String val, int remaining) throws IOException { int i = 0; for (; ; ) { int available = buf.length - count; if (available < remaining) { remaining -= available; int j = i + available; val.getBytes(i, j, buf, count); count = buf.length; flushBuffer(); i = j; } else { int j = i + remaining; val.getBytes(i, j, buf, count); count += remaining; return; } } } public final void writeVal(Boolean val) throws IOException { if (val == null) { writeNull(); } else { if (val) { writeTrue(); } else { writeFalse(); } } } public final void writeVal(boolean val) throws IOException { if (val) { writeTrue(); } else { writeFalse(); } } public final void writeTrue() throws IOException { write((byte) 't', (byte) 'r', (byte) 'u', (byte) 'e'); } public final void writeFalse() throws IOException { write((byte) 'f', (byte) 'a', (byte) 'l', (byte) 's', (byte) 'e'); } public final void writeVal(Short val) throws IOException { if (val == null) { writeNull(); } else { writeVal(val.intValue()); } } public final void writeVal(short val) throws IOException { writeVal((int) val); } public final void writeVal(Integer val) throws IOException { if (val == null) { writeNull(); } else { writeVal(val.intValue()); } } public final void writeVal(int val) throws IOException { StreamImplNumber.writeInt(this, val); } public final void writeVal(Long val) throws IOException { if (val == null) { writeNull(); } else { writeVal(val.longValue()); } } public final void writeVal(long val) throws IOException { StreamImplNumber.writeLong(this, val); } public final void writeVal(Float val) throws IOException { if (val == null) { writeNull(); } else { writeVal(val.floatValue()); } } public final void writeVal(float val) throws IOException { StreamImplNumber.writeFloat(this, val); } public final void writeVal(Double val) throws IOException { if (val == null) { writeNull(); } else { writeVal(val.doubleValue()); } } public final void writeVal(double val) throws IOException { StreamImplNumber.writeDouble(this, val); } public final void writeVal(Any val) throws IOException { val.writeTo(this); } public final void writeNull() throws IOException { write((byte) 'n', (byte) 'u', (byte) 'l', (byte) 'l'); } public final void writeEmptyObject() throws IOException { write((byte) '{', (byte) '}'); } public final void writeEmptyArray() throws IOException { write((byte) '[', (byte) ']'); } public final void writeArrayStart() throws IOException { indention += indentionStep; write('['); writeIndention(); } public final void writeMore() throws IOException { write(','); writeIndention(); } private void writeIndention() throws IOException { writeIndention(0); } private void writeIndention(int delta) throws IOException { if (indention == 0) { return; } write('\n'); int toWrite = indention - delta; int i = 0; for (; ; ) { for (; i < toWrite && count < buf.length; i++) { buf[count++] = ' '; } if (i == toWrite) { break; } else { flushBuffer(); } } } public final void writeArrayEnd() throws IOException { writeIndention(indentionStep); indention -= indentionStep; write(']'); } public final void writeObjectStart() throws IOException { indention += indentionStep; write('{'); writeIndention(); } public final void writeObjectField(String field) throws IOException { writeVal(field); write(':'); } public final void writeObjectEnd() throws IOException { writeIndention(indentionStep); indention -= indentionStep; write('}'); } public final void writeVal(Object obj) throws IOException { if (obj == null) { writeNull(); return; } Class<?> clazz = obj.getClass(); String cacheKey = TypeLiteral.create(clazz).getEncoderCacheKey(); Codegen.getEncoder(cacheKey, clazz).encode(obj, this); } public final <T> void writeVal(TypeLiteral<T> typeLiteral, T obj) throws IOException { if (null == obj) { writeNull(); } else { Codegen.getEncoder(typeLiteral.getEncoderCacheKey(), typeLiteral.getType()).encode(obj, this); } } private final static ThreadLocal<JsonStream> tlsStream = new ThreadLocal<JsonStream>() { @Override protected JsonStream initialValue() { return new JsonStream(null, 4096); } }; public static void serialize(Object obj, OutputStream out) { JsonStream stream = tlsStream.get(); try { try { stream.reset(out); stream.writeVal(obj); } finally { stream.close(); } } catch (IOException e) { throw new JsonException(e); } } private final static ThreadLocal<AsciiOutputStream> tlsAsciiOutputStream = new ThreadLocal<AsciiOutputStream>() { @Override protected AsciiOutputStream initialValue() { return new AsciiOutputStream(); } }; public static String serialize(Object obj) { AsciiOutputStream asciiOutputStream = tlsAsciiOutputStream.get(); asciiOutputStream.reset(); serialize(obj, asciiOutputStream); return asciiOutputStream.toString(); } public static void setMode(EncodingMode mode) { Codegen.setMode(mode); } public static void registerNativeEncoder(Class clazz, Encoder encoder) { CodegenImplNative.NATIVE_ENCODERS.put(clazz, encoder); } }