aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/com/code_intelligence/jazzer/mutation/api/Serializer.java
blob: b948b177b574f6ccd0cd1df62fe9433d21f2efe5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
 * Copyright 2023 Code Intelligence GmbH
 *
 * 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.
 */

package com.code_intelligence.jazzer.mutation.api;

import static com.code_intelligence.jazzer.mutation.support.InputStreamSupport.extendWithZeros;

import com.google.errorprone.annotations.CheckReturnValue;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Serializes and deserializes values of type {@code T>} to and from (in-memory or on disk) corpus
 * entries.
 *
 * <p>Binary representations must by default be self-delimiting. For variable-length types, the
 * {@link #readExclusive(InputStream)} and {@link #writeExclusive(Object, OutputStream)} methods can
 * optionally be overriden to implement more compact representations that align with existing binary
 * corpus entries. For example, a {@code Serializer<byte[]>} could implement these optional methods
 * to read and write the raw bytes without preceding length information whenever it is used in an
 * already delimited context.
 */
public interface Serializer<T> extends Detacher<T> {
  /**
   * Reads a {@code T} from an endless stream that is eventually 0.
   *
   * <p>Implementations
   * <ul>
   *   <li>MUST not attempt to consume the entire stream;
   *   <li>MUST return a valid {@code T} and not throw for any (even garbage) stream;
   *   <li>SHOULD short-circuit the creation of nested structures upon reading null bytes.
   * </ul>
   *
   * @param in an endless stream that eventually only reads null bytes
   * @return a {@code T} constructed from the bytes read
   * @throws IOException declared, but must not be thrown by implementations unless methods called
   *                     on {@code in} do
   */
  @CheckReturnValue T read(DataInputStream in) throws IOException;

  /**
   * Writes a {@code T} to a stream in such a way that an equal object can be recovered from the
   * written bytes via {@link #read(DataInputStream)}.
   *
   * <p>Since {@link #read(DataInputStream)} is called with an endless stream, the binary
   * representation MUST be self-delimiting. For example, when writing out a list, first write its
   * length.
   *
   * @param value the value to write
   * @param out   the stream to write to
   * @throws IOException declared, but must not be thrown by implementations unless methods called
   *                     on {@code out} do
   */
  void write(T value, DataOutputStream out) throws IOException;

  /**
   * Reads a {@code T} from a finite stream, potentially using a simpler representation than that
   * read by {@link #read(DataInputStream)}.
   *
   * <p>The default implementations call extends the stream with null bytes and then calls
   * {@link #read(DataInputStream)}.
   *
   * <p>Implementations
   * <ul>
   *   <li>MUST return a valid {@code T} and not throw for any (even garbage) stream;
   *   <li>SHOULD short-circuit the creation of nested structures upon reading null bytes;
   *   <li>SHOULD naturally consume the entire stream.
   * </ul>
   *
   * @param in a finite stream
   * @return a {@code T} constructed from the bytes read
   * @throws IOException declared, but must not be thrown by implementations unless methods called
   *                     on {@code in} do
   */
  @CheckReturnValue
  default T readExclusive(InputStream in) throws IOException {
    return read(new DataInputStream(extendWithZeros(in)));
  }

  /**
   * Writes a {@code T} to a stream in such a way that an equal object can be recovered from the
   * written bytes via {@link #readExclusive(InputStream)}.
   *
   * <p>The default implementations calls through to {@link #read(DataInputStream)} and should only
   * be overriden if {@link #readExclusive(InputStream)} is.
   *
   * <p>As opposed to {@link #read(DataInputStream)}, {@link #readExclusive(InputStream)} is called
   * with a finite stream. The binary representation of a {@code T} value thus does not have to be
   * self-delimiting, which can allow for simpler representations. For example, a {@code byte[]} can
   * be written to the stream without prepending its length.
   *
   * @param value the value to write
   * @param out   the stream to write to
   * @throws IOException declared, but must not be thrown by implementations unless methods called
   *                     on {@code out} do
   */
  default void writeExclusive(T value, OutputStream out) throws IOException {
    write(value, new DataOutputStream(out));
  }
}