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