com.swirlds.common.io.streams.SerializableDataOutputStream Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of swirlds-common Show documentation
Show all versions of swirlds-common Show documentation
Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.
/*
* Copyright (C) 2016-2024 Hedera Hashgraph, LLC
*
* 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.swirlds.common.io.streams;
import static com.swirlds.common.io.streams.SerializableStreamConstants.BOOLEAN_BYTES;
import static com.swirlds.common.io.streams.SerializableStreamConstants.CLASS_ID_BYTES;
import static com.swirlds.common.io.streams.SerializableStreamConstants.NULL_CLASS_ID;
import static com.swirlds.common.io.streams.SerializableStreamConstants.NULL_LIST_ARRAY_LENGTH;
import static com.swirlds.common.io.streams.SerializableStreamConstants.NULL_VERSION;
import static com.swirlds.common.io.streams.SerializableStreamConstants.SERIALIZATION_PROTOCOL_VERSION;
import static com.swirlds.common.io.streams.SerializableStreamConstants.VERSION_BYTES;
import com.swirlds.common.io.FunctionalSerialize;
import com.swirlds.common.io.SelfSerializable;
import com.swirlds.common.io.SerializableDet;
import com.swirlds.common.io.SerializableWithKnownLength;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* A drop-in replacement for {@link DataOutputStream}, which handles SerializableDet classes specially.
* It is designed for use with the SerializableDet interface, and its use is described there.
*/
public class SerializableDataOutputStream extends AugmentedDataOutputStream {
/**
* Creates a new data output stream to write data to the specified
* underlying output stream. The counter written
is
* set to zero.
*
* @param out
* the underlying output stream, to be saved for later
* use.
* @see java.io.FilterOutputStream#out
*/
public SerializableDataOutputStream(OutputStream out) {
super(out);
}
/**
* Write the serialization protocol version number to the stream. Should be used when serializing to a file that
* can be read by future versions.
*
* @throws IOException
* thrown if any IO problems occur
*/
public void writeProtocolVersion() throws IOException {
this.writeInt(SERIALIZATION_PROTOCOL_VERSION);
}
private void writeSerializable(
SelfSerializable serializable, boolean writeClassId, FunctionalSerialize serializeMethod)
throws IOException {
if (serializable == null) {
if (writeClassId) {
writeLong(NULL_CLASS_ID);
} else {
writeInt(NULL_VERSION);
}
return;
}
writeClassIdVersion(serializable, writeClassId);
serializeMethod.serialize(this);
}
/**
* Writes a {@link SelfSerializable} object to a stream. If the class is known at the time of deserialization, the
* the {@code writeClassId} param can be set to false. If the class might be unknown when deserializing, then the
* {@code writeClassId} must be written.
*
* @param serializable
* the object to serialize
* @param writeClassId
* whether to write the class ID or not
* @throws IOException
* thrown if any IO problems occur
*/
public void writeSerializable(SelfSerializable serializable, boolean writeClassId) throws IOException {
writeSerializable(serializable, writeClassId, serializable);
}
/**
* Writes a list of objects returned by an {@link Iterator} when the size in known ahead of time. If the class is
* known at the time of deserialization, the {@code writeClassId} param can be set to false. If the class might be
* unknown when deserializing, then the {@code writeClassId} must be written.
*
* @param iterator
* the iterator that returns the data
* @param size
* the size of the dataset
* @param writeClassId
* whether to write the class ID or not
* @param allSameClass
* should be set to true if all the objects in the list are the same class
* @param
* the type returned by the iterator
* @throws IOException
* thrown if any IO problems occur
*/
public void writeSerializableIterableWithSize(
Iterator iterator, int size, boolean writeClassId, boolean allSameClass) throws IOException {
this.writeInt(size);
if (size == 0) {
return;
}
writeBoolean(allSameClass);
// if the class ID and version is written only once, we need to write it when we come across
// the first non-null member, this variable will keep track of whether its written or not
boolean classIdVersionWritten = false;
while (iterator.hasNext()) {
SelfSerializable serializable = iterator.next();
if (!allSameClass) {
// if classes are different, we just write every class one by one
writeSerializable(serializable, writeClassId);
continue;
}
if (serializable == null) {
writeBoolean(true);
continue;
}
writeBoolean(false);
if (!classIdVersionWritten) {
// this is the first non-null member, so we write the ID and version
writeClassIdVersion(serializable, writeClassId);
classIdVersionWritten = true;
}
serializable.serialize(this);
}
}
/**
* Writes a list of {@link SelfSerializable} objects to the stream
*
* @param list
* the list to write, can be null
* @param writeClassId
* set to true if the classID should be written. This can be false if the class is known when
* de-serializing
* @param allSameClass
* should be set to true if all the objects in the list are the same class
* @param
* the class stored in the list
* @throws IOException
* thrown if any IO problems occur
*/
public void writeSerializableList(
List list, boolean writeClassId, boolean allSameClass) throws IOException {
if (list == null) {
this.writeInt(NULL_LIST_ARRAY_LENGTH);
return;
}
writeSerializableIterableWithSize(list.iterator(), list.size(), writeClassId, allSameClass);
}
/**
* Writes an array of {@link SelfSerializable} objects to the stream
*
* @param array
* the array to write, can be null
* @param writeClassId
* set to true if the classID should be written. This can be false if the class is known when
* de-serializing
* @param allSameClass
* should be set to true if all the objects in the list are the same class
* @param
* the class stored in the list
* @throws IOException
* thrown if any IO problems occur
*/
public void writeSerializableArray(
T[] array, boolean writeClassId, boolean allSameClass) throws IOException {
if (array == null) {
writeSerializableList(null, writeClassId, allSameClass);
} else {
writeSerializableList(Arrays.asList(array), writeClassId, allSameClass);
}
}
/**
* Get the serialized byte length an array of {@link SerializableWithKnownLength} objects
*
* @param array
* the array to write, can be null
* @param writeClassId
* set to true if the classID should be written. This can be false if the class is known when
* de-serializing
* @param allSameClass
* should be set to true if all the objects in the array are the same class
* @param
* the class stored in the array
*/
public static int getSerializedLength(
final T[] array, final boolean writeClassId, final boolean allSameClass) {
int totalByteLength = Integer.BYTES; // length of array size
if (array == null || array.length == 0) {
return totalByteLength;
}
totalByteLength += BOOLEAN_BYTES;
boolean classIdVersionWritten = false;
for (final T t : array) {
if (!allSameClass) {
totalByteLength += getInstanceSerializedLength(t, true, writeClassId);
continue;
}
if (t == null) {
totalByteLength += BOOLEAN_BYTES;
continue;
}
totalByteLength += BOOLEAN_BYTES;
if (!classIdVersionWritten) {
// this is the first non-null member, so we write the ID and version
totalByteLength += VERSION_BYTES;
if (writeClassId) {
totalByteLength += CLASS_ID_BYTES;
}
classIdVersionWritten = true;
}
// version and class info already written
totalByteLength += getInstanceSerializedLength(t, false, false);
}
return totalByteLength;
}
/**
* Get the serialized byte length of {@link SerializableWithKnownLength} object
*
* @param data
* array to write, should not be null
* @param writeVersion
* set to true if the version will be serialized
* @param writeClassId
* set to true if the classID should be written. This can be false if the class is known when
* de-serializing
*/
public static int getInstanceSerializedLength(
final T data, final boolean writeVersion, final boolean writeClassId) {
if (data == null) {
return writeClassId ? CLASS_ID_BYTES : writeVersion ? VERSION_BYTES : 0;
}
int totalByteLength = 0;
if (writeClassId) {
totalByteLength += CLASS_ID_BYTES;
}
if (writeVersion) {
totalByteLength += VERSION_BYTES; // version integer
}
totalByteLength += data.getSerializedLength(); // data its own content serialized length
return totalByteLength;
}
/** This method assumes serializable is not null */
protected void writeClassIdVersion(final SerializableDet serializable, final boolean writeClassId)
throws IOException {
if (writeClassId) {
this.writeLong(serializable.getClassId());
}
this.writeInt(serializable.getVersion());
}
}