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