DexWriter.java revision f3c33259dd0567294ef814be879b59a450c24f70
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 32package org.jf.dexlib2.writer; 33 34import com.google.common.base.Preconditions; 35import org.jf.util.ExceptionWithContext; 36 37import javax.annotation.Nonnull; 38import java.io.IOException; 39import java.io.OutputStream; 40import java.nio.ByteBuffer; 41import java.nio.MappedByteBuffer; 42import java.nio.channels.FileChannel; 43 44public class DexWriter extends OutputStream { 45 private static final int MAP_SIZE = 1024*1024; 46 private static final int BUF_SIZE = 256*1024; 47 48 @Nonnull private final FileChannel channel; 49 private MappedByteBuffer byteBuffer; 50 51 /** The position in the file at which byteBuffer starts. */ 52 private int mappedFilePosition; 53 54 private byte[] buf = new byte[BUF_SIZE]; 55 /** The index within buf to write to */ 56 private int bufPosition; 57 58 /** 59 * A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed. 60 * Must be at least 8 bytes 61 */ 62 private byte[] tempBuf = new byte[8]; 63 64 /** A buffer of 0s we used for writing alignment values */ 65 private byte[] zeroBuf = new byte[3]; 66 67 public DexWriter(FileChannel channel, int position) throws IOException { 68 this.channel = channel; 69 this.mappedFilePosition = position; 70 71 byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, position, MAP_SIZE); 72 } 73 74 @Override 75 public void write(int b) throws IOException { 76 if (bufPosition >= BUF_SIZE) { 77 flushBuffer(); 78 } 79 buf[bufPosition++] = (byte)b; 80 } 81 82 @Override 83 public void write(byte[] b) throws IOException { 84 write(b, 0, b.length); 85 } 86 87 @Override 88 public void write(byte[] b, int off, int len) throws IOException { 89 int toWrite = len; 90 91 if (bufPosition == BUF_SIZE) { 92 flushBuffer(); 93 } 94 int remainingBuffer = BUF_SIZE - bufPosition; 95 if (toWrite >= remainingBuffer) { 96 // fill up and write out the current buffer 97 System.arraycopy(b, 0, buf, bufPosition, remainingBuffer); 98 bufPosition += remainingBuffer; 99 toWrite -= remainingBuffer; 100 flushBuffer(); 101 102 // skip the intermediate buffer while we have a full buffer's worth 103 while (toWrite >= BUF_SIZE) { 104 writeBufferToMap(b, len - toWrite, BUF_SIZE); 105 toWrite -= BUF_SIZE; 106 } 107 } 108 // write out the final chunk, if any 109 if (toWrite > 0) { 110 System.arraycopy(b, len-toWrite, buf, bufPosition, len); 111 bufPosition += len; 112 } 113 } 114 115 public void writeLong(long value) throws IOException { 116 writeInt((int)value); 117 writeInt((int)(value >> 32)); 118 } 119 120 public void writeInt(int value) throws IOException { 121 write(value); 122 write(value >> 8); 123 write(value >> 16); 124 write(value >> 24); 125 } 126 127 public void writeShort(int value) throws IOException { 128 if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { 129 throw new ExceptionWithContext("Short value out of range: %d", value); 130 } 131 write(value); 132 write(value >> 8); 133 } 134 135 public void writeUshort(int value) throws IOException { 136 if (value < 0 || value > 0xFFFF) { 137 throw new ExceptionWithContext("Unsigned short value out of range: %d", value); 138 } 139 write(value); 140 write(value >> 8); 141 } 142 143 public void writeUbyte(int value) throws IOException { 144 if (value < 0 || value > 0xFF) { 145 throw new ExceptionWithContext("Unsigned byte value out of range: %d", value); 146 } 147 write(value); 148 } 149 150 public static void writeUleb128(OutputStream out, int value) throws IOException { 151 while (value > 0x7f) { 152 out.write((value & 0x7f) | 0x80); 153 value >>>= 7; 154 } 155 out.write(value); 156 } 157 158 public void writeUleb128(int value) throws IOException { 159 writeUleb128(this, value); 160 } 161 162 public static void writeSleb128(OutputStream out, int value) throws IOException { 163 if (value >= 0) { 164 while (value > 0x3f) { 165 out.write((value & 0x7f) | 0x80); 166 value >>>= 7; 167 } 168 out.write(value & 0x7f); 169 } else { 170 while (value < -0x40) { 171 out.write((value & 0x7f) | 0x80); 172 value >>= 7; 173 } 174 out.write(value & 0x7f); 175 } 176 } 177 178 public void writeSleb128(int value) throws IOException { 179 writeSleb128(this, value); 180 } 181 182 public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException { 183 write(valueType | (valueArg << 5)); 184 } 185 186 public void writeEncodedInt(int valueType, int value) throws IOException { 187 int index = 0; 188 if (value >= 0) { 189 while (value > 0x7f) { 190 tempBuf[index++] = (byte)value; 191 value >>= 8; 192 } 193 } else { 194 while (value < -0x80) { 195 tempBuf[index++] = (byte)value; 196 value >>= 8; 197 } 198 } 199 tempBuf[index++] = (byte)value; 200 writeEncodedValueHeader(valueType, index); 201 write(tempBuf, 0, index); 202 } 203 204 public void writeEncodedLong(int valueType, long value) throws IOException { 205 int index = 0; 206 if (value >= 0) { 207 while (value > 0x7f) { 208 tempBuf[index++] = (byte)value; 209 value >>= 8; 210 } 211 } else { 212 while (value < -0x80) { 213 tempBuf[index++] = (byte)value; 214 value >>= 8; 215 } 216 } 217 tempBuf[index++] = (byte)value; 218 writeEncodedValueHeader(valueType, index); 219 write(tempBuf, 0, index); 220 } 221 222 public void writeEncodedUint(int valueType, int value) throws IOException { 223 int index = 0; 224 do { 225 tempBuf[index++] = (byte)value; 226 value >>= 8; 227 } while (value != 0); 228 writeEncodedValueHeader(valueType, index); 229 write(tempBuf, 0, index); 230 } 231 232 public void writeEncodedFloat(int valueType, float value) throws IOException { 233 int intValue = Float.floatToRawIntBits(value); 234 235 int index = 3; 236 do { 237 buf[index--] = (byte)((intValue & 0xFF000000) >>> 24); 238 intValue <<= 8; 239 } while (intValue != 0); 240 writeEncodedValueHeader(valueType, 4-index); 241 write(buf, index+1, 4-index); 242 } 243 244 public void writeEncodedDouble(int valueType, double value) throws IOException { 245 long longValue = Double.doubleToRawLongBits(value); 246 247 int index = 7; 248 do { 249 buf[index--] = (byte)((longValue & 0xFF00000000000000L) >>> 56); 250 longValue <<= 8; 251 } while (longValue != 0); 252 writeEncodedValueHeader(valueType, 7-index); 253 write(buf, index+1, 7-index); 254 } 255 256 public void writeString(String string) throws IOException { 257 int len = string.length(); 258 259 // make sure we have enough room in the temporary buffer 260 if (tempBuf.length <= string.length()*3) { 261 tempBuf = new byte[string.length()*3]; 262 } 263 264 final byte[] buf = tempBuf; 265 266 int bufPos = 0; 267 for (int i = 0; i < len; i++) { 268 char c = string.charAt(i); 269 if ((c != 0) && (c < 0x80)) { 270 buf[bufPos++] = (byte)c; 271 } else if (c < 0x800) { 272 buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0); 273 buf[bufPos++] = (byte)((c & 0x3f) | 0x80); 274 } else { 275 buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0); 276 buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80); 277 buf[bufPos++] = (byte)((c & 0x3f) | 0x80); 278 } 279 } 280 write(buf, 0, bufPos); 281 } 282 283 public void align() throws IOException { 284 int zeros = (-getPosition()) & 3; 285 if (zeros > 0) { 286 write(zeroBuf, 0, zeros); 287 } 288 } 289 290 @Override 291 public void flush() throws IOException { 292 if (bufPosition > 0) { 293 writeBufferToMap(buf, 0, bufPosition); 294 bufPosition = 0; 295 } 296 byteBuffer.force(); 297 mappedFilePosition += byteBuffer.position(); 298 channel.position(mappedFilePosition + MAP_SIZE); 299 channel.write(ByteBuffer.wrap(new byte[]{0})); 300 byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, mappedFilePosition, MAP_SIZE); 301 } 302 303 @Override 304 public void close() throws IOException { 305 if (bufPosition > 0) { 306 writeBufferToMap(buf, 0, bufPosition); 307 bufPosition = 0; 308 } 309 byteBuffer.force(); 310 byteBuffer = null; 311 buf = null; 312 tempBuf = null; 313 } 314 315 private void flushBuffer() throws IOException { 316 Preconditions.checkState(bufPosition == BUF_SIZE); 317 writeBufferToMap(buf, 0, BUF_SIZE); 318 bufPosition = 0; 319 } 320 321 private void writeBufferToMap(byte[] buf, int bufOffset, int len) throws IOException { 322 // we always write BUF_SIZE, which evenly divides our mapped size, so we only care if remaining is 0 yet 323 if (!byteBuffer.hasRemaining()) { 324 byteBuffer.force(); 325 mappedFilePosition += MAP_SIZE; 326 byteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, mappedFilePosition, MAP_SIZE); 327 } 328 byteBuffer.put(buf, bufOffset, len); 329 } 330 331 public int getPosition() { 332 return mappedFilePosition + byteBuffer.position() + bufPosition; 333 } 334} 335