Home | History | Annotate | Download | only in writer
      1 /*
      2  * Copyright 2012, Google Inc.
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  *     * Redistributions of source code must retain the above copyright
     10  * notice, this list of conditions and the following disclaimer.
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *     * Neither the name of Google Inc. nor the names of its
     16  * contributors may be used to endorse or promote products derived from
     17  * this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 package org.jf.dexlib2.writer;
     33 
     34 import org.jf.util.ExceptionWithContext;
     35 
     36 import javax.annotation.Nonnull;
     37 import java.io.BufferedOutputStream;
     38 import java.io.IOException;
     39 import java.io.OutputStream;
     40 
     41 public class DexDataWriter extends BufferedOutputStream {
     42     /**
     43      * The position within the file that we will write to next. This is only updated when the buffer is flushed to the
     44      * outputStream.
     45      */
     46     private int filePosition;
     47 
     48     /**
     49      * A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed.
     50      * Must be at least 8 bytes
     51      */
     52     private byte[] tempBuf = new byte[8];
     53 
     54     /** A buffer of 0s to use for writing alignment values */
     55     private byte[] zeroBuf = new byte[3];
     56 
     57     /**
     58      * Construct a new DexWriter instance that writes to output.
     59      *
     60      * @param output An OutputStream to write the data to.
     61      * @param filePosition The position within the file that OutputStream will write to.
     62      */
     63     public DexDataWriter(@Nonnull OutputStream output, int filePosition) {
     64         this(output, filePosition, 256 * 1024);
     65     }
     66 
     67     public DexDataWriter(@Nonnull OutputStream output, int filePosition, int bufferSize) {
     68         super(output, bufferSize);
     69 
     70         this.filePosition = filePosition;
     71     }
     72 
     73     @Override
     74     public void write(int b) throws IOException {
     75         filePosition++;
     76         super.write(b);
     77     }
     78 
     79     @Override
     80     public void write(byte[] b) throws IOException {
     81         write(b, 0, b.length);
     82     }
     83 
     84     @Override
     85     public void write(byte[] b, int off, int len) throws IOException {
     86         filePosition += len;
     87         super.write(b, off, len);
     88     }
     89 
     90     public void writeLong(long value) throws IOException {
     91         writeInt((int)value);
     92         writeInt((int)(value >> 32));
     93     }
     94 
     95     public static void writeInt(OutputStream out, int value) throws IOException {
     96         out.write(value);
     97         out.write(value >> 8);
     98         out.write(value >> 16);
     99         out.write(value >> 24);
    100     }
    101 
    102     public void writeInt(int value) throws IOException {
    103         writeInt(this, value);
    104     }
    105 
    106     public void writeShort(int value) throws IOException {
    107         if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
    108             throw new ExceptionWithContext("Short value out of range: %d", value);
    109         }
    110         write(value);
    111         write(value >> 8);
    112     }
    113 
    114     public void writeUshort(int value) throws IOException {
    115         if (value < 0 || value > 0xFFFF) {
    116             throw new ExceptionWithContext("Unsigned short value out of range: %d", value);
    117         }
    118         write(value);
    119         write(value >> 8);
    120     }
    121 
    122     public void writeUbyte(int value) throws IOException {
    123         if (value < 0 || value > 0xFF) {
    124             throw new ExceptionWithContext("Unsigned byte value out of range: %d", value);
    125         }
    126         write(value);
    127     }
    128 
    129     public static void writeUleb128(OutputStream out, int value) throws IOException {
    130         while ((value & 0xffffffffL) > 0x7f) {
    131             out.write((value & 0x7f) | 0x80);
    132             value >>>= 7;
    133         }
    134         out.write(value);
    135     }
    136 
    137     public void writeUleb128(int value) throws IOException {
    138         writeUleb128(this, value);
    139     }
    140 
    141     public static void writeSleb128(OutputStream out, int value) throws IOException {
    142         if (value >= 0) {
    143             while (value > 0x3f) {
    144                 out.write((value & 0x7f) | 0x80);
    145                 value >>>= 7;
    146             }
    147             out.write(value & 0x7f);
    148         } else {
    149             while (value < -0x40) {
    150                 out.write((value & 0x7f) | 0x80);
    151                 value >>= 7;
    152             }
    153             out.write(value & 0x7f);
    154         }
    155     }
    156 
    157     public void writeSleb128(int value) throws IOException {
    158         writeSleb128(this, value);
    159     }
    160 
    161     public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException {
    162         write(valueType | (valueArg << 5));
    163     }
    164 
    165     public void writeEncodedInt(int valueType, int value) throws IOException {
    166         int index = 0;
    167         if (value >= 0) {
    168             while (value > 0x7f) {
    169                 tempBuf[index++] = (byte)value;
    170                 value >>= 8;
    171             }
    172         } else {
    173             while (value < -0x80) {
    174                 tempBuf[index++] = (byte)value;
    175                 value >>= 8;
    176             }
    177         }
    178         tempBuf[index++] = (byte)value;
    179         writeEncodedValueHeader(valueType, index-1);
    180         write(tempBuf, 0, index);
    181     }
    182 
    183     public void writeEncodedLong(int valueType, long value) throws IOException {
    184         int index = 0;
    185         if (value >= 0) {
    186             while (value > 0x7f) {
    187                 tempBuf[index++] = (byte)value;
    188                 value >>= 8;
    189             }
    190         } else {
    191             while (value < -0x80) {
    192                 tempBuf[index++] = (byte)value;
    193                 value >>= 8;
    194             }
    195         }
    196         tempBuf[index++] = (byte)value;
    197         writeEncodedValueHeader(valueType, index-1);
    198         write(tempBuf, 0, index);
    199     }
    200 
    201     public void writeEncodedUint(int valueType, int value) throws IOException {
    202         int index = 0;
    203         do {
    204             tempBuf[index++] = (byte)value;
    205             value >>>= 8;
    206         } while (value != 0);
    207         writeEncodedValueHeader(valueType, index-1);
    208         write(tempBuf, 0, index);
    209     }
    210 
    211     public void writeEncodedFloat(int valueType, float value) throws IOException {
    212         writeRightZeroExtendedInt(valueType, Float.floatToRawIntBits(value));
    213     }
    214 
    215     protected void writeRightZeroExtendedInt(int valueType, int value) throws IOException {
    216         int index = 3;
    217         do {
    218             tempBuf[index--] = (byte)((value & 0xFF000000) >>> 24);
    219             value <<= 8;
    220         } while (value != 0);
    221 
    222         int firstElement = index+1;
    223         int encodedLength = 4-firstElement;
    224         writeEncodedValueHeader(valueType, encodedLength - 1);
    225         write(tempBuf, firstElement, encodedLength);
    226     }
    227 
    228     public void writeEncodedDouble(int valueType, double value) throws IOException {
    229         writeRightZeroExtendedLong(valueType, Double.doubleToRawLongBits(value));
    230     }
    231 
    232     protected void writeRightZeroExtendedLong(int valueType, long value) throws IOException {
    233         int index = 7;
    234         do {
    235             tempBuf[index--] = (byte)((value & 0xFF00000000000000L) >>> 56);
    236             value <<= 8;
    237         } while (value != 0);
    238 
    239         int firstElement = index+1;
    240         int encodedLength = 8-firstElement;
    241         writeEncodedValueHeader(valueType, encodedLength - 1);
    242         write(tempBuf, firstElement, encodedLength);
    243     }
    244 
    245     public void writeString(String string) throws IOException {
    246         int len = string.length();
    247 
    248         // make sure we have enough room in the temporary buffer
    249         if (tempBuf.length <= string.length()*3) {
    250             tempBuf = new byte[string.length()*3];
    251         }
    252 
    253         final byte[] buf = tempBuf;
    254 
    255         int bufPos = 0;
    256         for (int i = 0; i < len; i++) {
    257             char c = string.charAt(i);
    258             if ((c != 0) && (c < 0x80)) {
    259                 buf[bufPos++] = (byte)c;
    260             } else if (c < 0x800) {
    261                 buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0);
    262                 buf[bufPos++] = (byte)((c & 0x3f) | 0x80);
    263             } else {
    264                 buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0);
    265                 buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80);
    266                 buf[bufPos++] = (byte)((c & 0x3f) | 0x80);
    267             }
    268         }
    269         write(buf, 0, bufPos);
    270     }
    271 
    272     public void align() throws IOException {
    273         int zeros = (-getPosition()) & 3;
    274         if (zeros > 0) {
    275             write(zeroBuf, 0, zeros);
    276         }
    277     }
    278 
    279     public int getPosition() {
    280         return filePosition;
    281     }
    282 }
    283