/* * Java Genetic Algorithm Library (@[email protected]). * Copyright (c) @[email protected] Franz Wilhelmstötter * * Licensed 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. * * Author: * Franz Wilhelmstötter ([email protected]) */ package io.jenetics.internal.util; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * Helper methods needed for implementing the Java serializations. * * @author <a href="mailto:[email protected]">Franz Wilhelmstötter</a> * @version 5.0 * @since 5.0 */ public final class SerialIO { private SerialIO() { } /** * Object writer interface. * * @param <T> the object type */ @FunctionalInterface public interface Writer<T> { void write(final T value, final DataOutput out) throws IOException; } /** * Object reader interface * * @param <T> the object type */ @FunctionalInterface public interface Reader<T> { T read(final DataInput in) throws IOException; } /** * Write the given, possible {@code null}, {@code value} to the data output * using the given {@code writer}. * * @param value the, possible {@code null}, value to write * @param writer the object writer * @param out the data output * @param <T> the object type * @throws NullPointerException if the {@code writer} or data output is * {@code null} * @throws IOException if an I/O error occurs */ public static <T> void writeNullable( final T value, final Writer<? super T> writer, final DataOutput out ) throws IOException { out.writeBoolean(value != null); if (value != null) { writer.write(value, out); } } /** * Reads a possible {@code null} value from the given data input. * * @param reader the object reader * @param in the data input * @param <T> the object type * @return the read object * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static <T> T readNullable( final Reader<? extends T> reader, final DataInput in ) throws IOException { T value = null; if (in.readBoolean()) { value = reader.read(in); } return value; } /** * Write the given string {@code value} to the given data output. * * @param value the string value to write * @param out the data output * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static void writeString(final String value, final DataOutput out) throws IOException { final byte[] bytes = value.getBytes(UTF_8); writeInt(bytes.length, out); out.write(bytes); } /** * Reads a string value from the given data input. * * @param in the data input * @return the read string value * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static String readString(final DataInput in) throws IOException { final byte[] bytes = new byte[readInt(in)]; in.readFully(bytes); return new String(bytes, UTF_8); } /** * Write the given string, possible {@code null}, {@code value} to the given * data output. * * @param value the string value to write * @param out the data output * @throws NullPointerException if the given data output is {@code null} * @throws IOException if an I/O error occurs */ public static void writeNullableString(final String value, final DataOutput out) throws IOException { writeNullable(value, SerialIO::writeString, out); } /** * Reads a, possible {@code null}, string value from the given data input. * * @param in the data input * @return the read string value * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static String readNullableString(final DataInput in) throws IOException { return readNullable(SerialIO::readString, in); } /** * Write the given elements to the data output. * * @param elements the elements to write * @param writer the element writer * @param out the data output * @param <T> the element type * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static <T> void writes( final Collection<? extends T> elements, final Writer<? super T> writer, final DataOutput out ) throws IOException { writeInt(elements.size(), out); for (T element : elements) { writer.write(element, out); } } /** * Reads a list of elements from the given data input. * * @param reader the element reader * @param in the data input * @param <T> the element type * @return the read element list * @throws NullPointerException if one of the given arguments is {@code null} * @throws IOException if an I/O error occurs */ public static <T> List<T> reads( final Reader<? extends T> reader, final DataInput in ) throws IOException { final int length = readInt(in); final List<T> elements = new ArrayList<>(length); for (int i = 0; i < length; ++i) { elements.add(reader.read(in)); } return elements; } public static void writeBytes(final byte[] bytes, final DataOutput out) throws IOException { writeInt(bytes.length, out); out.write(bytes); } public static byte[] readBytes(final DataInput in) throws IOException { final int length = readInt(in); final byte[] bytes = new byte[length]; in.readFully(bytes); return bytes; } /** * Writes an int value to a series of bytes. The values are written using * <a href="http://lucene.apache.org/core/3_5_0/fileformats.html#VInt">variable-length</a> * <a href="https://developers.google.com/protocol-buffers/docs/encoding?csw=1#types">zig-zag</a> * coding. Each {@code int} value is written in 1 to 5 bytes. * * @see #readInt(DataInput) * * @param value the integer value to write * @param out the data output the integer value is written to * @throws NullPointerException if the given data output is {@code null} * @throws IOException if an I/O error occurs */ public static void writeInt(final int value, final DataOutput out) throws IOException { // Zig-zag encoding. int n = (value << 1)^(value >> 31); if ((n & ~0x7F) != 0) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; } } } } out.write((byte)n); } /** * Reads an int value from the given data input. The integer value must have * been written by the {@link #writeInt(int, DataOutput)} before. * * @see #writeInt(int, DataOutput) * * @param in the data input where the integer value is read from * @return the read integer value * @throws NullPointerException if the given data input is {@code null} * @throws IOException if an I/O error occurs */ public static int readInt(final DataInput in) throws IOException { int b = in.readByte() & 0xFF; int n = b & 0x7F; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 7; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 14; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 21; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 28; if (b > 0x7F) { throw new IOException("Invalid int encoding."); } } } } } return (n >>> 1)^-(n & 1); } /** * Writes a long value to a series of bytes. The values are written using * <a href="http://lucene.apache.org/core/3_5_0/fileformats.html#VInt">variable-length</a> * <a href="https://developers.google.com/protocol-buffers/docs/encoding?csw=1#types">zig-zag</a> * coding. Each {@code long} value is written in 1 to 10 bytes. * * @see #readLong(DataInput) * * @param value the long value to write * @param out the data output the integer value is written to * @throws NullPointerException if the given data output is {@code null} * @throws IOException if an I/O error occurs */ public static void writeLong(final long value, final DataOutput out) throws IOException { // Zig-zag encoding. long n = (value << 1)^(value >> 63); if ((n & ~0x7FL) != 0) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; if (n > 0x7F) { out.write((byte)((n | 0x80) & 0xFF)); n >>>= 7; } } } } } } } } } out.write((byte)n); } /** * Reads a long value from the given data input. The integer value must have * been written by the {@link #writeInt(int, DataOutput)} before. * * @see #writeLong(long, DataOutput) * * @param in the data input where the integer value is read from * @return the read long value * @throws NullPointerException if the given data input is {@code null} * @throws IOException if an I/O error occurs */ public static long readLong(final DataInput in) throws IOException { int b = in.readByte() & 0xFF; int n = b & 0x7F; long l; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 7; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 14; if (b > 0x7F) { b = in.readByte() & 0xFF; n ^= (b & 0x7F) << 21; l = b > 0x7F ? innerLongDecode(n, in) : n; } else { l = n; } } else { l = n; } } else { l = n; } return (l >>> 1)^-(l & 1); } private static long innerLongDecode(long l, final DataInput in) throws IOException { int b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 28; if (b > 0x7F) { b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 35; if (b > 0x7F) { b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 42; if (b > 0x7F) { b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 49; if (b > 0x7F) { b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 56; if (b > 0x7F) { b = in.readByte() & 0xFF; l ^= (b & 0x7FL) << 63; if (b > 0x7F) { throw new IOException("Invalid long encoding."); } } } } } } return l; } /** * Write the given {@code int[]} array to the given data output. * * @param values the values to write * @param out the data sink * @throws IOException if an I/O error occurs */ public static void writeIntArray(final int[] values, final DataOutput out) throws IOException { writeInt(values.length, out); for (int value : values) { writeInt(value, out); } } /** * Write the given {@code char[]} array to the given data output. * * @param values the values to write * @param out the data sink * @throws IOException if an I/O error occurs */ public static void writeCharArray(final char[] values, final DataOutput out) throws IOException { writeInt(values.length, out); for (int value : values) { writeInt(value, out); } } /** * Write the given {@code long[]} array to the given data output. * * @param values the values to write * @param out the data sink * @throws IOException if an I/O error occurs */ public static void writeLongArray(final long[] values, final DataOutput out) throws IOException { writeInt(values.length, out); for (long value : values) { writeLong(value, out); } } /** * Write the given {@code double[]} array to the given data output. * * @param values the values to write * @param out the data sink * @throws IOException if an I/O error occurs */ public static void writeDoubleArray(final double[] values, final DataOutput out) throws IOException { writeInt(values.length, out); for (double value : values) { out.writeDouble(value); } } /** * Read an {@code char[]} array from the data input. * * @param in the data source * @return the read values * @throws IOException if an I/O error occurs */ public static char[] readCharArray(final DataInput in) throws IOException { final char[] values = new char[readInt(in)]; for (int i = 0; i < values.length; ++i) { values[i] = (char)readInt(in); } return values; } /** * Read an {@code int[]} array from the data input. * * @param in the data source * @return the read values * @throws IOException if an I/O error occurs */ public static int[] readIntArray(final DataInput in) throws IOException { final int[] values = new int[readInt(in)]; for (int i = 0; i < values.length; ++i) { values[i] = readInt(in); } return values; } /** * Read a {@code long[]} array from the data input. * * @param in the data source * @return the read values * @throws IOException if an I/O error occurs */ public static long[] readLongArray(final DataInput in) throws IOException { final long[] values = new long[readInt(in)]; for (int i = 0; i < values.length; ++i) { values[i] = readLong(in); } return values; } /** * Read a {@code double[]} array from the data input. * * @param in the data source * @return the read values * @throws IOException if an I/O error occurs */ public static double[] readDoubleArray(final DataInput in) throws IOException { final double[] values = new double[readInt(in)]; for (int i = 0; i < values.length; ++i) { values[i] = in.readDouble(); } return values; } /** * Write the given {@code Object[]} array to the given data output. * * @param values the values to write * @param out the data sink * @throws IOException if an I/O error occurs */ public static void writeObjectArray(final Object[] values, final ObjectOutput out) throws IOException { writeInt(values.length, out); for (Object value : values) { out.writeObject(value); } } /** * Read an {@code Object[]} array from the data input. * * @param in the data source * @return the read values * @throws IOException if an I/O error occurs */ public static Object[] readObjectArray(final ObjectInput in) throws IOException, ClassNotFoundException { final Object[] values = new Object[readInt(in)]; for (int i = 0; i < values.length; ++i) { values[i] = in.readObject(); } return values; } }