| /* |
| * 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); |
| } |
| } |