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    
015    package com.ibm.tuningfork.tracegen.chunk;
016    
017    import java.io.IOException;
018    import java.io.OutputStream;
019    
020    import org.jikesrvm.VM;
021    import org.jikesrvm.runtime.Magic;
022    import org.vmmagic.pragma.Inline;
023    import org.vmmagic.pragma.Interruptible;
024    import 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
039    public 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    }