001/*
002 * This file is part of the Tuning Fork Visualization Platform
003 *  (http://sourceforge.net/projects/tuningforkvp)
004 *
005 * Copyright (c) 2005 - 2008 IBM Corporation.
006 * All rights reserved. This program and the accompanying materials
007 * are made available under the terms of the Eclipse Public License v1.0
008 * which accompanies this distribution, and is available at
009 * http://www.eclipse.org/legal/epl-v10.html
010 *
011 * Contributors:
012 *     IBM Corporation - initial API and implementation
013 */
014
015package com.ibm.tuningfork.tracegen.chunk;
016
017import java.io.IOException;
018import java.io.OutputStream;
019
020import org.jikesrvm.VM;
021import org.jikesrvm.runtime.Magic;
022import org.vmmagic.pragma.Inline;
023import org.vmmagic.pragma.Interruptible;
024import org.vmmagic.pragma.Uninterruptible;
025
026/*
027 * There are 3 basic operations: (1) Closing a chunk which involves fixing up
028 * data that cannot be determined initially such as the length field. Subclasses
029 * should override this at each level and call the superclass's close(). The
030 * base implementation will limit and reset the buffer position so overrides
031 * need to put the position at the end of data. (2) Writing the data out. This
032 * method is not overrideable as it simply means to write out the contents of
033 * the buffer. The cursor position will be reset to zero. The entire chunk is
034 * written out and the position is undefined on return. (3) Reset which allows
035 * the buffer object to be reused. Subclasses that can actually be reset should
036 * define a reset method and use the resetImpl method here.
037 */
038@Uninterruptible
039public abstract class RawChunk {
040
041    public final static int ENCODING_SPACE_INT = 4;
042    public final static int ENCODING_SPACE_LONG = 8;
043    public final static int ENCODING_SPACE_DOUBLE = 8;
044
045    private final byte[] data;
046    private int cursor = 0;
047    private boolean open = true;
048
049    protected RawChunk(byte[] buffer) {
050        data = buffer;
051    }
052
053    protected RawChunk(int capacity) {
054        this(new byte[capacity]);
055    }
056
057    public void close() {
058        if (!open) {
059            VM.sysWriteln("RawChunk: Cannot close a closed chunk.");
060        }
061        open = false;
062    }
063
064    /* Synchronous */
065    @Interruptible
066    public final void write(OutputStream outputStream) throws IOException {
067        outputStream.write(data, 0, cursor);
068    }
069
070    protected void resetImpl() {
071        cursor = 0;
072        open = true;
073    }
074
075    protected int getPosition() {
076        return cursor;
077    }
078
079    protected void seek(int pos) {
080        cursor = pos;
081    }
082
083    protected final boolean hasRoom(int bytes) {
084        int remaining = data.length - cursor;
085        return remaining >= bytes;
086    }
087
088    protected final boolean addLong(long l) {
089        if (!hasRoom(ENCODING_SPACE_LONG)) {
090            return false;
091        }
092        putLong(l);
093        return true;
094    }
095
096    protected final void addLongUnchecked(long l) {
097      putLong(l);
098    }
099
100    protected final boolean addDouble(double d) {
101        if (!hasRoom(ENCODING_SPACE_DOUBLE)) {
102            return false;
103        }
104        putLong(Magic.doubleAsLongBits(d));
105        return true;
106    }
107
108    protected final void addDoubleUnchecked(double d) {
109      putLong(Magic.doubleAsLongBits(d));
110    }
111
112    protected final boolean addInt(int i) {
113        if (!hasRoom(ENCODING_SPACE_INT)) {
114            return false;
115        }
116        putInt(i);
117        return true;
118    }
119
120    protected final void addIntUnchecked(int i) {
121      putInt(i);
122    }
123
124    protected final boolean addByte(byte b) {
125        if (!hasRoom(1)) {
126            return false;
127        }
128        data[cursor++] = b;
129        return true;
130    }
131
132    protected final boolean addString(String str) {
133        int strLen = JikesRVMSupport.getStringLength(str);
134        int minimalSize = ENCODING_SPACE_INT + strLen;
135        if (!hasRoom(minimalSize)) {
136            return false;
137        }
138        char[] backingChars = JikesRVMSupport.getBackingCharArray(str);
139        return addStringInternal(backingChars);
140    }
141
142    /*
143     * Write String's char[] encoded as UTF-8.
144     * Table from http://tools.ietf.org/html/rfc3629
145     *
146     *    Char. number range  |        UTF-8 octet sequence
147     *       (hexadecimal)    |              (binary)
148     *    --------------------+---------------------------------------------
149     *    0000 0000-0000 007F | 0xxxxxxx
150     *    0000 0080-0000 07FF | 110xxxxx 10xxxxxx
151     *    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx
152     *    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
153     */
154    protected boolean addStringInternal(char[] backingChars) {
155      int startCursor = cursor;
156      int strLen = backingChars.length;
157      cursor += ENCODING_SPACE_INT;
158
159      for (int i=0; i<strLen; i++) {
160          char c = backingChars[i];
161          if (c <= 0x7f) {
162        data[cursor++] = (byte)(c);
163          } else if (c <= 0x7ff) {
164        if (!hasRoom(1 + (strLen-i))) {
165            cursor = startCursor;
166            return false;
167        }
168        data[cursor++] = (byte)(0xc0 | (c >> 6));
169        data[cursor++] = (byte)(0x80 | (c & 0x3f));
170          } else {
171        if (!hasRoom(2 + (strLen-i))) {
172            cursor = startCursor;
173            return false;
174        }
175        data[cursor++] = (byte)(0xe0 | (c >> 12));
176        data[cursor++] = (byte)(0x80 | ((c & 0xfc0) >> 6));
177        data[cursor++] = (byte)(0x80 | (c & 0x3f));
178          }
179      }
180      int endCursor = cursor;
181      int finalLen = endCursor-startCursor-ENCODING_SPACE_INT;
182      cursor = startCursor;
183      putInt(finalLen);
184      cursor = endCursor;
185      return true;
186    }
187
188    @Inline(value=Inline.When.AllArgumentsAreConstant)
189    private void putLong(long value) {
190        data[cursor + 0] = (byte) ((value >> 56) & 0xff);
191        data[cursor + 1] = (byte) ((value >> 48) & 0xff);
192        data[cursor + 2] = (byte) ((value >> 40) & 0xff);
193        data[cursor + 3] = (byte) ((value >> 32) & 0xff);
194        data[cursor + 4] = (byte) ((value >> 24) & 0xff);
195        data[cursor + 5] = (byte) ((value >> 16) & 0xff);
196        data[cursor + 6] = (byte) ((value >> 8) & 0xff);
197        data[cursor + 7] = (byte) ((value >> 0) & 0xff);
198        cursor += 8;
199    }
200
201    @Inline(value=Inline.When.AllArgumentsAreConstant)
202    private void putInt(int value) {
203        data[cursor + 0] = (byte) ((value >> 24) & 0xff);
204        data[cursor + 1] = (byte) ((value >> 16) & 0xff);
205        data[cursor + 2] = (byte) ((value >> 8) & 0xff);
206        data[cursor + 3] = (byte) ((value >> 0) & 0xff);
207        cursor += 4;
208    }
209
210    @Inline(value=Inline.When.AllArgumentsAreConstant)
211    protected void putIntAt(int index, int value) {
212        data[index + 0] = (byte) ((value >> 24) & 0xff);
213        data[index + 1] = (byte) ((value >> 16) & 0xff);
214        data[index + 2] = (byte) ((value >> 8) & 0xff);
215        data[index + 3] = (byte) ((value >> 0) & 0xff);
216    }
217
218}