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