001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.util.struct;
006
007import java.lang.reflect.Array;
008import java.nio.ByteBuffer;
009
010/**
011 * Interface for raw struct serialization.
012 *
013 * <p>This is designed for serializing small fixed-size data structures in the fastest and most
014 * compact means possible. Serialization consists of making relative put() calls to a ByteBuffer and
015 * deserialization consists of making relative get() calls from a ByteBuffer.
016 *
017 * <p>Idiomatically, classes that support raw struct serialization should provide a static final
018 * member named "struct" that provides an instance of an implementation of this interface.
019 *
020 * @param <T> object type
021 */
022public interface Struct<T> {
023  /** Serialized size of a "bool" value. */
024  int kSizeBool = 1;
025
026  /** Serialized size of an "int8" or "uint8" value. */
027  int kSizeInt8 = 1;
028
029  /** Serialized size of an "int16" or "uint16" value. */
030  int kSizeInt16 = 2;
031
032  /** Serialized size of an "int32" or "uint32" value. */
033  int kSizeInt32 = 4;
034
035  /** Serialized size of an "int64" or "uint64" value. */
036  int kSizeInt64 = 8;
037
038  /** Serialized size of an "float" or "float32" value. */
039  int kSizeFloat = 4;
040
041  /** Serialized size of an "double" or "float64" value. */
042  int kSizeDouble = 8;
043
044  /**
045   * Gets the Class object for the stored value.
046   *
047   * @return Class
048   */
049  Class<T> getTypeClass();
050
051  /**
052   * Gets the type name (e.g. for schemas of other structs). This should be globally unique among
053   * structs.
054   *
055   * @return type name
056   */
057  String getTypeName();
058
059  /**
060   * Gets the type string (e.g. for NetworkTables). This should be globally unique and start with
061   * "struct:".
062   *
063   * @return type string
064   */
065  default String getTypeString() {
066    return "struct:" + getTypeName();
067  }
068
069  /**
070   * Gets the serialized size (in bytes). This should always be a constant.
071   *
072   * @return serialized size
073   */
074  int getSize();
075
076  /**
077   * Gets the schema.
078   *
079   * @return schema
080   */
081  String getSchema();
082
083  /**
084   * Gets the list of struct types referenced by this struct.
085   *
086   * @return list of struct types
087   */
088  default Struct<?>[] getNested() {
089    return new Struct<?>[] {};
090  }
091
092  /**
093   * Deserializes an object from a raw struct serialized ByteBuffer starting at the current
094   * position. Will increment the ByteBuffer position by getStructSize() bytes. Will not otherwise
095   * modify the ByteBuffer (e.g. byte order will not be changed).
096   *
097   * @param bb ByteBuffer
098   * @return New object
099   */
100  T unpack(ByteBuffer bb);
101
102  /**
103   * Puts object contents to a ByteBuffer starting at the current position. Will increment the
104   * ByteBuffer position by getStructSize() bytes. Will not otherwise modify the ByteBuffer (e.g.
105   * byte order will not be changed).
106   *
107   * @param bb ByteBuffer
108   * @param value object to serialize
109   */
110  void pack(ByteBuffer bb, T value);
111
112  /**
113   * Updates object contents from a raw struct serialized ByteBuffer starting at the current
114   * position. Will increment the ByteBuffer position by getStructSize() bytes. Will not otherwise
115   * modify the ByteBuffer (e.g. byte order will not be changed).
116   *
117   * <p>Immutable classes cannot and should not implement this function. The default implementation
118   * throws UnsupportedOperationException.
119   *
120   * @param out object to update
121   * @param bb ByteBuffer
122   * @throws UnsupportedOperationException if the object is immutable
123   */
124  default void unpackInto(T out, ByteBuffer bb) {
125    throw new UnsupportedOperationException("object does not support unpackInto");
126  }
127
128  /**
129   * Deserializes an array from a raw struct serialized ByteBuffer starting at the current position.
130   * Will increment the ByteBuffer position by size * struct.size() bytes. Will not otherwise modify
131   * the ByteBuffer (e.g. byte order will not be changed).
132   *
133   * @param <T> Object type
134   * @param bb ByteBuffer
135   * @param size Size of the array
136   * @param struct Struct implementation
137   * @return Deserialized array
138   */
139  static <T> T[] unpackArray(ByteBuffer bb, int size, Struct<T> struct) {
140    @SuppressWarnings("unchecked")
141    T[] arr = (T[]) Array.newInstance(struct.getTypeClass(), size);
142    for (int i = 0; i < arr.length; i++) {
143      arr[i] = struct.unpack(bb);
144    }
145    return arr;
146  }
147
148  /**
149   * Deserializes a double array from a raw struct serialized ByteBuffer starting at the current
150   * position. Will increment the ByteBuffer position by size * kSizeDouble bytes. Will not
151   * otherwise modify the ByteBuffer (e.g. byte order will not be changed).
152   *
153   * @param bb ByteBuffer
154   * @param size Size of the array
155   * @return Double array
156   */
157  static double[] unpackDoubleArray(ByteBuffer bb, int size) {
158    double[] arr = new double[size];
159    for (int i = 0; i < size; i++) {
160      arr[i] = bb.getDouble();
161    }
162    return arr;
163  }
164
165  /**
166   * Puts array contents to a ByteBuffer starting at the current position. Will increment the
167   * ByteBuffer position by size * struct.size() bytes. Will not otherwise modify the ByteBuffer
168   * (e.g. byte order will not be changed).
169   *
170   * @param <T> Object type
171   * @param bb ByteBuffer
172   * @param arr Array to serialize
173   * @param struct Struct implementation
174   */
175  static <T> void packArray(ByteBuffer bb, T[] arr, Struct<T> struct) {
176    for (T obj : arr) {
177      struct.pack(bb, obj);
178    }
179  }
180
181  /**
182   * Puts array contents to a ByteBuffer starting at the current position. Will increment the
183   * ByteBuffer position by size * kSizeDouble bytes. Will not otherwise modify the ByteBuffer (e.g.
184   * byte order will not be changed).
185   *
186   * @param bb ByteBuffer
187   * @param arr Array to serialize
188   */
189  static void packArray(ByteBuffer bb, double[] arr) {
190    for (double obj : arr) {
191      bb.putDouble(obj);
192    }
193  }
194
195  /**
196   * Returns whether or not objects are immutable. Immutable objects must also be comparable using
197   * the equals() method. Default implementation returns false.
198   *
199   * @return True if object is immutable
200   */
201  default boolean isImmutable() {
202    return false;
203  }
204
205  /**
206   * Returns whether or not objects are cloneable using the clone() method. Cloneable objects must
207   * also be comparable using the equals() method. Default implementation returns false.
208   *
209   * @return True if object is cloneable
210   */
211  default boolean isCloneable() {
212    return false;
213  }
214
215  /**
216   * Creates a (deep) clone of the object. May also return the object directly if the object is
217   * immutable. Default implementation throws CloneNotSupportedException. Typically this should be
218   * implemented by implementing clone() on the object itself, and calling it from here.
219   *
220   * @param obj object to clone
221   * @return Clone of object (if immutable, may be same object)
222   * @throws CloneNotSupportedException if clone not supported
223   */
224  default T clone(T obj) throws CloneNotSupportedException {
225    throw new CloneNotSupportedException();
226  }
227}