001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 * 017 */ 018 019package org.apache.commons.compress.utils; 020 021import java.io.DataInput; 022import java.io.DataOutput; 023import java.io.IOException; 024import java.io.InputStream; 025import java.io.OutputStream; 026 027/** 028 * Utility methods for reading and writing bytes. 029 * @since 1.14 030 */ 031public final class ByteUtils { 032 033 /** 034 * Empty array. 035 * 036 * @since 1.21 037 */ 038 public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; 039 040 private ByteUtils() { /* no instances */ } 041 042 /** 043 * Used to supply bytes. 044 * @since 1.14 045 */ 046 public interface ByteSupplier { 047 /** 048 * The contract is similar to {@link InputStream#read()}, return 049 * the byte as an unsigned int, -1 if there are no more bytes. 050 * @return the supplied byte or -1 if there are no more bytes 051 * @throws IOException if supplying fails 052 */ 053 int getAsByte() throws IOException; 054 } 055 056 /** 057 * Used to consume bytes. 058 * @since 1.14 059 */ 060 public interface ByteConsumer { 061 /** 062 * The contract is similar to {@link OutputStream#write(int)}, 063 * consume the lower eight bytes of the int as a byte. 064 * @param b the byte to consume 065 * @throws IOException if consuming fails 066 */ 067 void accept(int b) throws IOException; 068 } 069 070 /** 071 * Reads the given byte array as a little endian long. 072 * @param bytes the byte array to convert 073 * @return the number read 074 */ 075 public static long fromLittleEndian(final byte[] bytes) { 076 return fromLittleEndian(bytes, 0, bytes.length); 077 } 078 079 /** 080 * Reads the given byte array as a little endian long. 081 * @param bytes the byte array to convert 082 * @param off the offset into the array that starts the value 083 * @param length the number of bytes representing the value 084 * @return the number read 085 * @throws IllegalArgumentException if len is bigger than eight 086 */ 087 public static long fromLittleEndian(final byte[] bytes, final int off, final int length) { 088 checkReadLength(length); 089 long l = 0; 090 for (int i = 0; i < length; i++) { 091 l |= (bytes[off + i] & 0xffL) << (8 * i); 092 } 093 return l; 094 } 095 096 /** 097 * Reads the given number of bytes from the given stream as a little endian long. 098 * @param in the stream to read from 099 * @param length the number of bytes representing the value 100 * @return the number read 101 * @throws IllegalArgumentException if len is bigger than eight 102 * @throws IOException if reading fails or the stream doesn't 103 * contain the given number of bytes anymore 104 */ 105 public static long fromLittleEndian(final InputStream in, final int length) throws IOException { 106 // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object 107 checkReadLength(length); 108 long l = 0; 109 for (int i = 0; i < length; i++) { 110 final long b = in.read(); 111 if (b == -1) { 112 throw new IOException("Premature end of data"); 113 } 114 l |= (b << (i * 8)); 115 } 116 return l; 117 } 118 119 /** 120 * Reads the given number of bytes from the given supplier as a little endian long. 121 * 122 * <p>Typically used by our InputStreams that need to count the 123 * bytes read as well.</p> 124 * 125 * @param supplier the supplier for bytes 126 * @param length the number of bytes representing the value 127 * @return the number read 128 * @throws IllegalArgumentException if len is bigger than eight 129 * @throws IOException if the supplier fails or doesn't supply the 130 * given number of bytes anymore 131 */ 132 public static long fromLittleEndian(final ByteSupplier supplier, final int length) throws IOException { 133 checkReadLength(length); 134 long l = 0; 135 for (int i = 0; i < length; i++) { 136 final long b = supplier.getAsByte(); 137 if (b == -1) { 138 throw new IOException("Premature end of data"); 139 } 140 l |= (b << (i * 8)); 141 } 142 return l; 143 } 144 145 /** 146 * Reads the given number of bytes from the given input as little endian long. 147 * @param in the input to read from 148 * @param length the number of bytes representing the value 149 * @return the number read 150 * @throws IllegalArgumentException if len is bigger than eight 151 * @throws IOException if reading fails or the stream doesn't 152 * contain the given number of bytes anymore 153 */ 154 public static long fromLittleEndian(final DataInput in, final int length) throws IOException { 155 // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object 156 checkReadLength(length); 157 long l = 0; 158 for (int i = 0; i < length; i++) { 159 final long b = in.readUnsignedByte(); 160 l |= (b << (i * 8)); 161 } 162 return l; 163 } 164 165 /** 166 * Inserts the given value into the array as a little endian 167 * sequence of the given length starting at the given offset. 168 * @param b the array to write into 169 * @param value the value to insert 170 * @param off the offset into the array that receives the first byte 171 * @param length the number of bytes to use to represent the value 172 */ 173 public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) { 174 long num = value; 175 for (int i = 0; i < length; i++) { 176 b[off + i] = (byte) (num & 0xff); 177 num >>= 8; 178 } 179 } 180 181 /** 182 * Writes the given value to the given stream as a little endian 183 * array of the given length. 184 * @param out the stream to write to 185 * @param value the value to write 186 * @param length the number of bytes to use to represent the value 187 * @throws IOException if writing fails 188 */ 189 public static void toLittleEndian(final OutputStream out, final long value, final int length) 190 throws IOException { 191 // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object 192 long num = value; 193 for (int i = 0; i < length; i++) { 194 out.write((int) (num & 0xff)); 195 num >>= 8; 196 } 197 } 198 199 /** 200 * Provides the given value to the given consumer as a little endian 201 * sequence of the given length. 202 * @param consumer the consumer to provide the bytes to 203 * @param value the value to provide 204 * @param length the number of bytes to use to represent the value 205 * @throws IOException if writing fails 206 */ 207 public static void toLittleEndian(final ByteConsumer consumer, final long value, final int length) 208 throws IOException { 209 long num = value; 210 for (int i = 0; i < length; i++) { 211 consumer.accept((int) (num & 0xff)); 212 num >>= 8; 213 } 214 } 215 216 /** 217 * Writes the given value to the given stream as a little endian 218 * array of the given length. 219 * @param out the output to write to 220 * @param value the value to write 221 * @param length the number of bytes to use to represent the value 222 * @throws IOException if writing fails 223 */ 224 public static void toLittleEndian(final DataOutput out, final long value, final int length) 225 throws IOException { 226 // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object 227 long num = value; 228 for (int i = 0; i < length; i++) { 229 out.write((int) (num & 0xff)); 230 num >>= 8; 231 } 232 } 233 234 /** 235 * {@link ByteSupplier} based on {@link InputStream}. 236 * @since 1.14 237 */ 238 public static class InputStreamByteSupplier implements ByteSupplier { 239 private final InputStream is; 240 public InputStreamByteSupplier(final InputStream is) { 241 this.is = is; 242 } 243 @Override 244 public int getAsByte() throws IOException { 245 return is.read(); 246 } 247 } 248 249 /** 250 * {@link ByteConsumer} based on {@link OutputStream}. 251 * @since 1.14 252 */ 253 public static class OutputStreamByteConsumer implements ByteConsumer { 254 private final OutputStream os; 255 public OutputStreamByteConsumer(final OutputStream os) { 256 this.os = os; 257 } 258 @Override 259 public void accept(final int b) throws IOException { 260 os.write(b); 261 } 262 } 263 264 private static void checkReadLength(final int length) { 265 if (length > 8) { 266 throw new IllegalArgumentException("Can't read more than eight bytes into a long value"); 267 } 268 } 269}