001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 Franz Wilhelmstötter 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 * Author: 018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com) 019 */ 020package io.jenetics.internal.util; 021 022import static java.nio.charset.StandardCharsets.UTF_8; 023 024import java.io.DataInput; 025import java.io.DataOutput; 026import java.io.IOException; 027import java.io.ObjectInput; 028import java.io.ObjectOutput; 029import java.util.ArrayList; 030import java.util.Collection; 031import java.util.List; 032 033/** 034 * Helper methods needed for implementing the Java serializations. 035 * 036 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a> 037 * @version 5.0 038 * @since 5.0 039 */ 040public final class SerialIO { 041 042 private SerialIO() { 043 } 044 045 /** 046 * Object writer interface. 047 * 048 * @param <T> the object type 049 */ 050 @FunctionalInterface 051 public interface Writer<T> { 052 void write(final T value, final DataOutput out) throws IOException; 053 } 054 055 /** 056 * Object reader interface 057 * 058 * @param <T> the object type 059 */ 060 @FunctionalInterface 061 public interface Reader<T> { 062 T read(final DataInput in) throws IOException; 063 } 064 065 /** 066 * Write the given, possible {@code null}, {@code value} to the data output 067 * using the given {@code writer}. 068 * 069 * @param value the, possible {@code null}, value to write 070 * @param writer the object writer 071 * @param out the data output 072 * @param <T> the object type 073 * @throws NullPointerException if the {@code writer} or data output is 074 * {@code null} 075 * @throws IOException if an I/O error occurs 076 */ 077 public static <T> void writeNullable( 078 final T value, 079 final Writer<? super T> writer, 080 final DataOutput out 081 ) 082 throws IOException 083 { 084 out.writeBoolean(value != null); 085 if (value != null) { 086 writer.write(value, out); 087 } 088 } 089 090 /** 091 * Reads a possible {@code null} value from the given data input. 092 * 093 * @param reader the object reader 094 * @param in the data input 095 * @param <T> the object type 096 * @return the read object 097 * @throws NullPointerException if one of the given arguments is {@code null} 098 * @throws IOException if an I/O error occurs 099 */ 100 public static <T> T readNullable( 101 final Reader<? extends T> reader, 102 final DataInput in 103 ) 104 throws IOException 105 { 106 T value = null; 107 if (in.readBoolean()) { 108 value = reader.read(in); 109 } 110 111 return value; 112 } 113 114 /** 115 * Write the given string {@code value} to the given data output. 116 * 117 * @param value the string value to write 118 * @param out the data output 119 * @throws NullPointerException if one of the given arguments is {@code null} 120 * @throws IOException if an I/O error occurs 121 */ 122 public static void writeString(final String value, final DataOutput out) 123 throws IOException 124 { 125 final byte[] bytes = value.getBytes(UTF_8); 126 writeInt(bytes.length, out); 127 out.write(bytes); 128 } 129 130 /** 131 * Reads a string value from the given data input. 132 * 133 * @param in the data input 134 * @return the read string value 135 * @throws NullPointerException if one of the given arguments is {@code null} 136 * @throws IOException if an I/O error occurs 137 */ 138 public static String readString(final DataInput in) throws IOException { 139 final byte[] bytes = new byte[readInt(in)]; 140 in.readFully(bytes); 141 return new String(bytes, UTF_8); 142 } 143 144 /** 145 * Write the given string, possible {@code null}, {@code value} to the given 146 * data output. 147 * 148 * @param value the string value to write 149 * @param out the data output 150 * @throws NullPointerException if the given data output is {@code null} 151 * @throws IOException if an I/O error occurs 152 */ 153 public static void writeNullableString(final String value, final DataOutput out) 154 throws IOException 155 { 156 writeNullable(value, SerialIO::writeString, out); 157 } 158 159 /** 160 * Reads a, possible {@code null}, string value from the given data input. 161 * 162 * @param in the data input 163 * @return the read string value 164 * @throws NullPointerException if one of the given arguments is {@code null} 165 * @throws IOException if an I/O error occurs 166 */ 167 public static String readNullableString(final DataInput in) throws IOException { 168 return readNullable(SerialIO::readString, in); 169 } 170 171 /** 172 * Write the given elements to the data output. 173 * 174 * @param elements the elements to write 175 * @param writer the element writer 176 * @param out the data output 177 * @param <T> the element type 178 * @throws NullPointerException if one of the given arguments is {@code null} 179 * @throws IOException if an I/O error occurs 180 */ 181 public static <T> void writes( 182 final Collection<? extends T> elements, 183 final Writer<? super T> writer, 184 final DataOutput out 185 ) 186 throws IOException 187 { 188 writeInt(elements.size(), out); 189 for (T element : elements) { 190 writer.write(element, out); 191 } 192 } 193 194 /** 195 * Reads a list of elements from the given data input. 196 * 197 * @param reader the element reader 198 * @param in the data input 199 * @param <T> the element type 200 * @return the read element list 201 * @throws NullPointerException if one of the given arguments is {@code null} 202 * @throws IOException if an I/O error occurs 203 */ 204 public static <T> List<T> reads( 205 final Reader<? extends T> reader, 206 final DataInput in 207 ) 208 throws IOException 209 { 210 final int length = readInt(in); 211 final List<T> elements = new ArrayList<>(length); 212 for (int i = 0; i < length; ++i) { 213 elements.add(reader.read(in)); 214 } 215 return elements; 216 } 217 218 219 public static void writeBytes(final byte[] bytes, final DataOutput out) 220 throws IOException 221 { 222 writeInt(bytes.length, out); 223 out.write(bytes); 224 } 225 226 public static byte[] readBytes(final DataInput in) throws IOException { 227 final int length = readInt(in); 228 final byte[] bytes = new byte[length]; 229 in.readFully(bytes); 230 return bytes; 231 } 232 233 /** 234 * Writes an int value to a series of bytes. The values are written using 235 * <a href="http://lucene.apache.org/core/3_5_0/fileformats.html#VInt">variable-length</a> 236 * <a href="https://developers.google.com/protocol-buffers/docs/encoding?csw=1#types">zig-zag</a> 237 * coding. Each {@code int} value is written in 1 to 5 bytes. 238 * 239 * @see #readInt(DataInput) 240 * 241 * @param value the integer value to write 242 * @param out the data output the integer value is written to 243 * @throws NullPointerException if the given data output is {@code null} 244 * @throws IOException if an I/O error occurs 245 */ 246 public static void writeInt(final int value, final DataOutput out) 247 throws IOException 248 { 249 // Zig-zag encoding. 250 int n = (value << 1)^(value >> 31); 251 if ((n & ~0x7F) != 0) { 252 out.write((byte)((n | 0x80) & 0xFF)); 253 n >>>= 7; 254 if (n > 0x7F) { 255 out.write((byte)((n | 0x80) & 0xFF)); 256 n >>>= 7; 257 if (n > 0x7F) { 258 out.write((byte)((n | 0x80) & 0xFF)); 259 n >>>= 7; 260 if (n > 0x7F) { 261 out.write((byte)((n | 0x80) & 0xFF)); 262 n >>>= 7; 263 } 264 } 265 } 266 } 267 out.write((byte)n); 268 } 269 270 /** 271 * Reads an int value from the given data input. The integer value must have 272 * been written by the {@link #writeInt(int, DataOutput)} before. 273 * 274 * @see #writeInt(int, DataOutput) 275 * 276 * @param in the data input where the integer value is read from 277 * @return the read integer value 278 * @throws NullPointerException if the given data input is {@code null} 279 * @throws IOException if an I/O error occurs 280 */ 281 public static int readInt(final DataInput in) throws IOException { 282 int b = in.readByte() & 0xFF; 283 int n = b & 0x7F; 284 285 if (b > 0x7F) { 286 b = in.readByte() & 0xFF; 287 n ^= (b & 0x7F) << 7; 288 if (b > 0x7F) { 289 b = in.readByte() & 0xFF; 290 n ^= (b & 0x7F) << 14; 291 if (b > 0x7F) { 292 b = in.readByte() & 0xFF; 293 n ^= (b & 0x7F) << 21; 294 if (b > 0x7F) { 295 b = in.readByte() & 0xFF; 296 n ^= (b & 0x7F) << 28; 297 if (b > 0x7F) { 298 throw new IOException("Invalid int encoding."); 299 } 300 } 301 } 302 } 303 } 304 305 return (n >>> 1)^-(n & 1); 306 } 307 308 /** 309 * Writes a long value to a series of bytes. The values are written using 310 * <a href="http://lucene.apache.org/core/3_5_0/fileformats.html#VInt">variable-length</a> 311 * <a href="https://developers.google.com/protocol-buffers/docs/encoding?csw=1#types">zig-zag</a> 312 * coding. Each {@code long} value is written in 1 to 10 bytes. 313 * 314 * @see #readLong(DataInput) 315 * 316 * @param value the long value to write 317 * @param out the data output the integer value is written to 318 * @throws NullPointerException if the given data output is {@code null} 319 * @throws IOException if an I/O error occurs 320 */ 321 public static void writeLong(final long value, final DataOutput out) 322 throws IOException 323 { 324 // Zig-zag encoding. 325 long n = (value << 1)^(value >> 63); 326 if ((n & ~0x7FL) != 0) { 327 out.write((byte)((n | 0x80) & 0xFF)); 328 n >>>= 7; 329 if (n > 0x7F) { 330 out.write((byte)((n | 0x80) & 0xFF)); 331 n >>>= 7; 332 if (n > 0x7F) { 333 out.write((byte)((n | 0x80) & 0xFF)); 334 n >>>= 7; 335 if (n > 0x7F) { 336 out.write((byte)((n | 0x80) & 0xFF)); 337 n >>>= 7; 338 if (n > 0x7F) { 339 out.write((byte)((n | 0x80) & 0xFF)); 340 n >>>= 7; 341 if (n > 0x7F) { 342 out.write((byte)((n | 0x80) & 0xFF)); 343 n >>>= 7; 344 if (n > 0x7F) { 345 out.write((byte)((n | 0x80) & 0xFF)); 346 n >>>= 7; 347 if (n > 0x7F) { 348 out.write((byte)((n | 0x80) & 0xFF)); 349 n >>>= 7; 350 if (n > 0x7F) { 351 out.write((byte)((n | 0x80) & 0xFF)); 352 n >>>= 7; 353 } 354 } 355 } 356 } 357 } 358 } 359 } 360 } 361 } 362 out.write((byte)n); 363 } 364 365 /** 366 * Reads a long value from the given data input. The integer value must have 367 * been written by the {@link #writeInt(int, DataOutput)} before. 368 * 369 * @see #writeLong(long, DataOutput) 370 * 371 * @param in the data input where the integer value is read from 372 * @return the read long value 373 * @throws NullPointerException if the given data input is {@code null} 374 * @throws IOException if an I/O error occurs 375 */ 376 public static long readLong(final DataInput in) throws IOException { 377 int b = in.readByte() & 0xFF; 378 int n = b & 0x7F; 379 long l; 380 if (b > 0x7F) { 381 b = in.readByte() & 0xFF; 382 n ^= (b & 0x7F) << 7; 383 if (b > 0x7F) { 384 b = in.readByte() & 0xFF; 385 n ^= (b & 0x7F) << 14; 386 if (b > 0x7F) { 387 b = in.readByte() & 0xFF; 388 n ^= (b & 0x7F) << 21; 389 l = b > 0x7F ? innerLongDecode(n, in) : n; 390 } else { 391 l = n; 392 } 393 } else { 394 l = n; 395 } 396 } else { 397 l = n; 398 } 399 return (l >>> 1)^-(l & 1); 400 } 401 402 private static long innerLongDecode(long l, final DataInput in) 403 throws IOException 404 { 405 int b = in.readByte() & 0xFF; 406 l ^= (b & 0x7FL) << 28; 407 if (b > 0x7F) { 408 b = in.readByte() & 0xFF; 409 l ^= (b & 0x7FL) << 35; 410 if (b > 0x7F) { 411 b = in.readByte() & 0xFF; 412 l ^= (b & 0x7FL) << 42; 413 if (b > 0x7F) { 414 b = in.readByte() & 0xFF; 415 l ^= (b & 0x7FL) << 49; 416 if (b > 0x7F) { 417 b = in.readByte() & 0xFF; 418 l ^= (b & 0x7FL) << 56; 419 if (b > 0x7F) { 420 b = in.readByte() & 0xFF; 421 l ^= (b & 0x7FL) << 63; 422 if (b > 0x7F) { 423 throw new IOException("Invalid long encoding."); 424 } 425 } 426 } 427 } 428 } 429 } 430 return l; 431 } 432 433 /** 434 * Write the given {@code int[]} array to the given data output. 435 * 436 * @param values the values to write 437 * @param out the data sink 438 * @throws IOException if an I/O error occurs 439 */ 440 public static void writeIntArray(final int[] values, final DataOutput out) 441 throws IOException 442 { 443 writeInt(values.length, out); 444 for (int value : values) { 445 writeInt(value, out); 446 } 447 } 448 449 /** 450 * Write the given {@code char[]} array to the given data output. 451 * 452 * @param values the values to write 453 * @param out the data sink 454 * @throws IOException if an I/O error occurs 455 */ 456 public static void writeCharArray(final char[] values, final DataOutput out) 457 throws IOException 458 { 459 writeInt(values.length, out); 460 for (int value : values) { 461 writeInt(value, out); 462 } 463 } 464 465 /** 466 * Write the given {@code long[]} array to the given data output. 467 * 468 * @param values the values to write 469 * @param out the data sink 470 * @throws IOException if an I/O error occurs 471 */ 472 public static void writeLongArray(final long[] values, final DataOutput out) 473 throws IOException 474 { 475 writeInt(values.length, out); 476 for (long value : values) { 477 writeLong(value, out); 478 } 479 } 480 481 /** 482 * Write the given {@code double[]} array to the given data output. 483 * 484 * @param values the values to write 485 * @param out the data sink 486 * @throws IOException if an I/O error occurs 487 */ 488 public static void writeDoubleArray(final double[] values, final DataOutput out) 489 throws IOException 490 { 491 writeInt(values.length, out); 492 for (double value : values) { 493 out.writeDouble(value); 494 } 495 } 496 497 /** 498 * Read an {@code char[]} array from the data input. 499 * 500 * @param in the data source 501 * @return the read values 502 * @throws IOException if an I/O error occurs 503 */ 504 public static char[] readCharArray(final DataInput in) throws IOException { 505 final char[] values = new char[readInt(in)]; 506 for (int i = 0; i < values.length; ++i) { 507 values[i] = (char)readInt(in); 508 } 509 return values; 510 } 511 512 /** 513 * Read an {@code int[]} array from the data input. 514 * 515 * @param in the data source 516 * @return the read values 517 * @throws IOException if an I/O error occurs 518 */ 519 public static int[] readIntArray(final DataInput in) throws IOException { 520 final int[] values = new int[readInt(in)]; 521 for (int i = 0; i < values.length; ++i) { 522 values[i] = readInt(in); 523 } 524 return values; 525 } 526 527 /** 528 * Read a {@code long[]} array from the data input. 529 * 530 * @param in the data source 531 * @return the read values 532 * @throws IOException if an I/O error occurs 533 */ 534 public static long[] readLongArray(final DataInput in) throws IOException { 535 final long[] values = new long[readInt(in)]; 536 for (int i = 0; i < values.length; ++i) { 537 values[i] = readLong(in); 538 } 539 return values; 540 } 541 542 /** 543 * Read a {@code double[]} array from the data input. 544 * 545 * @param in the data source 546 * @return the read values 547 * @throws IOException if an I/O error occurs 548 */ 549 public static double[] readDoubleArray(final DataInput in) throws IOException { 550 final double[] values = new double[readInt(in)]; 551 for (int i = 0; i < values.length; ++i) { 552 values[i] = in.readDouble(); 553 } 554 return values; 555 } 556 557 /** 558 * Write the given {@code Object[]} array to the given data output. 559 * 560 * @param values the values to write 561 * @param out the data sink 562 * @throws IOException if an I/O error occurs 563 */ 564 public static void writeObjectArray(final Object[] values, final ObjectOutput out) 565 throws IOException 566 { 567 writeInt(values.length, out); 568 for (Object value : values) { 569 out.writeObject(value); 570 } 571 } 572 573 /** 574 * Read an {@code Object[]} array from the data input. 575 * 576 * @param in the data source 577 * @return the read values 578 * @throws IOException if an I/O error occurs 579 * @throws ClassNotFoundException if the class can't be loaded 580 */ 581 public static Object[] readObjectArray(final ObjectInput in) 582 throws IOException, ClassNotFoundException 583 { 584 final Object[] values = new Object[readInt(in)]; 585 for (int i = 0; i < values.length; ++i) { 586 values[i] = in.readObject(); 587 } 588 return values; 589 } 590 591}