ss: Move the safe byte buffer to datastore plugin
[deliverable/tracecompass.git] / statesystem / org.eclipse.tracecompass.statesystem.core / src / org / eclipse / tracecompass / internal / provisional / statesystem / core / statevalue / CustomStateValue.java
1 /*******************************************************************************
2 * Copyright (c) 2016 École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *******************************************************************************/
9
10 package org.eclipse.tracecompass.internal.provisional.statesystem.core.statevalue;
11
12 import java.nio.BufferOverflowException;
13
14 import org.eclipse.jdt.annotation.Nullable;
15 import org.eclipse.tracecompass.internal.provisional.datastore.core.serialization.ISafeByteBufferReader;
16 import org.eclipse.tracecompass.internal.provisional.datastore.core.serialization.ISafeByteBufferWriter;
17 import org.eclipse.tracecompass.internal.statesystem.core.Activator;
18 import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
19
20 import com.google.common.annotations.VisibleForTesting;
21
22 /**
23 * This allows to define custom state values.
24 *
25 * Each sub-class should define a {@link CustomStateValueFactory} that has to be
26 * registered, for example by the analysis using this state value type, through
27 * the
28 * {@link CustomStateValue#registerCustomFactory(byte, CustomStateValueFactory)}
29 * method.
30 *
31 * Note to implementers: These state values are meant to be used in data
32 * structures that save information on objects that varies in time, like a state
33 * system. It will often be made persistent on disk at some point, so it is
34 * suggested to make the child classes immutable.
35 *
36 * Data structures using these custom values will often keep the values in a
37 * transient state before they are sent to be persisted. For persistence, it
38 * will request the size of the value, then write its bytes to a buffer. Once
39 * the size is requested, the value is about to be saved, so it is important
40 * that its value and size do not change after that, as it may get in an
41 * incoherent state, or, worse throw runtime exceptions.
42 *
43 * @author Geneviève Bastien
44 * @since 2.0
45 */
46 public abstract class CustomStateValue extends TmfStateValue {
47
48 /* Minimum size of the state value */
49 private static final int MIN_SIZE = Byte.BYTES;
50
51 /**
52 * Custom value factory interface. Each custom state value should have a
53 * corresponding factory to re-create the object from a ByteBuffer
54 */
55 @FunctionalInterface
56 public interface CustomStateValueFactory {
57
58 /**
59 * Get the custom state value from a byte buffer
60 *
61 * @param buffer
62 * the byte buffer
63 * @return the custom state value
64 */
65 CustomStateValue readCustomValue(ISafeByteBufferReader buffer);
66 }
67
68 private static final CustomStateValueFactory[] CUSTOM_FACTORIES = new CustomStateValueFactory[256];
69
70 /**
71 * Register the custom factory that will be reused to create instances of
72 * custom state value objects
73 *
74 * @param customId
75 * The ID of this custom type. For possible values of this ID,
76 * see {@link #getCustomTypeId()}
77 * @param factory
78 * The factory used to create a new custom type object of this ID
79 */
80 public static void registerCustomFactory(byte customId, CustomStateValueFactory factory) {
81 if (customId >= 0 && customId <= 20) {
82 throw new IllegalArgumentException("State value IDs between 0 and 20 are reserved for built-in state value types"); //$NON-NLS-1$
83 }
84 CustomStateValueFactory currentFactory = CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
85 if (currentFactory == null) {
86 CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = factory;
87 } else if (currentFactory != factory) {
88 throw new IllegalStateException("Already a custom factory registered for " + Byte.toString(customId)); //$NON-NLS-1$
89 }
90 }
91
92 /**
93 * Unregisters the custom factory
94 *
95 * @param customId
96 * The ID of this custom type
97 */
98 @VisibleForTesting
99 protected static void unregisterCustomFactory(byte customId) {
100 CUSTOM_FACTORIES[customId - Byte.MIN_VALUE] = null;
101 }
102
103 /**
104 * Get the custom factory for a byte
105 *
106 * @param customId
107 * the custom factory key
108 * @return the custom factory or null if none is registered for the key
109 */
110 public static @Nullable CustomStateValueFactory getCustomFactory(byte customId) {
111 return CUSTOM_FACTORIES[customId - Byte.MIN_VALUE];
112 }
113
114 /**
115 * Read a serialized value (obtained with the
116 * {@link #serialize(ISafeByteBufferWriter)} method) into a real
117 * {@link CustomStateValue} object.
118 *
119 * @param buffer
120 * The byte buffer to read from
121 * @return The state value object
122 */
123 public static TmfStateValue readSerializedValue(ISafeByteBufferReader buffer) {
124 /* the first byte = the custom type id */
125 byte customType = buffer.get();
126 CustomStateValueFactory customFactory = CustomStateValue.getCustomFactory(customType);
127 if (customFactory == null) {
128 Activator.getDefault().logWarning("Custom factory for type " + customType + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
129 return TmfStateValue.nullValue();
130 }
131
132 return customFactory.readCustomValue(buffer);
133 }
134
135 /**
136 * Get custom type id. The value should be between {@link Byte#MIN_VALUE}
137 * and {@link Byte#MAX_VALUE}, but not between {@code 0} and {@code 20} that
138 * are reserved for built-in state value types.
139 *
140 * @return the custom type ID
141 */
142 protected abstract Byte getCustomTypeId();
143
144 /**
145 * Serialize this state value into the byte buffer. This method should only
146 * write the payload of the state value and should match what will be read
147 * by the factory when deserializing.
148 *
149 * This serialized value must contain all the payload of the state value
150 *
151 * @param buffer
152 * The ByteBuffer to write the values to
153 */
154 protected abstract void serializeValue(ISafeByteBufferWriter buffer);
155
156 /**
157 * Get the serialized size of this state value. The size must not exceed
158 * {@link Short#MAX_VALUE - MIN_SIZE}, otherwise serialization might throw a
159 * {@link BufferOverflowException}
160 *
161 * @return The serialized size of this state value
162 */
163 protected abstract int getSerializedValueSize();
164
165 /**
166 * Serialize this custom state value into a byte buffer. It calls the
167 * serialization of the state value itself and adds the specific fields to
168 * interpret that byte array.
169 *
170 * The format of the value is [custom type (byte)][payload]
171 *
172 * The total serialized size should never exceed {@link Short#MAX_VALUE}
173 *
174 * @param buffer
175 * The ByteBuffer to write the values to
176 *
177 * @throws BufferOverflowException
178 * If the serialized size of the value ends up larger than the
179 * maximum of {@link Short#MAX_VALUE} and the implementation has
180 * no way of handling it
181 */
182 public final void serialize(ISafeByteBufferWriter buffer) {
183 buffer.put(getCustomTypeId());
184 serializeValue(buffer);
185 }
186
187 /**
188 * Get the serialized size of this state value. This size will be used to
189 * allow the buffer that will be sent to the
190 * {@link #serialize(ISafeByteBufferWriter)} method
191 *
192 * @return The size of the serialized value
193 */
194 public final int getSerializedSize() {
195 int size = getSerializedValueSize();
196 if (size > Short.MAX_VALUE - MIN_SIZE) {
197 throw new ArrayIndexOutOfBoundsException("Serialized state value is larger than the maximum allowed size of " + (Short.MAX_VALUE - MIN_SIZE) + ": " + size); //$NON-NLS-1$ //$NON-NLS-2$
198 }
199 return size + MIN_SIZE;
200 }
201
202 @Override
203 public final Type getType() {
204 return Type.CUSTOM;
205 }
206
207 @Override
208 public boolean isNull() {
209 return false;
210 }
211
212 }
This page took 0.035179 seconds and 5 git commands to generate.