diff options
Diffstat (limited to 'src/main/java/org/apache/commons/compress/utils/ByteUtils.java')
-rw-r--r-- | src/main/java/org/apache/commons/compress/utils/ByteUtils.java | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/src/main/java/org/apache/commons/compress/utils/ByteUtils.java b/src/main/java/org/apache/commons/compress/utils/ByteUtils.java new file mode 100644 index 000000000..85b4118e9 --- /dev/null +++ b/src/main/java/org/apache/commons/compress/utils/ByteUtils.java @@ -0,0 +1,261 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.commons.compress.utils; + +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Utility methods for reading and writing bytes. + * @since 1.14 + */ +public final class ByteUtils { + private ByteUtils() { /* no instances */ } + + /** + * Used to supply bytes. + * @since 1.14 + */ + public interface ByteSupplier { + /** + * The contract is similar to {@link InputStream#read()}, return + * the byte as an unsigned int, -1 if there are no more bytes. + * @return the supplied byte or -1 if there are no more bytes + * @throws IOException if supplying fails + */ + int getAsByte() throws IOException; + } + + /** + * Used to consume bytes. + * @since 1.14 + */ + public interface ByteConsumer { + /** + * The contract is similar to {@link OutputStream#write(int)}, + * consume the lower eight bytes of the int as a byte. + * @param b the byte to consume + * @throws IOException if consuming fails + */ + void accept(int b) throws IOException; + } + + /** + * Reads the given byte array as a little endian long. + * @param bytes the byte array to convert + * @return the number read + */ + public static long fromLittleEndian(byte[] bytes) { + return fromLittleEndian(bytes, 0, bytes.length); + } + + /** + * Reads the given byte array as a little endian long. + * @param bytes the byte array to convert + * @param off the offset into the array that starts the value + * @param length the number of bytes representing the value + * @return the number read + * @throws IllegalArgumentException if len is bigger than eight + */ + public static long fromLittleEndian(byte[] bytes, final int off, final int length) { + checkReadLength(length); + long l = 0; + for (int i = 0; i < length; i++) { + l |= (bytes[off + i] & 0xffL) << (8 * i); + } + return l; + } + + /** + * Reads the given number of bytes from the given stream as a little endian long. + * @param in the stream to read from + * @param length the number of bytes representing the value + * @return the number read + * @throws IllegalArgumentException if len is bigger than eight + * @throws IOException if reading fails or the stream doesn't + * contain the given number of bytes anymore + */ + public static long fromLittleEndian(InputStream in, int length) throws IOException { + // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object + checkReadLength(length); + long l = 0; + for (int i = 0; i < length; i++) { + long b = in.read(); + if (b == -1) { + throw new IOException("premature end of data"); + } + l |= (b << (i * 8)); + } + return l; + } + + /** + * Reads the given number of bytes from the given supplier as a little endian long. + * + * <p>Typically used by our InputStreams that need to count the + * bytes read as well.</p> + * + * @param supplier the supplier for bytes + * @param length the number of bytes representing the value + * @return the number read + * @throws IllegalArgumentException if len is bigger than eight + * @throws IOException if the supplier fails or doesn't supply the + * given number of bytes anymore + */ + public static long fromLittleEndian(ByteSupplier supplier, final int length) throws IOException { + checkReadLength(length); + long l = 0; + for (int i = 0; i < length; i++) { + long b = supplier.getAsByte(); + if (b == -1) { + throw new IOException("premature end of data"); + } + l |= (b << (i * 8)); + } + return l; + } + + /** + * Reads the given number of bytes from the given input as little endian long. + * @param in the input to read from + * @param length the number of bytes representing the value + * @return the number read + * @throws IllegalArgumentException if len is bigger than eight + * @throws IOException if reading fails or the stream doesn't + * contain the given number of bytes anymore + */ + public static long fromLittleEndian(DataInput in, int length) throws IOException { + // somewhat duplicates the ByteSupplier version in order to save the creation of a wrapper object + checkReadLength(length); + long l = 0; + for (int i = 0; i < length; i++) { + long b = in.readUnsignedByte(); + l |= (b << (i * 8)); + } + return l; + } + + /** + * Inserts the given value into the array as a little endian + * sequence of the given length starting at the given offset. + * @param b the array to write into + * @param value the value to insert + * @param off the offset into the array that receives the first byte + * @param length the number of bytes to use to represent the value + */ + public static void toLittleEndian(final byte[] b, final long value, final int off, final int length) { + long num = value; + for (int i = 0; i < length; i++) { + b[off + i] = (byte) (num & 0xff); + num >>= 8; + } + } + + /** + * Writes the given value to the given stream as a little endian + * array of the given length. + * @param out the stream to write to + * @param value the value to write + * @param length the number of bytes to use to represent the value + * @throws IOException if writing fails + */ + public static void toLittleEndian(OutputStream out, final long value, final int length) + throws IOException { + // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object + long num = value; + for (int i = 0; i < length; i++) { + out.write((int) (num & 0xff)); + num >>= 8; + } + } + + /** + * Provides the given value to the given consumer as a little endian + * sequence of the given length. + * @param consumer the consumer to provide the bytes to + * @param value the value to provide + * @param length the number of bytes to use to represent the value + * @throws IOException if writing fails + */ + public static void toLittleEndian(ByteConsumer consumer, final long value, final int length) + throws IOException { + long num = value; + for (int i = 0; i < length; i++) { + consumer.accept((int) (num & 0xff)); + num >>= 8; + } + } + + /** + * Writes the given value to the given stream as a little endian + * array of the given length. + * @param out the output to write to + * @param value the value to write + * @param length the number of bytes to use to represent the value + * @throws IOException if writing fails + */ + public static void toLittleEndian(DataOutput out, final long value, final int length) + throws IOException { + // somewhat duplicates the ByteConsumer version in order to save the creation of a wrapper object + long num = value; + for (int i = 0; i < length; i++) { + out.write((int) (num & 0xff)); + num >>= 8; + } + } + + /** + * {@link ByteSupplier} based on {@link InputStream}. + * @since 1.14 + */ + public static class InputStreamByteSupplier implements ByteSupplier { + private final InputStream is; + public InputStreamByteSupplier(InputStream is) { + this.is = is; + } + @Override + public int getAsByte() throws IOException { + return is.read(); + } + } + + /** + * {@link ByteConsumer} based on {@link OutputStream}. + * @since 1.14 + */ + public static class OutputStreamByteConsumer implements ByteConsumer { + private final OutputStream os; + public OutputStreamByteConsumer(OutputStream os) { + this.os = os; + } + @Override + public void accept(int b) throws IOException { + os.write(b); + } + } + + private static final void checkReadLength(int length) { + if (length > 8) { + throw new IllegalArgumentException("can't read more than eight bytes into a long value"); + } + } +} |