blob: 0bba8ac179604c545109854d8ee56298883b54b6 [file] [log] [blame]
/**
* 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;
}
}