aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/skyframe/serialization/ObjectCodecs.java
blob: f7bf43360b46a9e4c84ce1e5d0b70e30818469b5 (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// 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.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.ByteString;
import com.google.protobuf.CodedInputStream;
import com.google.protobuf.CodedOutputStream;
import java.io.IOException;

/**
 * Wrapper for the minutiae of serializing and deserializing objects using {@link ObjectCodec}s,
 * serving as a layer between the streaming-oriented {@link ObjectCodec} interface and users.
 */
public class ObjectCodecs {
  private final SerializationContext serializationContext;
  private final DeserializationContext deserializationContext;

  /**
   * Creates an instance using the supplied {@link ObjectCodecRegistry} for looking up {@link
   * ObjectCodec}s.
   */
  public ObjectCodecs(
      ObjectCodecRegistry codecRegistry, ImmutableMap<Class<?>, Object> dependencies) {
    serializationContext = new SerializationContext(codecRegistry, dependencies);
    deserializationContext = new DeserializationContext(codecRegistry, dependencies);
  }

  public ObjectCodecs(ObjectCodecRegistry codecRegistry) {
    this(codecRegistry, ImmutableMap.of());
  }

  @VisibleForTesting
  public SerializationContext getSerializationContext() {
    return serializationContext;
  }

  @VisibleForTesting
  public DeserializationContext getDeserializationContext() {
    return deserializationContext;
  }

  public ByteString serialize(Object subject) throws SerializationException {
    return serializeToByteString(subject, this::serialize);
  }

  public void serialize(Object subject, CodedOutputStream codedOut) throws SerializationException {
    serializeImpl(subject, codedOut, serializationContext);
  }

  public ByteString serializeMemoized(Object subject) throws SerializationException {
    return serializeToByteString(subject, this::serializeMemoized);
  }

  public SerializationResult<ByteString> serializeMemoizedAndBlocking(Object subject)
      throws SerializationException {
    SerializationContext memoizingContext =
        serializationContext.getMemoizingAndBlockingOnWriteContext();
    ByteString byteString =
        serializeToByteString(
            subject, (subj, codedOut) -> serializeImpl(subj, codedOut, memoizingContext));
    return SerializationResult.create(byteString, memoizingContext.createFutureToBlockWritingOn());
  }

  public void serializeMemoized(Object subject, CodedOutputStream codedOut)
      throws SerializationException {
    serializeImpl(subject, codedOut, serializationContext.getMemoizingContext());
  }

  public Object deserialize(ByteString data) throws SerializationException {
    return deserialize(data.newCodedInput());
  }

  public Object deserialize(CodedInputStream codedIn) throws SerializationException {
    return deserializeImpl(codedIn, /*memoize=*/ false);
  }

  public Object deserializeMemoized(ByteString data) throws SerializationException {
    return deserializeMemoized(data.newCodedInput());
  }

  public Object deserializeMemoized(CodedInputStream codedIn) throws SerializationException {
    return deserializeImpl(codedIn, /*memoize=*/ true);
  }

  private void serializeImpl(
      Object subject, CodedOutputStream codedOut, SerializationContext serializationContext)
      throws SerializationException {
    try {
      serializationContext.serialize(subject, codedOut);
    } catch (IOException e) {
      throw new SerializationException("Failed to serialize " + subject, e);
    }
  }

  private Object deserializeImpl(CodedInputStream codedIn, boolean memoize)
      throws SerializationException {
    // Allows access to buffer without copying (although this means buffer may be pinned in memory).
    codedIn.enableAliasing(true);
    try {
      if (memoize) {
        return deserializationContext.getMemoizingContext().deserialize(codedIn);
      } else {
        return deserializationContext.deserialize(codedIn);
      }
    } catch (IOException e) {
      throw new SerializationException("Failed to deserialize data", e);
    }
  }

  @FunctionalInterface
  private static interface SerializeCall {
    void serialize(Object subject, CodedOutputStream codedOut) throws SerializationException;
  }

  private static ByteString serializeToByteString(Object subject, SerializeCall wrapped)
      throws SerializationException {
    ByteString.Output resultOut = ByteString.newOutput();
    CodedOutputStream codedOut = CodedOutputStream.newInstance(resultOut);
    wrapped.serialize(subject, codedOut);
    try {
      codedOut.flush();
      return resultOut.toByteString();
    } catch (IOException e) {
      throw new SerializationException("Failed to serialize " + subject, e);
    }
  }
}