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