blob: ca5c4021efa7913000fe7b8453d72ec4f781a281 [file] [log] [blame]
/*
* 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.jackrabbit.oak.commons;
import java.io.Closeable;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.Locale;
/**
* Input/output utility methods.
*/
public final class IOUtils {
/**
* Avoid instantiation
*/
private IOUtils() {
}
/**
* Try to read the given number of bytes to the buffer. This method reads
* until the maximum number of bytes have been read or until the end of
* file.
*
* @param in the input stream
* @param buffer the output buffer
* @param off the offset in the buffer
* @param max the number of bytes to read at most
* @return the number of bytes read, 0 meaning EOF or no space in buffer
* @throws java.io.IOException If an error occurs.
*/
public static int readFully(InputStream in, byte[] buffer, int off, int max) throws IOException {
int len = Math.min(max, buffer.length);
int result = 0;
while (len > 0) {
int l = in.read(buffer, off, len);
if (l < 0) {
break;
}
result += l;
off += l;
len -= l;
}
return result;
}
/**
* Try to read the given number of bytes starting at the specified position
* into the buffer. This method reads until the maximum number of bytes have
* been read or until the end of the channel.
*
* @param channel the input channel
* @param position the position to start reading from the channel
* @param buffer the output buffer
* @return the number of bytes read, 0 meaning EOF or no space in buffer
* @throws java.io.IOException If an error occurs.
*/
public static int readFully(FileChannel channel, int position, ByteBuffer buffer)
throws IOException {
int result = 0;
while (buffer.remaining() > 0) {
int count = channel.read(buffer, position);
if (count < 0) {
break;
}
result += count;
position += count;
}
return result;
}
/**
* Skip a number of bytes in an input stream.
*
* @param in the input stream
* @param skip the number of bytes to skip
* @throws EOFException if the end of file has been reached before all bytes
* could be skipped
* @throws IOException if an IO exception occurred while skipping
*/
public static void skipFully(InputStream in, long skip) throws IOException {
while (skip > 0) {
long skipped = in.skip(skip);
if (skipped <= 0) {
throw new EOFException();
}
skip -= skipped;
}
}
/**
* Write a String. This will first write the length as 4 bytes, and then the
* UTF-8 encoded string.
*
* @param out the data output stream
* @param s the string (maximum length about 2 GB)
* @throws IOException if an IO exception occurred while writing
*/
public static void writeString(OutputStream out, String s) throws IOException {
writeBytes(out, s.getBytes("UTF-8"));
}
/**
* Read a String. This will first read the length as 4 bytes, and then the
* UTF-8 encoded string.
*
* @param in the data input stream
* @return the string
* @throws IOException if an IO exception occurred while reading
*/
public static String readString(InputStream in) throws IOException {
return new String(readBytes(in), "UTF-8");
}
/**
* Write a byte array. This will first write the length as 4 bytes, and then
* the actual bytes.
*
* @param out the data output stream
* @param data the byte array
* @throws IOException if an IO exception occurred while writing.
*/
public static void writeBytes(OutputStream out, byte[] data) throws IOException {
writeVarInt(out, data.length);
out.write(data);
}
/**
* Read a byte array. This will first read the length as 4 bytes, and then
* the actual bytes.
*
* @param in the data input stream
* @return the bytes
* @throws IOException if an IO exception occurred while reading from the stream.
*/
public static byte[] readBytes(InputStream in) throws IOException {
int len = readVarInt(in);
byte[] data = new byte[len];
for (int pos = 0; pos < len;) {
int l = in.read(data, pos, data.length - pos);
if (l < 0) {
throw new EOFException();
}
pos += l;
}
return data;
}
/**
* Write a variable size integer. Negative values need 5 bytes.
*
* @param out the output stream
* @param x the value
* @throws IOException if an IO exception occurred while writing.
*/
public static void writeVarInt(OutputStream out, int x) throws IOException {
while ((x & ~0x7f) != 0) {
out.write((x & 0x7f) | 0x80);
x >>>= 7;
}
out.write(x);
}
/**
* Read a variable size integer.
*
* @param in the input stream
* @return the integer
* @throws IOException if an IO exception occurred while reading.
*/
public static int readVarInt(InputStream in) throws IOException {
int x = (byte) in.read();
if (x >= 0) {
return x;
}
x &= 0x7f;
for (int s = 7;; s += 7) {
int b = in.read();
if (b < 0) {
throw new EOFException();
}
b = (byte) b;
x |= (b & 0x7f) << s;
if (b >= 0) {
return x;
}
}
}
/**
* Write a variable size long.
* Negative values need 10 bytes.
*
* @param out the output stream
* @param x the value
* @throws IOException if an IO exception occurred while writing.
*/
public static void writeVarLong(OutputStream out, long x) throws IOException {
while ((x & ~0x7f) != 0) {
out.write((byte) ((x & 0x7f) | 0x80));
x >>>= 7;
}
out.write((byte) x);
}
/**
* Write a long (8 bytes).
*
* @param out the output stream
* @param x the value
* @throws IOException if an IO exception occurred while writing.
*/
public static void writeLong(OutputStream out, long x) throws IOException {
writeInt(out, (int) (x >>> 32));
writeInt(out, (int) x);
}
/**
* Read a long (8 bytes).
*
* @param in the input stream
* @return the value
* @throws IOException if an IO exception occurred while reading.
*/
public static long readLong(InputStream in) throws IOException {
return ((long) (readInt(in)) << 32) + (readInt(in) & 0xffffffffL);
}
/**
* Write an integer (4 bytes).
*
* @param out the output stream
* @param x the value
* @throws IOException if an IO exception occurred while writing.
*/
public static void writeInt(OutputStream out, int x) throws IOException {
out.write((byte) (x >> 24));
out.write((byte) (x >> 16));
out.write((byte) (x >> 8));
out.write((byte) x);
}
/**
* Read an integer (4 bytes).
*
* @param in the input stream
* @return the value
* @throws IOException if an IO exception occurred while reading.
*/
public static int readInt(InputStream in) throws IOException {
return ((in.read() & 0xff) << 24) +
((in.read() & 0xff) << 16) +
((in.read() & 0xff) << 8) +
(in.read() & 0xff);
}
/**
* Read a variable size long.
*
* @param in the input stream
* @return the long
* @throws IOException if an IO exception occurred while reading.
*/
public static long readVarLong(InputStream in) throws IOException {
long x = (byte) in.read();
if (x >= 0) {
return x;
}
x &= 0x7f;
for (int s = 7;; s += 7) {
long b = in.read();
if (b < 0) {
throw new EOFException();
}
b = (byte) b;
x |= (b & 0x7f) << s;
if (b >= 0) {
return x;
}
}
}
/**
* Get the value that is equal or higher than this value, and that is a
* power of two. The returned value will be in the range [0, 2^31].
* If the input is less than zero, the result of 1 is returned (powers of
* negative numbers are not integer values).
*
* @param x the original value.
* @return the next power of two value. Results are always in the
* range [0, 2^31].
*/
public static long nextPowerOf2(int x) {
long i = 1;
while (i < x) {
i += i;
}
return i;
}
/**
* Unconditionally close a {@code Closeable}.
* <p>
* Equivalent to {@link Closeable#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param closeable the object to close, may be null or already closed
*/
public static void closeQuietly(Closeable closeable) {
try {
if (closeable != null) {
closeable.close();
}
} catch (IOException ioe) {
// ignore
}
}
/**
* Unconditionally close a {@code Socket}.
* <p>
* Equivalent to {@link Socket#close()}, except any exceptions will be ignored.
* This is typically used in finally blocks.
*
* @param sock the Socket to close, may be null or already closed
*/
public static void closeQuietly(Socket sock) {
if (sock != null) {
try {
sock.close();
} catch (IOException ioe) {
// ignored
}
}
}
/**
* Copy bytes from an {@code InputStream} to an
* {@code OutputStream}.
* <p>
* This method buffers the input internally, so there is no need to use a
* {@code BufferedInputStream}.
*
* @param input the {@code InputStream} to read from
* @param output the {@code OutputStream} to write to
* @return the number of bytes copied
* @throws IOException if an I/O error occurs
*/
public static long copy(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[4096];
long count = 0;
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
count += n;
}
return count;
}
/**
* Returns a human-readable version of the file size, where the input represents
* a specific number of bytes. Based on http://stackoverflow.com/a/3758880/1035417
*/
public static String humanReadableByteCount(long bytes) {
if (bytes < 0) {
return "0";
}
int unit = 1000;
if (bytes < unit) {
return bytes + " B";
}
int exp = (int) (Math.log(bytes) / Math.log(unit));
char pre = "kMGTPE".charAt(exp - 1);
return String.format(Locale.ENGLISH, "%.1f %sB", bytes / Math.pow(unit, exp), pre);
}
}