blob: b948b177b574f6ccd0cd1df62fe9433d21f2efe5 [file] [log] [blame]
/*
* 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));
}
}