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; 009import java.nio.ByteOrder; 010import java.util.Collection; 011 012/** 013 * Reusable buffer for serialization/deserialization to/from a raw struct. 014 * 015 * @param <T> Object type. 016 */ 017public final class StructBuffer<T> { 018 private StructBuffer(Struct<T> struct) { 019 m_structSize = struct.getSize(); 020 m_buf = ByteBuffer.allocateDirect(m_structSize).order(ByteOrder.LITTLE_ENDIAN); 021 m_struct = struct; 022 } 023 024 /** 025 * Returns a StructBuffer for the given struct. 026 * 027 * @param struct A struct. 028 * @param <T> Object type. 029 * @return A StructBuffer for the given struct. 030 */ 031 public static <T> StructBuffer<T> create(Struct<T> struct) { 032 return new StructBuffer<>(struct); 033 } 034 035 /** 036 * Gets the struct object of the stored type. 037 * 038 * @return struct object 039 */ 040 public Struct<T> getStruct() { 041 return m_struct; 042 } 043 044 /** 045 * Gets the type string. 046 * 047 * @return type string 048 */ 049 public String getTypeString() { 050 return m_struct.getTypeString(); 051 } 052 053 /** 054 * Ensures sufficient buffer space is available for the given number of elements. 055 * 056 * @param nelem number of elements 057 */ 058 public void reserve(int nelem) { 059 if ((nelem * m_structSize) > m_buf.capacity()) { 060 m_buf = ByteBuffer.allocateDirect(nelem * m_structSize).order(ByteOrder.LITTLE_ENDIAN); 061 } 062 } 063 064 /** 065 * Serializes a value to a ByteBuffer. The returned ByteBuffer is a direct byte buffer with the 066 * position set to the end of the serialized data. 067 * 068 * @param value value 069 * @return byte buffer 070 */ 071 public ByteBuffer write(T value) { 072 m_buf.position(0); 073 m_struct.pack(m_buf, value); 074 return m_buf; 075 } 076 077 /** 078 * Deserializes a value from a byte array, creating a new object. 079 * 080 * @param buf byte array 081 * @param start starting location within byte array 082 * @param len length of serialized data 083 * @return new object 084 */ 085 public T read(byte[] buf, int start, int len) { 086 return read(ByteBuffer.wrap(buf, start, len)); 087 } 088 089 /** 090 * Deserializes a value from a byte array, creating a new object. 091 * 092 * @param buf byte array 093 * @return new object 094 */ 095 public T read(byte[] buf) { 096 return read(buf, 0, buf.length); 097 } 098 099 /** 100 * Deserializes a value from a ByteBuffer, creating a new object. 101 * 102 * @param buf byte buffer 103 * @return new object 104 */ 105 public T read(ByteBuffer buf) { 106 buf.order(ByteOrder.LITTLE_ENDIAN); 107 return m_struct.unpack(buf); 108 } 109 110 /** 111 * Deserializes a value from a byte array into a mutable object. 112 * 113 * @param out object (will be updated with deserialized contents) 114 * @param buf byte array 115 * @param start starting location within byte array 116 * @param len length of serialized data 117 * @throws UnsupportedOperationException if T is immutable 118 */ 119 public void readInto(T out, byte[] buf, int start, int len) { 120 readInto(out, ByteBuffer.wrap(buf, start, len)); 121 } 122 123 /** 124 * Deserializes a value from a byte array into a mutable object. 125 * 126 * @param out object (will be updated with deserialized contents) 127 * @param buf byte array 128 * @throws UnsupportedOperationException if T is immutable 129 */ 130 public void readInto(T out, byte[] buf) { 131 readInto(out, buf, 0, buf.length); 132 } 133 134 /** 135 * Deserializes a value from a ByteBuffer into a mutable object. 136 * 137 * @param out object (will be updated with deserialized contents) 138 * @param buf byte buffer 139 * @throws UnsupportedOperationException if T is immutable 140 */ 141 public void readInto(T out, ByteBuffer buf) { 142 m_struct.unpackInto(out, buf); 143 } 144 145 /** 146 * Serializes a collection of values to a ByteBuffer. The returned ByteBuffer is a direct byte 147 * buffer with the position set to the end of the serialized data. 148 * 149 * @param values values 150 * @return byte buffer 151 */ 152 public ByteBuffer writeArray(Collection<T> values) { 153 m_buf.position(0); 154 if ((values.size() * m_structSize) > m_buf.capacity()) { 155 m_buf = 156 ByteBuffer.allocateDirect(values.size() * m_structSize * 2) 157 .order(ByteOrder.LITTLE_ENDIAN); 158 } 159 for (T v : values) { 160 m_struct.pack(m_buf, v); 161 } 162 return m_buf; 163 } 164 165 /** 166 * Serializes an array of values to a ByteBuffer. The returned ByteBuffer is a direct byte buffer 167 * with the position set to the end of the serialized data. 168 * 169 * @param values values 170 * @return byte buffer 171 */ 172 public ByteBuffer writeArray(T[] values) { 173 m_buf.position(0); 174 if ((values.length * m_structSize) > m_buf.capacity()) { 175 m_buf = 176 ByteBuffer.allocateDirect(values.length * m_structSize * 2) 177 .order(ByteOrder.LITTLE_ENDIAN); 178 } 179 for (T v : values) { 180 m_struct.pack(m_buf, v); 181 } 182 return m_buf; 183 } 184 185 /** 186 * Deserializes an array of values from a byte array, creating an array of new objects. 187 * 188 * @param buf byte array 189 * @param start starting location within byte array 190 * @param len length of serialized data 191 * @return new object array 192 */ 193 public T[] readArray(byte[] buf, int start, int len) { 194 return readArray(ByteBuffer.wrap(buf, start, len)); 195 } 196 197 /** 198 * Deserializes an array of values from a byte array, creating an array of new objects. 199 * 200 * @param buf byte array 201 * @return new object array 202 */ 203 public T[] readArray(byte[] buf) { 204 return readArray(buf, 0, buf.length); 205 } 206 207 /** 208 * Deserializes an array of values from a ByteBuffer, creating an array of new objects. 209 * 210 * @param buf byte buffer 211 * @return new object array 212 */ 213 public T[] readArray(ByteBuffer buf) { 214 buf.order(ByteOrder.LITTLE_ENDIAN); 215 int len = buf.limit() - buf.position(); 216 if ((len % m_structSize) != 0) { 217 throw new RuntimeException("buffer size not a multiple of struct size"); 218 } 219 int nelem = len / m_structSize; 220 @SuppressWarnings("unchecked") 221 T[] arr = (T[]) Array.newInstance(m_struct.getTypeClass(), nelem); 222 for (int i = 0; i < nelem; i++) { 223 arr[i] = m_struct.unpack(buf); 224 } 225 return arr; 226 } 227 228 private ByteBuffer m_buf; 229 private final Struct<T> m_struct; 230 private final int m_structSize; 231}