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