/* * Copyright 2012, Google Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.dexlib2.writer; import org.jf.util.ExceptionWithContext; import javax.annotation.Nonnull; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; public class DexDataWriter extends BufferedOutputStream { /** * The position within the file that we will write to next. This is only updated when the buffer is flushed to the * outputStream. */ private int filePosition; /** * A temporary buffer that can be used for larger writes. Can be replaced with a larger buffer if needed. * Must be at least 8 bytes */ private byte[] tempBuf = new byte[8]; /** A buffer of 0s to use for writing alignment values */ private byte[] zeroBuf = new byte[3]; /** * Construct a new DexWriter instance that writes to output. * * @param output An OutputStream to write the data to. * @param filePosition The position within the file that OutputStream will write to. */ public DexDataWriter(@Nonnull OutputStream output, int filePosition) { this(output, filePosition, 256 * 1024); } public DexDataWriter(@Nonnull OutputStream output, int filePosition, int bufferSize) { super(output, bufferSize); this.filePosition = filePosition; } @Override public void write(int b) throws IOException { filePosition++; super.write(b); } @Override public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { filePosition += len; super.write(b, off, len); } public void writeLong(long value) throws IOException { writeInt((int)value); writeInt((int)(value >> 32)); } public static void writeInt(OutputStream out, int value) throws IOException { out.write(value); out.write(value >> 8); out.write(value >> 16); out.write(value >> 24); } public void writeInt(int value) throws IOException { writeInt(this, value); } public void writeShort(int value) throws IOException { if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) { throw new ExceptionWithContext("Short value out of range: %d", value); } write(value); write(value >> 8); } public void writeUshort(int value) throws IOException { if (value < 0 || value > 0xFFFF) { throw new ExceptionWithContext("Unsigned short value out of range: %d", value); } write(value); write(value >> 8); } public void writeUbyte(int value) throws IOException { if (value < 0 || value > 0xFF) { throw new ExceptionWithContext("Unsigned byte value out of range: %d", value); } write(value); } public static void writeUleb128(OutputStream out, int value) throws IOException { while (value > 0x7f) { out.write((value & 0x7f) | 0x80); value >>>= 7; } out.write(value); } public void writeUleb128(int value) throws IOException { writeUleb128(this, value); } public static void writeSleb128(OutputStream out, int value) throws IOException { if (value >= 0) { while (value > 0x3f) { out.write((value & 0x7f) | 0x80); value >>>= 7; } out.write(value & 0x7f); } else { while (value < -0x40) { out.write((value & 0x7f) | 0x80); value >>= 7; } out.write(value & 0x7f); } } public void writeSleb128(int value) throws IOException { writeSleb128(this, value); } public void writeEncodedValueHeader(int valueType, int valueArg) throws IOException { write(valueType | (valueArg << 5)); } public void writeEncodedInt(int valueType, int value) throws IOException { int index = 0; if (value >= 0) { while (value > 0x7f) { tempBuf[index++] = (byte)value; value >>= 8; } } else { while (value < -0x80) { tempBuf[index++] = (byte)value; value >>= 8; } } tempBuf[index++] = (byte)value; writeEncodedValueHeader(valueType, index-1); write(tempBuf, 0, index); } public void writeEncodedLong(int valueType, long value) throws IOException { int index = 0; if (value >= 0) { while (value > 0x7f) { tempBuf[index++] = (byte)value; value >>= 8; } } else { while (value < -0x80) { tempBuf[index++] = (byte)value; value >>= 8; } } tempBuf[index++] = (byte)value; writeEncodedValueHeader(valueType, index-1); write(tempBuf, 0, index); } public void writeEncodedUint(int valueType, int value) throws IOException { int index = 0; do { tempBuf[index++] = (byte)value; value >>>= 8; } while (value != 0); writeEncodedValueHeader(valueType, index-1); write(tempBuf, 0, index); } public void writeEncodedFloat(int valueType, float value) throws IOException { writeRightZeroExtendedInt(valueType, Float.floatToRawIntBits(value)); } protected void writeRightZeroExtendedInt(int valueType, int value) throws IOException { int index = 3; do { tempBuf[index--] = (byte)((value & 0xFF000000) >>> 24); value <<= 8; } while (value != 0); int firstElement = index+1; int encodedLength = 4-firstElement; writeEncodedValueHeader(valueType, encodedLength - 1); write(tempBuf, firstElement, encodedLength); } public void writeEncodedDouble(int valueType, double value) throws IOException { writeRightZeroExtendedLong(valueType, Double.doubleToRawLongBits(value)); } protected void writeRightZeroExtendedLong(int valueType, long value) throws IOException { int index = 7; do { tempBuf[index--] = (byte)((value & 0xFF00000000000000L) >>> 56); value <<= 8; } while (value != 0); int firstElement = index+1; int encodedLength = 8-firstElement; writeEncodedValueHeader(valueType, encodedLength - 1); write(tempBuf, firstElement, encodedLength); } public void writeString(String string) throws IOException { int len = string.length(); // make sure we have enough room in the temporary buffer if (tempBuf.length <= string.length()*3) { tempBuf = new byte[string.length()*3]; } final byte[] buf = tempBuf; int bufPos = 0; for (int i = 0; i < len; i++) { char c = string.charAt(i); if ((c != 0) && (c < 0x80)) { buf[bufPos++] = (byte)c; } else if (c < 0x800) { buf[bufPos++] = (byte)(((c >> 6) & 0x1f) | 0xc0); buf[bufPos++] = (byte)((c & 0x3f) | 0x80); } else { buf[bufPos++] = (byte)(((c >> 12) & 0x0f) | 0xe0); buf[bufPos++] = (byte)(((c >> 6) & 0x3f) | 0x80); buf[bufPos++] = (byte)((c & 0x3f) | 0x80); } } write(buf, 0, bufPos); } public void align() throws IOException { int zeros = (-getPosition()) & 3; if (zeros > 0) { write(zeroBuf, 0, zeros); } } public int getPosition() { return filePosition; } }