/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.oracle.max.vm.ext.maxri;
import java.io.*;
import java.util.*;
/**
* Combination of {@link ByteArrayOutputStream} and {@link DataOutputStream} that supports
* direct access to the underlying buffer and write position.
*/
final class EncodingStream {
byte[] buf;
int pos;
public EncodingStream(int capacity) {
buf = new byte[capacity];
}
private void ensureCapacity(int capacity) {
if (capacity > buf.length) {
buf = Arrays.copyOf(buf, Math.max(buf.length << 1, capacity));
}
}
/**
* Updates the write position of this stream. The stream can only be repositioned between {@code [0 .. this.buf.length]}.
*
* @param zeroAhead specifies if all the data at indexes greater or equal to {@code pos} should be zeroed
*/
public void seek(int pos, boolean zeroAhead) {
assert pos >= 0 && pos <= buf.length;
this.pos = pos;
if (zeroAhead) {
Arrays.fill(buf, pos, buf.length, (byte) 0);
}
}
/**
* Skips the write position of this stream forward by {@code amount} bytes.
*/
void skip(int amount) {
ensureCapacity(pos + amount);
pos += amount;
}
public void write(int b) {
ensureCapacity(pos + 1);
buf[pos++] = (byte) b;
}
public void writeByte(int v) {
write(v);
}
public void writeBoolean(boolean v) {
write(v ? 1 : 0);
}
public void writeShort(int v) {
ensureCapacity(pos + 2);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
buf[pos++] = (byte) (v >>> 8);
buf[pos++] = (byte) (v >>> 0);
}
public void writeChar(int v) {
writeShort(v);
}
public void writeInt(int v) {
ensureCapacity(pos + 4);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
assert buf[pos + 2] == 0;
assert buf[pos + 3] == 0;
buf[pos++] = (byte) (v >>> 24);
buf[pos++] = (byte) (v >>> 16);
buf[pos++] = (byte) (v >>> 8);
buf[pos++] = (byte) (v >>> 0);
}
public void writeFloat(float v) {
writeInt(Float.floatToRawIntBits(v));
}
public void writeLong(long v) {
ensureCapacity(pos + 8);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
assert buf[pos + 2] == 0;
assert buf[pos + 3] == 0;
assert buf[pos + 4] == 0;
assert buf[pos + 5] == 0;
assert buf[pos + 6] == 0;
assert buf[pos + 7] == 0;
buf[pos++] = (byte) (v >>> 56);
buf[pos++] = (byte) (v >>> 48);
buf[pos++] = (byte) (v >>> 40);
buf[pos++] = (byte) (v >>> 32);
buf[pos++] = (byte) (v >>> 24);
buf[pos++] = (byte) (v >>> 16);
buf[pos++] = (byte) (v >>> 8);
buf[pos++] = (byte) (v >>> 0);
}
public void writeDouble(double v) {
writeLong(Double.doubleToRawLongBits(v));
}
/**
* Encodes an unsigned integer value to this stream.
* The number of bytes written to the stream is given below:
* <pre>
* Value range Bytes used for encoding
* 0 .. 127 1
* 128 .. 16383 2
* 16384 .. 2097151 3
* 2097152 .. 268435455 4
* </pre>
*
* @param value the value to encode (must be between 0 and 0x0FFFFFFF)
*/
void encodeUInt(int value) {
if (value < 128) {
assert value >= 0;
/* 0xxxxxxx */
write(value);
} else if (value < 16384) {
/* 1xxxxxxx 0xxxxxxx */
ensureCapacity(pos + 2);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
buf[pos++] = (byte) (((value >> 0) & 0x7F) | 0x80);
buf[pos++] = (byte) (value >> 7);
} else if (value < 2097152) {
/* 1xxxxxxx 1xxxxxxx 0xxxxxxx */
ensureCapacity(pos + 3);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
assert buf[pos + 2] == 0;
buf[pos++] = (byte) (((value >> 0) & 0x7F) | 0x80);
buf[pos++] = (byte) (((value >> 7) & 0x7F) | 0x80);
buf[pos++] = (byte) (value >> 14);
} else {
assert value < 0x0FFFFFFF;
/* 1xxxxxxx 1xxxxxxx 1xxxxxxx 0xxxxxxx */
ensureCapacity(pos + 4);
assert buf[pos] == 0;
assert buf[pos + 1] == 0;
assert buf[pos + 2] == 0;
assert buf[pos + 3] == 0;
buf[pos++] = (byte) (((value >> 0) & 0x7F) | 0x80);
buf[pos++] = (byte) (((value >> 7) & 0x7F) | 0x80);
buf[pos++] = (byte) (((value >> 14) & 0x7F) | 0x80);
buf[pos++] = (byte) (value >> 21);
}
}
byte toByteArray()[] {
return Arrays.copyOf(buf, pos);
}
}