/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* Licensed 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 erjang;
import java.math.BigInteger;
import erjang.driver.IO;
/**
*
*/
public class EBitStringBuilder {
public static final int PB_IS_WRITABLE = 1;
public static final int PB_ACTIVE_WRITER = 2;
EBitString bs;
int byte_pos;
int extra_bits;
byte[] data;
byte flags;
/**
* @param byte_size
* @param flags
*/
public EBitStringBuilder(int byte_size, int flags) {
this.flags = (byte) flags;
data = new byte[byte_size];
bs = EBitString.make(data, 0, byte_size, 0);
}
public static
EBitStringBuilder bs_init_writable(EObject size) {
int bin_size = 1024;
ESmall sz = size.testSmall();
if (sz != null && sz.value >= 0) {
bin_size = sz.value;
}
EBitStringBuilder res = new EBitStringBuilder(bin_size, PB_IS_WRITABLE|PB_ACTIVE_WRITER);
return res;
}
public EBitStringBuilder(int byte_size, int extra_bits, int flags) {
if (flags != 0) throw new NotImplemented("flags="+flags);
data = new byte[byte_size+(extra_bits>0?1:0)];
bs = EBitString.make(data, 0, byte_size, extra_bits);
}
/** return bitstring under construction */
public EBitString bitstring() {
if ((flags & PB_IS_WRITABLE) == PB_IS_WRITABLE)
bs.byte_size = this.byte_pos;
return bs;
}
public void put_float(EObject value, int bit_size, int flags) {
if (extra_bits != 0)
throw new NotImplemented();
switch (bit_size) {
case 32: {
ENumber en = value.testNumber();
if (en==null) { throw ERT.badarg(value); }
float val = (float)en.doubleValue();
put_int32(Float.floatToIntBits(val), flags);
return;
}
case 64: {
ENumber en = value.testNumber();
if (en==null) { throw ERT.badarg(value); }
double val = en.doubleValue();
put_int64(Double.doubleToLongBits(val), flags);
return;
}
} // switch
throw new NotImplemented("val="+value+";size="+bit_size+";flags="+flags);
}
public void put_integer(EObject value, int flags) {
throw new NotImplemented("val="+value+";flags="+flags);
}
public void put_integer(EObject value, int bit_size, int flags) {
boolean litteEndian = (flags & EBinMatchState.BSF_LITTLE) > 0;
EInteger ei = value.testInteger();
if (ei==null) throw ERT.badarg(value);
if (bit_size == 8 && extra_bits==0) { // Common case optimization
data[byte_pos++] = (byte)ei.intValue();
return;
}
ESmall sm = value.testSmall();
if (extra_bits == 0 && (bit_size % 8) == 0) {
int nBytes = bit_size/8;
// We process the bytes little-endian:
int pos, delta;
if (litteEndian) {
pos = byte_pos;
delta = 1;
} else{
pos = byte_pos + nBytes-1;
delta = -1;
}
byte_pos += nBytes;
if (sm!=null) { // ESmall case
int val = sm.intValue();
while (nBytes-- > 0) {
data[pos] = (byte)val;
pos += delta;
val >>= 8;
}
} else { // Larger integer case
BigInteger big_int = ei.bigintValue();
byte[] bytes = big_int.toByteArray();
int src_pos = bytes.length;
while (--src_pos >= 0 && nBytes-- > 0) {
data[pos] = bytes[src_pos];
pos += delta;
}
if (nBytes > 0) {
byte sign_byte = (byte)(big_int.signum() < 0 ? -1 : 0);
while (nBytes-- > 0) {
data[pos] = sign_byte;
pos += delta;
}
}
}
return;
}
if (bit_size <= 32 && (bit_size % 8) != 0) {
int val = sm.value;
if (litteEndian)
throw new NotImplemented();
int bits_left_in_current_byte = 8-extra_bits;
int msb_bits = Math.min(bits_left_in_current_byte, bit_size);
int lsb_bits = bit_size-msb_bits;
while (lsb_bits + msb_bits > 0) {
int mask = ((1 << msb_bits) - 1);
int putval = ((val >>> lsb_bits) & mask) << (8-msb_bits);
int getval = data[byte_pos];
assert ((putval & getval) == 0);
data[byte_pos] = (byte) (putval | getval);
extra_bits = (extra_bits + msb_bits) % 8;
if (extra_bits == 0) {
byte_pos += 1;
}
lsb_bits -= msb_bits;
msb_bits = Math.min(8, lsb_bits);
}
return;
}
EBig bi = value.testBig();
if (bit_size <= 64 && (bit_size % 8) != 0) {
long val = bi == null
? sm.longValue()
: bi.longValue();
if (litteEndian)
throw new NotImplemented();
int bits_left_in_current_byte =
(extra_bits == 0
? 8
: 8-extra_bits);
int msb_bits = Math.min(bits_left_in_current_byte, bit_size);
int lsb_bits = bit_size-msb_bits;
while(lsb_bits + msb_bits > 0) {
int mask = ((1 << msb_bits) - 1);
int putval = (int) ((val >>> lsb_bits) & mask);
int getval = data[byte_pos] & ~mask;
assert ((putval & getval) == 0);
data[byte_pos] = (byte) (putval | getval);
extra_bits = (extra_bits + msb_bits) % 8;
if (extra_bits == 0) {
byte_pos += 1;
}
lsb_bits -= msb_bits;
msb_bits = Math.min(8, lsb_bits);
};
return;
}
throw new NotImplemented("put_integer value="+ value +"; bit_size="+ bit_size + "; flags=" + flags);
}
protected void put_int64(long val, int flags) {
if ((flags & EBinMatchState.BSF_LITTLE) > 0) {
put_int32_little((int)val);
put_int32_little((int)(val>>32));
} else {
put_int32_big((int)(val>>32));
put_int32_big((int)val);
}
}
protected void put_int32(int val, int flags) {
if ((flags & EBinMatchState.BSF_LITTLE) > 0) {
put_int32_little(val);
} else {
put_int32_big(val);
}
}
protected void put_int32_little(int val) {
byte b1, b2, b3, b4;
b1 = (byte)val; val >>= 8;
b2 = (byte)val; val >>= 8;
b3 = (byte)val; val >>= 8;
b4 = (byte)val;
put_byte(b1);
put_byte(b2);
put_byte(b3);
put_byte(b4);
}
protected void put_int32_big(int val) {
byte b1, b2, b3, b4;
b4 = (byte)val; val >>= 8;
b3 = (byte)val; val >>= 8;
b2 = (byte)val; val >>= 8;
b1 = (byte)val;
put_byte(b1);
put_byte(b2);
put_byte(b3);
put_byte(b4);
}
protected void put_int16(int val, int flags) {
if ((flags & EBinMatchState.BSF_LITTLE) > 0) {
put_int16_little(val);
} else {
put_int16_big(val);
}
}
protected void put_int16_little(int val) {
byte b1, b2;
b1 = (byte)val; val >>= 8;
b2 = (byte)val;
put_byte(b1);
put_byte(b2);
}
protected void put_int16_big(int val) {
byte b1, b2;
b2 = (byte)val; val >>= 8;
b1 = (byte)val;
put_byte(b1);
put_byte(b2);
}
private void put_byte(byte val) {
if (extra_bits == 0) {
data[byte_pos++] = val;
return;
} else {
// | bits1 : bits2 |
int bits1 = extra_bits;
int bits2 = 8-bits1;
data[byte_pos] |= (byte)((0xff & val) >> bits1);
data[byte_pos+1] = (byte) ((val & ((1<<bits1)-1)) << bits2);
byte_pos += 1;
}
}
public void put_string(EString str) {
if (extra_bits != 0)
{
for (int i = 0; i < str.length(); i++) {
put_byte(str.data[str.off+i]);
}
return;
}
System.arraycopy(str.data, str.off, data, byte_pos, str.length());
byte_pos += str.length();
}
/** grow a bitstring by extra_size bits, and return a string builder with position at end of original bitstring */
public static EBitStringBuilder bs_private_append(EObject str_or_builder, int extra_size, int unit, int flags)
{
EBitString ebs = str_or_builder.testBitString();
if (ebs == null) throw new NotImplemented();
long bitSize = ebs.bitSize() + extra_size;
int size = (int) (bitSize/8);
int extra = (int) (bitSize % 8);
EBitStringBuilder result = new EBitStringBuilder(size, extra, flags);
System.arraycopy(ebs.data, ebs.byteOffset(), result.data, 0, ebs.dataByteSize());
result.byte_pos = ebs.byteSize();
result.extra_bits = ebs.extra_bits;
return result;
}
/** grow a bitstring by extra_size bits, and return a string builder with position at end of original bitstring */
public static EBitStringBuilder bs_append(EObject str_or_builder, int extra_size, int unit, int flags)
{
EBitString ebs = str_or_builder.testBitString();
if (ebs == null) throw new NotImplemented();
long bitSize = ebs.bitSize() + extra_size;
int size = (int) (bitSize/8);
int extra = (int) (bitSize % 8);
EBitStringBuilder result = new EBitStringBuilder(size, extra, flags);
System.arraycopy(ebs.data, ebs.byteOffset(), result.data, 0, ebs.dataByteSize());
result.byte_pos = ebs.byteSize();
result.extra_bits = ebs.extra_bits;
return result;
}
/** Append a bit string.
* @param size The output size in bits; -1 means the entire bitstring.
*/
public void put_bitstring(EObject str, int size, int flags) {
EBitString ebs = str.testBitString();
if (ebs == null) throw new InternalError("bad code gen, arg is "+str.getClass());
if (extra_bits != 0)
throw new NotImplemented();
if (size != -1 && size != ebs.bitSize()) {
throw new NotImplemented();
}
System.arraycopy(ebs.data, ebs.byteOffset(), data, byte_pos, ebs.totalByteSize());
byte_pos += ebs.byteSize();
extra_bits += ebs.extra_bits; // TODO on extension
}
public void put_utf8(EObject value, int flags) {
ESmall sm;
if ((sm=value.testSmall()) != null && sm.value >= 0 && sm.value < 0x110000) {
if (sm.value < 0x80) {
put_byte((byte) sm.value);
return;
}
if (sm.value < 0x0800) {
put_byte((byte) (0xc0 | ((sm.value >> 6) & 0x1f)));
put_byte((byte) (0x80 | (sm.value & 0x3f)));
return;
}
if (sm.value < 0x10000) {
put_byte((byte) (0xe0 | ((sm.value >> 12) & 0x0f)));
put_byte((byte) (0x80 | ((sm.value >> 6) & 0x3f)));
put_byte((byte) (0x80 | (sm.value & 0x3f)));
return;
}
if (sm.value < 0x110000) {
put_byte((byte) (0xf0 | ((sm.value >> 18) & 0x7)));
put_byte((byte) (0x80 | ((sm.value >> 12) & 0x3f)));
put_byte((byte) (0x80 | ((sm.value >> 6) & 0x3f)));
put_byte((byte) (0x80 | (sm.value & 0x3f)));
return;
}
}
throw ERT.badarg(value);
}
public void put_utf16(EObject value, int flags) {
ESmall num = value.testSmall();
if (num == null
|| num.value < 0
|| num.value > 0x10FFFF
|| (0xD800 <= num.value && num.value <= 0xDFFF)) {
throw ERT.badarg(value);
}
if (num.value < 0x10000) {
put_int16(num.value, flags);
} else {
int low = num.value - 0x10000;
int num1 = 0xD800 | ((low >> 10) & 0x3ff);
int num2 = 0xDC00 | (low & 0x3ff);
put_int16(num1, flags);
put_int16(num2, flags);
}
}
public void put_utf32(EObject value, int flags) {
ESmall num = value.testSmall();
if (num == null || !Character.isDefined(num.value))
throw ERT.badarg(value); // TODO: throw what?
String val = new String(new char[] { (char) num.value });
byte[] bytes = val.getBytes(IO.UTF32);
for (int i = 0; i < bytes.length; i++) {
put_byte(bytes[i]);
}
}
// compute size of utf8 char
static public ESmall bs_utf8_size(EObject value) {
ESmall sm;
if ((sm=value.testSmall()) != null) {
if (sm.value < 0) return null;
if (sm.value < 0x80) return ERT.box(1);
if (sm.value < 0x800) return ERT.box(2);
if (sm.value < 0x10000) return ERT.box(3);
if (sm.value < 0x200000) return ERT.box(4);
if (sm.value < 0x4000000) return ERT.box(5);
return ERT.box(6);
}
return null;
}
// compute size of utf16 char
static public ESmall bs_utf16_size(EObject value) {
ESmall num = value.testSmall();
if (num == null || num.value < 0)
return null;
if (num.value < 0x10000) return ERT.box(2);
return ERT.box(4);
}
}