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 * Used to consume bytes. 035 * @since 1.14 036 */ 037 public interface ByteConsumer { 038 /** 039 * The contract is similar to {@link OutputStream#write(int)}, 040 * consume the lower eight bytes of the int as a byte. 041 * @param b the byte to consume 042 * @throws IOException if consuming fails 043 */ 044 void accept(int b) throws IOException; 045 } 046 047 /** 048 * Used to supply bytes. 049 * @since 1.14 050 */ 051 public interface ByteSupplier { 052 /** 053 * The contract is similar to {@link InputStream#read()}, return 054 * the byte as an unsigned int, -1 if there are no more bytes. 055 * @return the supplied byte or -1 if there are no more bytes 056 * @throws IOException if supplying fails 057 */ 058 int getAsByte() throws IOException; 059 } 060 061 /** 062 * {@link ByteSupplier} based on {@link InputStream}. 063 * @since 1.14 064 */ 065 public static class InputStreamByteSupplier implements ByteSupplier { 066 private final InputStream is; 067 public InputStreamByteSupplier(final InputStream is) { 068 this.is = is; 069 } 070 @Override 071 public int getAsByte() throws IOException { 072 return is.read(); 073 } 074 } 075 076 /** 077 * {@link ByteConsumer} based on {@link OutputStream}. 078 * @since 1.14 079 */ 080 public static class OutputStreamByteConsumer implements ByteConsumer { 081 private final OutputStream os; 082 public OutputStreamByteConsumer(final OutputStream os) { 083 this.os = os; 084 } 085 @Override 086 public void accept(final int b) throws IOException { 087 os.write(b); 088 } 089 } 090 091 /** 092 * Empty array. 093 * 094 * @since 1.21 095 */ 096 public static final byte[] EMPTY_BYTE_ARRAY = {}; 097 098 private static void checkReadLength(final int length) { 099 if (length > 8) { 100 throw new IllegalArgumentException("Can't read more than eight bytes into a long value"); 101 } 102 } 103 104 /** 105 * Reads the given byte array as a little endian long. 106 * @param bytes the byte array to convert 107 * @return the number read 108 */ 109 public static long fromLittleEndian(final byte[] bytes) { 110 return fromLittleEndian(bytes, 0, bytes.length); 111 } 112 113 /** 114 * Reads the given byte array as a little endian long. 115 * @param bytes the byte array to convert 116 * @param off the offset into the array that starts the value 117 * @param length the number of bytes representing the value 118 * @return the number read 119 * @throws IllegalArgumentException if len is bigger than eight 120 */ 121 public static long fromLittleEndian(final byte[] bytes, final int off, final int length) { 122 checkReadLength(length); 123 long l = 0; 124 for (int i = 0; i < length; i++) { 125 l |= (bytes[off + i] & 0xffL) << (8 * i); 126 } 127 return l; 128 } 129 130 /** 131 * Reads the given number of bytes from the given supplier as a little endian long. 132 * 133 * <p>Typically used by our InputStreams that need to count the 134 * bytes read as well.</p> 135 * 136 * @param supplier the supplier for bytes 137 * @param length the number of bytes representing the value 138 * @return the number read 139 * @throws IllegalArgumentException if len is bigger than eight 140 * @throws IOException if the supplier fails or doesn't supply the 141 * given number of bytes anymore 142 */ 143 public static long fromLittleEndian(final ByteSupplier supplier, final int length) throws IOException { 144 checkReadLength(length); 145 long l = 0; 146 for (int i = 0; i < length; i++) { 147 final long b = supplier.getAsByte(); 148 if (b == -1) { 149 throw new IOException("Premature end of data"); 150 } 151 l |= (b << (i * 8)); 152 } 153 return l; 154 } 155 156 /** 157 * Reads the given number of bytes from the given input as little endian long. 158 * @param in the input to read from 159 * @param length the number of bytes representing the value 160 * @return the number read 161 * @throws IllegalArgumentException if len is bigger than eight 162 * @throws IOException if reading fails or the stream doesn't 163 * contain the given number of bytes anymore 164 */ 165 public static long fromLittleEndian(final DataInput in, final int length) throws IOException { 166 // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object 167 checkReadLength(length); 168 long l = 0; 169 for (int i = 0; i < length; i++) { 170 final long b = in.readUnsignedByte(); 171 l |= (b << (i * 8)); 172 } 173 return l; 174 } 175 176 /** 177 * Reads the given number of bytes from the given stream as a little endian long. 178 * @param in the stream to read from 179 * @param length the number of bytes representing the value 180 * @return the number read 181 * @throws IllegalArgumentException if len is bigger than eight 182 * @throws IOException if reading fails or the stream doesn't 183 * contain the given number of bytes anymore 184 */ 185 public static long fromLittleEndian(final InputStream in, final int length) throws IOException { 186 // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object 187 checkReadLength(length); 188 long l = 0; 189 for (int i = 0; i < length; i++) { 190 final long b = in.read(); 191 if (b == -1) { 192 throw new IOException("Premature end of data"); 193 } 194 l |= (b << (i * 8)); 195 } 196 return l; 197 } 198 199 /** 200 * Inserts the given value into the array as a little endian 201 * sequence of the given length starting at the given offset. 202 * @param b the array to write into 203 * @param value the value to insert 204 * @param off the offset into the array that receives the first byte 205 * @param length the number of bytes to use to represent the value 206 */ 207 public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) { 208 long num = value; 209 for (int i = 0; i < length; i++) { 210 b[off + i] = (byte) (num & 0xff); 211 num >>= 8; 212 } 213 } 214 215 /** 216 * Provides the given value to the given consumer as a little endian 217 * sequence of the given length. 218 * @param consumer the consumer to provide the bytes to 219 * @param value the value to provide 220 * @param length the number of bytes to use to represent the value 221 * @throws IOException if writing fails 222 */ 223 public static void toLittleEndian(final ByteConsumer consumer, final long value, final int length) 224 throws IOException { 225 long num = value; 226 for (int i = 0; i < length; i++) { 227 consumer.accept((int) (num & 0xff)); 228 num >>= 8; 229 } 230 } 231 232 /** 233 * Writes the given value to the given stream as a little endian 234 * array of the given length. 235 * @param out the output to write to 236 * @param value the value to write 237 * @param length the number of bytes to use to represent the value 238 * @throws IOException if writing fails 239 */ 240 public static void toLittleEndian(final DataOutput out, final long value, final int length) 241 throws IOException { 242 // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object 243 long num = value; 244 for (int i = 0; i < length; i++) { 245 out.write((int) (num & 0xff)); 246 num >>= 8; 247 } 248 } 249 250 /** 251 * Writes the given value to the given stream as a little endian 252 * array of the given length. 253 * @param out the stream to write to 254 * @param value the value to write 255 * @param length the number of bytes to use to represent the value 256 * @throws IOException if writing fails 257 */ 258 public static void toLittleEndian(final OutputStream out, final long value, final int length) 259 throws IOException { 260 // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object 261 long num = value; 262 for (int i = 0; i < length; i++) { 263 out.write((int) (num & 0xff)); 264 num >>= 8; 265 } 266 } 267 268 private ByteUtils() { /* no instances */ } 269}