blob: 7ab9de595908daf7d6b2d67c3b30489687795f07 [file] [log] [blame]
// Copyright 2017 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.skyframe.serialization;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;
import java.util.List;
/**
* Generic object serialization/deserialization. Implementations should serialize values
* deterministically.
*/
public interface ObjectCodec<T> {
/**
* Returns the class of the objects serialized/deserialized by this codec.
*
* <p>This is useful for automatically dispatching to the correct codec, e.g. in {@link
* ObjectCodecs}.
*
* <p>If {@link T} is an interface, then this codec will never be used by the auto-registration
* framework in {@link ObjectCodecRegistry} unless it is explicitly invoked or {@link
* #additionalEncodedClasses} is non-empty, since the {@link ObjectCodecRegistry} traverses the
* concrete class hierarchy looking for matches, and will never come to an interface.
*/
Class<? extends T> getEncodedClass();
/**
* Returns additional subtypes of {@code T} that may be serialized/deserialized using this codec
* without loss of information.
*
* <p>This method is intended for when {@code T} has multiple concrete implementations whose
* details are known to the codec but not to the codec dispatching mechanism. It signals that the
* dispatcher may choose to use this codec for the subtype, rather than raise {@link
* SerializationException.NoCodecException}.
*
* <p>This method should not be used if the codec's serialization and deserialization methods
* perform their own dispatching to other codecs for subtypes of {@code T}.
*
* <p>{@code T} itself should not be included in the returned list.
*/
default List<Class<? extends T>> additionalEncodedClasses() {
return ImmutableList.of();
}
/**
* Serializes {@code obj}, inverse of {@link #deserialize}.
*
* @param context {@link SerializationContext} providing additional information to the
* serialization process
* @param obj the object to serialize
* @param codedOut the {@link CodedOutputStream} to write this object into. Implementations need
* not call {@link CodedOutputStream#flush()}, this should be handled by the caller.
* @throws SerializationException on failure to serialize
* @throws IOException on {@link IOException} during serialization
*/
void serialize(SerializationContext context, T obj, CodedOutputStream codedOut)
throws SerializationException, IOException;
/**
* Deserializes from {@code codedIn}, inverse of {@link #serialize}.
*
* @param context {@link DeserializationContext} for providing additional information to the
* deserialization process.
* @param codedIn the {@link CodedInputStream} to read the serialized object from
* @return the object deserialized from {@code codedIn}
* @throws SerializationException on failure to deserialize
* @throws IOException on {@link IOException} during deserialization
*/
T deserialize(DeserializationContext context, CodedInputStream codedIn)
throws SerializationException, IOException;
/**
* Returns the memoization strategy for this codec.
*
* <p>If set to {@link MemoizationStrategy#MEMOIZE_BEFORE}, then {@link
* DeserializationContext#registerInitialValue} must be called first in the {@link #deserialize}
* method, before delegating to any other codecs.
*
* <p>Implementations of this method should just return a constant, since the choice of strategy
* is usually intrinsic to {@link T}.
*/
default MemoizationStrategy getStrategy() {
return MemoizationStrategy.MEMOIZE_AFTER;
}
/** Indicates how an {@link ObjectCodec} is memoized. */
enum MemoizationStrategy {
/**
* Indicates that memoization is not directly used by this codec.
*
* <p>Codecs with this strategy will always serialize payloads, never backreferences, even if
* the same value has been serialized before. This does not apply to other codecs that are
* delegated to within this codec. Deserialization behaves analogously.
*
* <p>This strategy is useful for codecs that write very little data themselves, but that still
* delegate to other codecs.
*/
DO_NOT_MEMOIZE,
/**
* Indicates that the value is memoized before recursing to its children, so that it is
* available to form cyclic references from its children. If this strategy is used, {@link
* DeserializationContext#registerInitialValue} must be called during the {@link #deserialize}
* method.
*
* <p>This should be used for all types where it is feasible to provide an initial value. Any
* cycle that does not go through at least one {@code MEMOIZE_BEFORE} type of value (e.g., a
* pathological self-referential tuple) is unserializable.
*/
MEMOIZE_BEFORE,
/**
* Indicates that the value is memoized after recursing to its children, so that it cannot be
* referred to until after it has been constructed (regardless of whether its children are still
* under construction).
*
* <p>This is typically used for immutable types, since they cannot be created by mutating an
* initial value.
*/
MEMOIZE_AFTER
}
}