/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.trevni; import java.io.IOException; import java.io.ByteArrayOutputStream; import java.nio.charset.Charset; import java.nio.ByteBuffer; import java.util.Arrays; /** Used to write values. */ class OutputBuffer extends ByteArrayOutputStream { static final int BLOCK_SIZE = 64 * 1024; private int bitCount; // position in booleans public OutputBuffer() { super(BLOCK_SIZE + BLOCK_SIZE >> 2); } public boolean isFull() { return size() >= BLOCK_SIZE; } public ByteBuffer asByteBuffer() { return ByteBuffer.wrap(buf, 0, count); } public void writeValue(Object value, ValueType type) throws IOException { switch (type) { case NULL: break; case BOOLEAN: writeBoolean((Boolean)value); break; case INT: writeInt((Integer)value); break; case LONG: writeLong((Long)value); break; case FIXED32: writeFixed32((Integer)value); break; case FIXED64: writeFixed64((Long)value); break; case FLOAT: writeFloat((Float)value); break; case DOUBLE: writeDouble((Double)value); break; case STRING: writeString((String)value); break; case BYTES: if (value instanceof ByteBuffer) writeBytes((ByteBuffer)value); else writeBytes((byte[])value); break; default: throw new TrevniRuntimeException("Unknown value type: "+type); } } public void writeBoolean(boolean value) { if (bitCount == 0) { // first bool in byte ensure(1); count++; } if (value) buf[count-1] |= (byte)(1 << bitCount); bitCount++; if (bitCount == 8) bitCount = 0; } public void writeLength(int length) throws IOException { bitCount = 0; writeInt(length); } private static final Charset UTF8 = Charset.forName("UTF-8"); public void writeString(String string) throws IOException { byte[] bytes = string.getBytes(UTF8); writeInt(bytes.length); write(bytes, 0, bytes.length); } public void writeBytes(ByteBuffer bytes) throws IOException { int pos = bytes.position(); int start = bytes.arrayOffset() + pos; int len = bytes.limit() - pos; writeBytes(bytes.array(), start, len); } public void writeBytes(byte[] bytes) throws IOException { writeBytes(bytes, 0, bytes.length); } public void writeBytes(byte[] bytes, int start, int len) throws IOException { writeInt(len); write(bytes, start, len); } public void writeFloat(float f) throws IOException { writeFixed32(Float.floatToRawIntBits(f)); } public void writeDouble(double d) throws IOException { writeFixed64(Double.doubleToRawLongBits(d)); } public void writeFixed32(int i) throws IOException { ensure(4); buf[count ] = (byte)((i ) & 0xFF); buf[count+1] = (byte)((i >>> 8) & 0xFF); buf[count+2] = (byte)((i >>> 16) & 0xFF); buf[count+3] = (byte)((i >>> 24) & 0xFF); count += 4; } public void writeFixed64(long l) throws IOException { ensure(8); int first = (int)(l & 0xFFFFFFFF); int second = (int)((l >>> 32) & 0xFFFFFFFF); buf[count ] = (byte)((first ) & 0xFF); buf[count+4] = (byte)((second ) & 0xFF); buf[count+5] = (byte)((second >>> 8) & 0xFF); buf[count+1] = (byte)((first >>> 8) & 0xFF); buf[count+2] = (byte)((first >>> 16) & 0xFF); buf[count+6] = (byte)((second >>> 16) & 0xFF); buf[count+7] = (byte)((second >>> 24) & 0xFF); buf[count+3] = (byte)((first >>> 24) & 0xFF); count += 8; } public void writeInt(int n) throws IOException { ensure(5); n = (n << 1) ^ (n >> 31); // move sign to low-order bit if ((n & ~0x7F) != 0) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; } } } } buf[count++] = (byte) n; } public void writeLong(long n) throws IOException { ensure(10); n = (n << 1) ^ (n >> 63); // move sign to low-order bit if ((n & ~0x7FL) != 0) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; if (n > 0x7F) { buf[count++] = (byte)((n | 0x80) & 0xFF); n >>>= 7; } } } } } } } } } buf[count++] = (byte) n; } private void ensure(int n) { if (count + n > buf.length) buf = Arrays.copyOf(buf, Math.max(buf.length << 1, count + n)); } public static int size(Object value, ValueType type) { switch (type) { case NULL: return 0; case INT: return size((Integer)value); case LONG: return size((Long)value); case FIXED32: case FLOAT: return 4; case FIXED64: case DOUBLE: return 8; case STRING: return size((String)value); case BYTES: if (value instanceof ByteBuffer) return size((ByteBuffer)value); return size((byte[])value); default: throw new TrevniRuntimeException("Unknown value type: "+type); } } public static int size(int n) { n = (n << 1) ^ (n >> 31); // move sign to low-order bit if (n <= (1<<7*1)-1) return 1; if (n <= (1<<7*2)-1) return 2; if (n <= (1<<7*3)-1) return 3; if (n <= (1<<7*4)-1) return 4; return 5; } public static int size(long n) { n = (n << 1) ^ (n >> 63); // move sign to low-order bit if (n <= (1<<7*1)-1) return 1; if (n <= (1<<7*2)-1) return 2; if (n <= (1<<7*3)-1) return 3; if (n <= (1<<7*4)-1) return 4; if (n <= (1<<7*5)-1) return 5; if (n <= (1<<7*6)-1) return 6; if (n <= (1<<7*7)-1) return 7; if (n <= (1<<7*8)-1) return 8; if (n <= (1<<7*9)-1) return 9; return 10; } public static int size(ByteBuffer bytes) { int length = bytes.remaining(); return size(length) + length; } public static int size(byte[] bytes) { int length = bytes.length; return size(length) + length; } public static int size(String string) { int length = utf8Length(string); return size(length) + length; } private static int utf8Length(String string) { int stringLength = string.length(); int utf8Length = 0; for (int i = 0; i < stringLength; i++) { char c = string.charAt(i); int p = c; // code point if (Character.isHighSurrogate(c) // surrogate pair && i != stringLength-1 && Character.isLowSurrogate(string.charAt(i+1))) { p = string.codePointAt(i); i++; } if (p <= 0x007F) { utf8Length += 1; } else if (p <= 0x07FF) { utf8Length += 2; } else if (p <= 0x0FFFF) { utf8Length += 3; } else if (p <= 0x01FFFFF) { utf8Length += 4; } else if (p <= 0x03FFFFFF) { utf8Length += 5; } else { utf8Length += 6; } } return utf8Length; } }