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