| /******************************************************************************* |
| * Copyright (c) 2000, 2003 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.cdt.internal.core.index.impl; |
| |
| import java.io.UTFDataFormatException; |
| |
| public class CodeByteStream { |
| protected byte[] bytes; |
| protected int byteOffset= 0; |
| protected int bitOffset= 0; |
| protected int markByteOffset= -1; |
| protected int markBitOffset= -1; |
| |
| public CodeByteStream() { |
| this(16); |
| } |
| public CodeByteStream(byte[] bytes) { |
| this.bytes= bytes; |
| } |
| public CodeByteStream(int initialByteLength) { |
| bytes= new byte[initialByteLength]; |
| } |
| public int byteLength() { |
| return (bitOffset + 7) / 8 + byteOffset; |
| } |
| public byte[] getBytes(int startOffset, int endOffset) { |
| int byteLength= byteLength(); |
| if (startOffset > byteLength || endOffset > byteLength || startOffset > endOffset) |
| throw new IndexOutOfBoundsException(); |
| int length= endOffset - startOffset; |
| byte[] result= new byte[length]; |
| System.arraycopy(bytes, startOffset, result, 0, length); |
| if (endOffset == byteLength && bitOffset != 0) { |
| int mask= (1 << bitOffset) - 1; |
| result[length - 1] &= (mask << 8 - bitOffset); |
| } |
| return result; |
| } |
| protected void grow() { |
| byte[] newBytes= new byte[bytes.length * 2 + 1]; |
| System.arraycopy(bytes, 0, newBytes, 0, bytes.length); |
| bytes= newBytes; |
| } |
| public void mark() { |
| markByteOffset= byteOffset; |
| markBitOffset= bitOffset; |
| } |
| /** |
| * Reads a single bit (value == 0 or == 1). |
| */ |
| public int readBit() { |
| int value= (bytes[byteOffset] >> (7 - bitOffset)) & 1; |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| ++byteOffset; |
| } |
| return value; |
| } |
| /** |
| * Read up to 32 bits from the stream. |
| */ |
| public int readBits(int numBits) { |
| int value= 0; |
| while (numBits > 0) { |
| int bitsToRead= 8 - bitOffset; |
| if (bitsToRead > numBits) |
| bitsToRead= numBits; |
| int mask= (1 << bitsToRead) - 1; |
| value |= ((bytes[byteOffset] >> (8 - bitOffset - bitsToRead)) & mask) << (numBits - bitsToRead); |
| numBits -= bitsToRead; |
| bitOffset += bitsToRead; |
| if (bitOffset >= 8) { |
| bitOffset -= 8; |
| byteOffset += 1; |
| } |
| } |
| return value; |
| } |
| public final int readByte() { |
| |
| // no need to rebuild byte value from bit sequences |
| if (bitOffset == 0) return bytes[byteOffset++] & 255; |
| |
| int value= 0; |
| int numBits = 8; |
| while (numBits > 0) { |
| int bitsToRead= 8 - bitOffset; |
| if (bitsToRead > numBits) |
| bitsToRead= numBits; |
| int mask= (1 << bitsToRead) - 1; |
| value |= ((bytes[byteOffset] >> (8 - bitOffset - bitsToRead)) & mask) << (numBits - bitsToRead); |
| numBits -= bitsToRead; |
| bitOffset += bitsToRead; |
| if (bitOffset >= 8) { |
| bitOffset -= 8; |
| byteOffset += 1; |
| } |
| } |
| return value; |
| } |
| /** |
| * Reads a value using Gamma coding. |
| */ |
| public int readGamma() { |
| int numBits= readUnary(); |
| return readBits(numBits - 1) | (1 << (numBits - 1)); |
| } |
| public char[] readUTF() throws UTFDataFormatException { |
| int utflen= readByte(); |
| if (utflen == 255) { |
| // long UTF |
| int high = readByte(); |
| int low = readByte(); |
| utflen = (high << 8) + low; |
| } |
| char str[]= new char[utflen]; |
| int count= 0; |
| int strlen= 0; |
| while (count < utflen) { |
| int c= readByte(); |
| int char2, char3; |
| switch (c >> 4) { |
| case 0 : |
| case 1 : |
| case 2 : |
| case 3 : |
| case 4 : |
| case 5 : |
| case 6 : |
| case 7 : |
| // 0xxxxxxx |
| count++; |
| str[strlen++]= (char) c; |
| break; |
| case 12 : |
| case 13 : |
| // 110x xxxx 10xx xxxx |
| count += 2; |
| if (count > utflen) |
| throw new UTFDataFormatException(); |
| char2= readByte(); |
| if ((char2 & 0xC0) != 0x80) |
| throw new UTFDataFormatException(); |
| str[strlen++]= (char) (((c & 0x1F) << 6) | (char2 & 0x3F)); |
| break; |
| case 14 : |
| // 1110 xxxx 10xx xxxx 10xx xxxx |
| count += 3; |
| if (count > utflen) |
| throw new UTFDataFormatException(); |
| char2= readByte(); |
| char3= readByte(); |
| if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) |
| throw new UTFDataFormatException(); |
| str[strlen++]= (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0)); |
| break; |
| default : |
| // 10xx xxxx, 1111 xxxx |
| throw new UTFDataFormatException(); |
| } |
| } |
| if (strlen < utflen) |
| System.arraycopy(str, 0, str= new char[strlen], 0, strlen); |
| return str; |
| } |
| /** |
| * Reads a value in unary. |
| */ |
| public int readUnary() { |
| int value= 1; |
| int mask= 1 << (7 - bitOffset); |
| while ((bytes[byteOffset] & mask) != 0) { |
| ++value; |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| ++byteOffset; |
| mask= 0x80; |
| } else { |
| mask >>>= 1; |
| } |
| } |
| // skip the 0 bit |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| ++byteOffset; |
| } |
| return value; |
| } |
| public void reset() { |
| byteOffset= bitOffset= 0; |
| markByteOffset= markBitOffset= -1; |
| } |
| public void reset(byte[] bytes) { |
| this.bytes= bytes; |
| reset(); |
| } |
| public void reset(byte[] bytes, int byteOffset) { |
| reset(bytes); |
| this.byteOffset= byteOffset; |
| } |
| public boolean resetToMark() { |
| if (markByteOffset == -1) |
| return false; |
| byteOffset= markByteOffset; |
| bitOffset= markBitOffset; |
| markByteOffset= markBitOffset= -1; |
| return true; |
| } |
| public void skipBits(int numBits) { |
| int newOffset= byteOffset * 8 + bitOffset + numBits; |
| if (newOffset < 0 || (newOffset + 7) / 8 >= bytes.length) |
| throw new IllegalArgumentException(); |
| byteOffset= newOffset / 8; |
| bitOffset= newOffset % 8; |
| } |
| public byte[] toByteArray() { |
| return getBytes(0, byteLength()); |
| } |
| /** |
| * Writes a single bit (value == 0 or == 1). |
| */ |
| public void writeBit(int value) { |
| bytes[byteOffset] |= (value & 1) << (7 - bitOffset); |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| if (++byteOffset >= bytes.length) |
| grow(); |
| } |
| } |
| /** |
| * Write up to 32 bits to the stream. |
| * The least significant numBits bits of value are written. |
| */ |
| public void writeBits(int value, int numBits) { |
| while (numBits > 0) { |
| int bitsToWrite= 8 - bitOffset; |
| if (bitsToWrite > numBits) |
| bitsToWrite= numBits; |
| int shift= 8 - bitOffset - bitsToWrite; |
| int mask= ((1 << bitsToWrite) - 1) << shift; |
| bytes[byteOffset]= (byte) ((bytes[byteOffset] & ~mask) | (((value >>> (numBits - bitsToWrite)) << shift) & mask)); |
| numBits -= bitsToWrite; |
| bitOffset += bitsToWrite; |
| if (bitOffset >= 8) { |
| bitOffset -= 8; |
| if (++byteOffset >= bytes.length) |
| grow(); |
| } |
| } |
| } |
| public void writeByte(int value) { |
| writeBits(value, 8); |
| } |
| /** |
| * Writes the given value using Gamma coding, in which positive integer x |
| * is represented by coding floor(log2(x) in unary followed by the value |
| * of x - 2**floor(log2(x)) in binary. |
| * The value must be >= 1. |
| */ |
| public void writeGamma(int value) { |
| if (value < 1) |
| throw new IllegalArgumentException(); |
| int temp= value; |
| int numBits= 0; |
| while (temp != 0) { |
| temp >>>= 1; |
| ++numBits; |
| } |
| writeUnary(numBits); |
| writeBits(value, numBits - 1); |
| } |
| public void writeUTF(char[] str, int start, int end) { |
| int utflen= 0; |
| for (int i= start; i < end; i++) { |
| int c= str[i]; |
| if ((c >= 0x0001) && (c <= 0x007F)) { |
| utflen++; |
| } else if (c > 0x07FF) { |
| utflen += 3; |
| } else { |
| utflen += 2; |
| } |
| } |
| if (utflen < 255) { |
| writeByte(utflen & 0xFF); |
| } else if (utflen > 65535) { |
| throw new IllegalArgumentException(); |
| } else { |
| writeByte(255); // marker for long UTF |
| writeByte((utflen >>> 8) & 0xFF); // high byte |
| writeByte((utflen >>> 0) & 0xFF); // low byte |
| } |
| for (int i= start; i < end; i++) { |
| int c= str[i]; |
| if ((c >= 0x0001) && (c <= 0x007F)) { |
| writeByte(c); |
| } else if (c > 0x07FF) { |
| writeByte(0xE0 | ((c >> 12) & 0x0F)); |
| writeByte(0x80 | ((c >> 6) & 0x3F)); |
| writeByte(0x80 | ((c >> 0) & 0x3F)); |
| } else { |
| writeByte(0xC0 | ((c >> 6) & 0x1F)); |
| writeByte(0x80 | ((c >> 0) & 0x3F)); |
| } |
| } |
| } |
| /** |
| * Write the given value in unary. The value must be >= 1. |
| */ |
| public void writeUnary(int value) { |
| if (value < 1) |
| throw new IllegalArgumentException(); |
| int mask= 1 << (7 - bitOffset); |
| // write N-1 1-bits |
| while (--value > 0) { |
| bytes[byteOffset] |= mask; |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| if (++byteOffset >= bytes.length) |
| grow(); |
| mask= 0x80; |
| } else { |
| mask >>>= 1; |
| } |
| } |
| // write a 0-bit |
| bytes[byteOffset] &= ~mask; |
| if (++bitOffset >= 8) { |
| bitOffset= 0; |
| if (++byteOffset >= bytes.length) |
| grow(); |
| } |
| } |
| } |
| |