1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.build.apkzlib.zip.utils; 18 19 import com.google.common.base.Preconditions; 20 import com.google.common.base.Verify; 21 import java.io.EOFException; 22 import java.io.IOException; 23 import java.nio.ByteBuffer; 24 import javax.annotation.Nonnull; 25 26 /** 27 * Utilities to read and write 16 and 32 bit integers with support for little-endian 28 * encoding, as used in zip files. Zip files actually use unsigned data types. We use Java's native 29 * (signed) data types but will use long (64 bit) to ensure we can fit the whole range. 30 */ 31 public class LittleEndianUtils { 32 /** 33 * Utility class, no constructor. 34 */ LittleEndianUtils()35 private LittleEndianUtils() { 36 } 37 38 /** 39 * Reads 4 bytes in little-endian format and converts them into a 32-bit value. 40 * 41 * @param bytes from where should the bytes be read; the first 4 bytes of the source will be 42 * read 43 * @return the 32-bit value 44 * @throws IOException failed to read the value 45 */ readUnsigned4Le(@onnull ByteBuffer bytes)46 public static long readUnsigned4Le(@Nonnull ByteBuffer bytes) throws IOException { 47 Preconditions.checkNotNull(bytes, "bytes == null"); 48 49 if (bytes.remaining() < 4) { 50 throw new EOFException("Not enough data: 4 bytes expected, " + bytes.remaining() 51 + " available."); 52 } 53 54 byte b0 = bytes.get(); 55 byte b1 = bytes.get(); 56 byte b2 = bytes.get(); 57 byte b3 = bytes.get(); 58 long r = (b0 & 0xff) | ((b1 & 0xff) << 8) | ((b2 & 0xff) << 16) | ((b3 & 0xffL) << 24); 59 Verify.verify(r >= 0); 60 Verify.verify(r <= 0x00000000ffffffffL); 61 return r; 62 } 63 64 /** 65 * Reads 2 bytes in little-endian format and converts them into a 16-bit value. 66 * 67 * @param bytes from where should the bytes be read; the first 2 bytes of the source will be 68 * read 69 * @return the 16-bit value 70 * @throws IOException failed to read the value 71 */ readUnsigned2Le(@onnull ByteBuffer bytes)72 public static int readUnsigned2Le(@Nonnull ByteBuffer bytes) throws IOException { 73 Preconditions.checkNotNull(bytes, "bytes == null"); 74 75 if (bytes.remaining() < 2) { 76 throw new EOFException( 77 "Not enough data: 2 bytes expected, " 78 + bytes.remaining() 79 + " available."); 80 } 81 82 byte b0 = bytes.get(); 83 byte b1 = bytes.get(); 84 int r = (b0 & 0xff) | ((b1 & 0xff) << 8); 85 86 Verify.verify(r >= 0); 87 Verify.verify(r <= 0x0000ffff); 88 return r; 89 } 90 91 /** 92 * Writes 4 bytes in little-endian format, converting them from a 32-bit value. 93 * 94 * @param output the output stream where the bytes will be written 95 * @param value the 32-bit value to convert 96 * @throws IOException failed to write the value data 97 */ writeUnsigned4Le(@onnull ByteBuffer output, long value)98 public static void writeUnsigned4Le(@Nonnull ByteBuffer output, long value) 99 throws IOException { 100 Preconditions.checkNotNull(output, "output == null"); 101 Preconditions.checkArgument(value >= 0, "value (%s) < 0", value); 102 Preconditions.checkArgument( 103 value <= 0x00000000ffffffffL, 104 "value (%s) > 0x00000000ffffffffL", 105 value); 106 107 output.put((byte) (value & 0xff)); 108 output.put((byte) ((value >> 8) & 0xff)); 109 output.put((byte) ((value >> 16) & 0xff)); 110 output.put((byte) ((value >> 24) & 0xff)); 111 } 112 113 /** 114 * Writes 2 bytes in little-endian format, converting them from a 16-bit value. 115 * 116 * @param output the output stream where the bytes will be written 117 * @param value the 16-bit value to convert 118 * @throws IOException failed to write the value data 119 */ writeUnsigned2Le(@onnull ByteBuffer output, int value)120 public static void writeUnsigned2Le(@Nonnull ByteBuffer output, int value) 121 throws IOException { 122 Preconditions.checkNotNull(output, "output == null"); 123 Preconditions.checkArgument(value >= 0, "value (%s) < 0", value); 124 Preconditions.checkArgument(value <= 0x0000ffff, "value (%s) > 0x0000ffff", value); 125 126 output.put((byte) (value & 0xff)); 127 output.put((byte) ((value >> 8) & 0xff)); 128 } 129 } 130