/* * This file is part of the Tuning Fork Visualization Platform * (http://sourceforge.net/projects/tuningforkvp) * * Copyright (c) 2005 - 2008 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation */ package com.ibm.tuningfork.tracegen.chunk; import java.io.IOException; import java.io.OutputStream; import org.jikesrvm.VM; import org.jikesrvm.runtime.Magic; import org.vmmagic.pragma.Inline; import org.vmmagic.pragma.Interruptible; import org.vmmagic.pragma.Uninterruptible; /* * There are 3 basic operations: (1) Closing a chunk which involves fixing up * data that cannot be determined initially such as the length field. Subclasses * should override this at each level and call the superclass's close(). The * base implementation will limit and reset the buffer position so overrides * need to put the position at the end of data. (2) Writing the data out. This * method is not overrideable as it simply means to write out the contents of * the buffer. The cursor position will be reset to zero. The entire chunk is * written out and the position is undefined on return. (3) Reset which allows * the buffer object to be reused. Subclasses that can actually be reset should * define a reset method and use the resetImpl method here. */ @Uninterruptible public abstract class RawChunk { public final static int ENCODING_SPACE_INT = 4; public final static int ENCODING_SPACE_LONG = 8; public final static int ENCODING_SPACE_DOUBLE = 8; private final byte[] data; private int cursor = 0; private boolean open = true; protected RawChunk(byte[] buffer) { data = buffer; } protected RawChunk(int capacity) { this(new byte[capacity]); } public void close() { if (!open) { VM.sysWriteln("RawChunk: Cannot close a closed chunk."); } open = false; } /* Synchronous */ @Interruptible public final void write(OutputStream outputStream) throws IOException { outputStream.write(data, 0, cursor); } protected void resetImpl() { cursor = 0; open = true; } protected int getPosition() { return cursor; } protected void seek(int pos) { cursor = pos; } protected final boolean hasRoom(int bytes) { int remaining = data.length - cursor; return remaining >= bytes; } protected final boolean addLong(long l) { if (!hasRoom(ENCODING_SPACE_LONG)) { return false; } putLong(l); return true; } protected final void addLongUnchecked(long l) { putLong(l); } protected final boolean addDouble(double d) { if (!hasRoom(ENCODING_SPACE_DOUBLE)) { return false; } putLong(Magic.doubleAsLongBits(d)); return true; } protected final void addDoubleUnchecked(double d) { putLong(Magic.doubleAsLongBits(d)); } protected final boolean addInt(int i) { if (!hasRoom(ENCODING_SPACE_INT)) { return false; } putInt(i); return true; } protected final void addIntUnchecked(int i) { putInt(i); } protected final boolean addByte(byte b) { if (!hasRoom(1)) { return false; } data[cursor++] = b; return true; } protected final boolean addString(String str) { int strLen = JikesRVMSupport.getStringLength(str); int minimalSize = ENCODING_SPACE_INT + strLen; if (!hasRoom(minimalSize)) { return false; } char[] backingChars = JikesRVMSupport.getBackingCharArray(str); return addStringInternal(backingChars); } /* * Write String's char[] encoded as UTF-8. * Table from http://tools.ietf.org/html/rfc3629 * * Char. number range | UTF-8 octet sequence * (hexadecimal) | (binary) * --------------------+--------------------------------------------- * 0000 0000-0000 007F | 0xxxxxxx * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ protected boolean addStringInternal(char[] backingChars) { int startCursor = cursor; int strLen = backingChars.length; cursor += ENCODING_SPACE_INT; for (int i=0; i<strLen; i++) { char c = backingChars[i]; if (c <= 0x7f) { data[cursor++] = (byte)(c); } else if (c <= 0x7ff) { if (!hasRoom(1 + (strLen-i))) { cursor = startCursor; return false; } data[cursor++] = (byte)(0xc0 | (c >> 6)); data[cursor++] = (byte)(0x80 | (c & 0x3f)); } else { if (!hasRoom(2 + (strLen-i))) { cursor = startCursor; return false; } data[cursor++] = (byte)(0xe0 | (c >> 12)); data[cursor++] = (byte)(0x80 | ((c & 0xfc0) >> 6)); data[cursor++] = (byte)(0x80 | (c & 0x3f)); } } int endCursor = cursor; int finalLen = endCursor-startCursor-ENCODING_SPACE_INT; cursor = startCursor; putInt(finalLen); cursor = endCursor; return true; } @Inline(value=Inline.When.AllArgumentsAreConstant) private void putLong(long value) { data[cursor + 0] = (byte) ((value >> 56) & 0xff); data[cursor + 1] = (byte) ((value >> 48) & 0xff); data[cursor + 2] = (byte) ((value >> 40) & 0xff); data[cursor + 3] = (byte) ((value >> 32) & 0xff); data[cursor + 4] = (byte) ((value >> 24) & 0xff); data[cursor + 5] = (byte) ((value >> 16) & 0xff); data[cursor + 6] = (byte) ((value >> 8) & 0xff); data[cursor + 7] = (byte) ((value >> 0) & 0xff); cursor += 8; } @Inline(value=Inline.When.AllArgumentsAreConstant) private void putInt(int value) { data[cursor + 0] = (byte) ((value >> 24) & 0xff); data[cursor + 1] = (byte) ((value >> 16) & 0xff); data[cursor + 2] = (byte) ((value >> 8) & 0xff); data[cursor + 3] = (byte) ((value >> 0) & 0xff); cursor += 4; } @Inline(value=Inline.When.AllArgumentsAreConstant) protected void putIntAt(int index, int value) { data[index + 0] = (byte) ((value >> 24) & 0xff); data[index + 1] = (byte) ((value >> 16) & 0xff); data[index + 2] = (byte) ((value >> 8) & 0xff); data[index + 3] = (byte) ((value >> 0) & 0xff); } }