blob: 952dcf09cc32643e92985f4fd8093bf08d0da222 [file] [log] [blame]
// Copyright 2018 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.base.Preconditions;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
/**
* Specialized {@link ObjectCodec} for storing singleton values. Values serialize to a supplied
* representation, which is useful for debugging and is used to verify the serialized representation
* during deserialization.
*/
public class SingletonCodec<T> implements ObjectCodec<T> {
/**
* Create instance wrapping the singleton {@code value}. Will serialize to the byte array
* representation of {@code mnemonic}. On deserialization if {@code mnemonic} matches the
* serialized data then {@code value} is returned.
*/
public static <T> SingletonCodec<T> of(T value, String mnemonic) {
return new SingletonCodec<T>(value, mnemonic);
}
private final T value;
private final byte[] mnemonic;
private SingletonCodec(T value, String mnemonic) {
this.value = Preconditions.checkNotNull(value, "SingletonCodec cannot represent null");
this.mnemonic = mnemonic.getBytes(StandardCharsets.UTF_8);
}
@SuppressWarnings("unchecked")
@Override
public Class<T> getEncodedClass() {
return (Class<T>) value.getClass();
}
@Override
public void serialize(SerializationContext context, T t, CodedOutputStream codedOut)
throws IOException {
// TODO(michajlo): See how usefully mnemonic actually winds up being for debugging, we may
// want to just toss it and trust that the classifier for this value is good enough.
codedOut.writeByteArrayNoTag(mnemonic);
}
@Override
public T deserialize(DeserializationContext context, CodedInputStream codedIn)
throws SerializationException, IOException {
// Get ByteBuffer instead of raw bytes, as it may be a direct view of the data and not a copy,
// which is much more efficient.
ByteBuffer readMnemonic = codedIn.readByteBuffer();
if (!bytesEqual(mnemonic, readMnemonic)) {
throw new SerializationException(
"Failed to decode singleton " + value + " expected " + Arrays.toString(mnemonic));
}
return value;
}
private static boolean bytesEqual(byte[] expected, ByteBuffer buffer) {
if (buffer.remaining() != expected.length) {
return false;
}
for (int i = 0; i < expected.length; i++) {
if (expected[i] != buffer.get(i)) {
return false;
}
}
return true;
}
}