001/* 002 * Copyright (C) Photon Vision. 003 * 004 * This program is free software: you can redistribute it and/or modify 005 * it under the terms of the GNU General Public License as published by 006 * the Free Software Foundation, either version 3 of the License, or 007 * (at your option) any later version. 008 * 009 * This program is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 * GNU General Public License for more details. 013 * 014 * You should have received a copy of the GNU General Public License 015 * along with this program. If not, see <https://www.gnu.org/licenses/>. 016 */ 017 018package org.photonvision.common.dataflow.structures; 019 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.List; 023import java.util.Optional; 024import org.photonvision.targeting.serde.PhotonStructSerializable; 025 026/** A packet that holds byte-packed data to be sent over NetworkTables. */ 027public class Packet { 028 // Data stored in the packet. 029 byte[] packetData; 030 // Read and write positions. 031 int readPos, writePos; 032 033 /** 034 * Constructs an empty packet. This buffer will dynamically expand if we need more data space. 035 * 036 * @param size The size of the packet buffer. 037 */ 038 public Packet(int size) { 039 packetData = new byte[size]; 040 } 041 042 /** 043 * Constructs a packet with the given data. 044 * 045 * @param data The packet data. 046 */ 047 public Packet(byte[] data) { 048 packetData = data; 049 } 050 051 /** Clears the packet and resets the read and write positions. */ 052 public void clear() { 053 packetData = new byte[packetData.length]; 054 readPos = 0; 055 writePos = 0; 056 } 057 058 public int getNumBytesWritten() { 059 return writePos + 1; 060 } 061 062 public int getNumBytesRead() { 063 return readPos + 1; 064 } 065 066 public int getSize() { 067 return packetData.length; 068 } 069 070 /** 071 * Returns a copy of only the packet data we've actually written to so far. 072 * 073 * @return The packet data. 074 */ 075 public byte[] getWrittenDataCopy() { 076 return Arrays.copyOfRange(packetData, 0, writePos); 077 } 078 079 /** 080 * Sets the packet data. 081 * 082 * @param data The packet data. 083 */ 084 public void setData(byte[] data) { 085 packetData = data; 086 } 087 088 // Logic taken from ArraysSupport, licensed under GPL V2 089 public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; 090 091 // Logic taken from ArraysSupport, licensed under GPL V2 092 private static int newLength(int oldLength, int minGrowth, int prefGrowth) { 093 // preconditions not checked because of inlining 094 // assert oldLength >= 0 095 // assert minGrowth > 0 096 097 int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow 098 if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) { 099 return prefLength; 100 } else { 101 // put code cold in a separate method 102 return hugeLength(oldLength, minGrowth); 103 } 104 } 105 106 // Logic taken from ArraysSupport, licensed under GPL V2 107 private static int hugeLength(int oldLength, int minGrowth) { 108 int minLength = oldLength + minGrowth; 109 if (minLength < 0) { // overflow 110 throw new OutOfMemoryError( 111 "Required array length " + oldLength + " + " + minGrowth + " is too large"); 112 } else if (minLength <= SOFT_MAX_ARRAY_LENGTH) { 113 return SOFT_MAX_ARRAY_LENGTH; 114 } else { 115 return minLength; 116 } 117 } 118 119 /** 120 * Increases the capacity to ensure that it can hold at least the number of elements specified by 121 * the minimum capacity argument. 122 * 123 * <p>This logic is copied from ArrayList, which is licensed GPL V2 124 * 125 * @param minCapacity the desired minimum capacity 126 * @return 127 */ 128 private void ensureCapacity(int bytesToAdd) { 129 int minCapacity = writePos + bytesToAdd; 130 int oldCapacity = packetData.length; 131 if (minCapacity <= oldCapacity) { 132 return; 133 } 134 if (oldCapacity > 0) { 135 int newCapacity = 136 Packet.newLength( 137 oldCapacity, 138 minCapacity - oldCapacity, /* minimum growth */ 139 oldCapacity >> 1 /* preferred growth */); 140 packetData = Arrays.copyOf(packetData, newCapacity); 141 } else { 142 packetData = new byte[Math.max(256, minCapacity)]; 143 } 144 } 145 146 /** 147 * Encodes the byte into the packet. 148 * 149 * @param src The byte to encode. 150 */ 151 public void encode(byte src) { 152 ensureCapacity(1); 153 packetData[writePos++] = src; 154 } 155 156 /** 157 * Encodes the short into the packet. 158 * 159 * @param src The short to encode. 160 */ 161 public void encode(short src) { 162 ensureCapacity(2); 163 packetData[writePos++] = (byte) src; 164 packetData[writePos++] = (byte) (src >>> 8); 165 } 166 167 /** 168 * Encodes the integer into the packet. 169 * 170 * @param src The integer to encode. 171 */ 172 public void encode(int src) { 173 ensureCapacity(4); 174 packetData[writePos++] = (byte) src; 175 packetData[writePos++] = (byte) (src >>> 8); 176 packetData[writePos++] = (byte) (src >>> 16); 177 packetData[writePos++] = (byte) (src >>> 24); 178 } 179 180 /** 181 * Encodes the float into the packet. 182 * 183 * @param src The float to encode. 184 */ 185 public void encode(float src) { 186 ensureCapacity(4); 187 int data = Float.floatToIntBits(src); 188 packetData[writePos++] = (byte) (data & 0xff); 189 packetData[writePos++] = (byte) ((data >> 8) & 0xff); 190 packetData[writePos++] = (byte) ((data >> 16) & 0xff); 191 packetData[writePos++] = (byte) ((data >> 24) & 0xff); 192 } 193 194 /** 195 * Encodes the double into the packet. 196 * 197 * @param data The double to encode. 198 */ 199 public void encode(long data) { 200 ensureCapacity(8); 201 packetData[writePos++] = (byte) (data & 0xff); 202 packetData[writePos++] = (byte) ((data >> 8) & 0xff); 203 packetData[writePos++] = (byte) ((data >> 16) & 0xff); 204 packetData[writePos++] = (byte) ((data >> 24) & 0xff); 205 packetData[writePos++] = (byte) ((data >> 32) & 0xff); 206 packetData[writePos++] = (byte) ((data >> 40) & 0xff); 207 packetData[writePos++] = (byte) ((data >> 48) & 0xff); 208 packetData[writePos++] = (byte) ((data >> 56) & 0xff); 209 } 210 211 /** 212 * Encodes the double into the packet. 213 * 214 * @param src The double to encode. 215 */ 216 public void encode(double src) { 217 ensureCapacity(8); 218 long data = Double.doubleToRawLongBits(src); 219 packetData[writePos++] = (byte) (data & 0xff); 220 packetData[writePos++] = (byte) ((data >> 8) & 0xff); 221 packetData[writePos++] = (byte) ((data >> 16) & 0xff); 222 packetData[writePos++] = (byte) ((data >> 24) & 0xff); 223 packetData[writePos++] = (byte) ((data >> 32) & 0xff); 224 packetData[writePos++] = (byte) ((data >> 40) & 0xff); 225 packetData[writePos++] = (byte) ((data >> 48) & 0xff); 226 packetData[writePos++] = (byte) ((data >> 56) & 0xff); 227 } 228 229 /** 230 * Encodes the boolean into the packet. 231 * 232 * @param src The boolean to encode. 233 */ 234 public void encode(boolean src) { 235 ensureCapacity(1); 236 packetData[writePos++] = src ? (byte) 1 : (byte) 0; 237 } 238 239 public void encode(List<Short> data) { 240 byte size = (byte) data.size(); 241 if (data.size() > Byte.MAX_VALUE) { 242 throw new RuntimeException("Array too long! Got " + size); 243 } 244 245 // length byte 246 encode(size); 247 248 for (var f : data) { 249 encode(f); 250 } 251 } 252 253 public <T extends PhotonStructSerializable<T>> void encode(T data) { 254 data.getSerde().pack(this, data); 255 } 256 257 /** 258 * Encode a list of serializable structs. Lists are stored as [uint8 length, [length many] data 259 * structs] 260 * 261 * @param <T> the class this list will be packing 262 * @param data 263 */ 264 public <T extends PhotonStructSerializable<T>> void encodeList(List<T> data) { 265 byte size = (byte) data.size(); 266 if (data.size() > Byte.MAX_VALUE) { 267 throw new RuntimeException("Array too long! Got " + size); 268 } 269 270 // length byte 271 encode(size); 272 273 for (var f : data) { 274 f.getSerde().pack(this, f); 275 } 276 } 277 278 public <T extends PhotonStructSerializable<T>> void encodeOptional(Optional<T> data) { 279 encode(data.isPresent()); 280 if (data.isPresent()) { 281 data.get().getSerde().pack(this, data.get()); 282 } 283 } 284 285 /** 286 * Returns a decoded byte from the packet. 287 * 288 * @return A decoded byte from the packet. 289 */ 290 public byte decodeByte() { 291 if (packetData.length < readPos) { 292 return '\0'; 293 } 294 return packetData[readPos++]; 295 } 296 297 /** 298 * Returns a decoded int from the packet. 299 * 300 * @return A decoded int from the packet. 301 */ 302 public int decodeInt() { 303 if (packetData.length < readPos + 3) { 304 return 0; 305 } 306 return (0xff & packetData[readPos++]) 307 | (0xff & packetData[readPos++]) << 8 308 | (0xff & packetData[readPos++]) << 16 309 | (0xff & packetData[readPos++]) << 24; 310 } 311 312 public long decodeLong() { 313 if (packetData.length < (readPos + 7)) { 314 return 0; 315 } 316 long data = 317 (long) 318 (0xff & packetData[readPos++] 319 | (long) (0xff & packetData[readPos++]) << 8 320 | (long) (0xff & packetData[readPos++]) << 16 321 | (long) (0xff & packetData[readPos++]) << 24 322 | (long) (0xff & packetData[readPos++]) << 32 323 | (long) (0xff & packetData[readPos++]) << 40 324 | (long) (0xff & packetData[readPos++]) << 48 325 | (long) (0xff & packetData[readPos++]) << 56); 326 return data; 327 } 328 329 /** 330 * Returns a decoded double from the packet. 331 * 332 * @return A decoded double from the packet. 333 */ 334 public double decodeDouble() { 335 if (packetData.length < (readPos + 7)) { 336 return 0; 337 } 338 long data = 339 (long) 340 (0xff & packetData[readPos++] 341 | (long) (0xff & packetData[readPos++]) << 8 342 | (long) (0xff & packetData[readPos++]) << 16 343 | (long) (0xff & packetData[readPos++]) << 24 344 | (long) (0xff & packetData[readPos++]) << 32 345 | (long) (0xff & packetData[readPos++]) << 40 346 | (long) (0xff & packetData[readPos++]) << 48 347 | (long) (0xff & packetData[readPos++]) << 56); 348 return Double.longBitsToDouble(data); 349 } 350 351 /** 352 * Returns a decoded float from the packet. 353 * 354 * @return A decoded float from the packet. 355 */ 356 public float decodeFloat() { 357 if (packetData.length < (readPos + 3)) { 358 return 0; 359 } 360 361 int data = 362 ((0xff & packetData[readPos++] 363 | (0xff & packetData[readPos++]) << 8 364 | (0xff & packetData[readPos++]) << 16 365 | (0xff & packetData[readPos++]) << 24)); 366 return Float.intBitsToFloat(data); 367 } 368 369 /** 370 * Returns a decoded boolean from the packet. 371 * 372 * @return A decoded boolean from the packet. 373 */ 374 public boolean decodeBoolean() { 375 if (packetData.length < readPos) { 376 return false; 377 } 378 return packetData[readPos++] == 1; 379 } 380 381 public void encode(double[] data) { 382 for (double d : data) { 383 encode(d); 384 } 385 } 386 387 public double[] decodeDoubleArray(int len) { 388 double[] ret = new double[len]; 389 for (int i = 0; i < len; i++) { 390 ret[i] = decodeDouble(); 391 } 392 return ret; 393 } 394 395 public short decodeShort() { 396 if (packetData.length < readPos + 1) { 397 return 0; 398 } 399 return (short) ((0xff & packetData[readPos++]) | (0xff & packetData[readPos++]) << 8); 400 } 401 402 /** 403 * Decode a list of serializable structs. Lists are stored as [uint8 length, [length many] data 404 * structs]. Because java sucks, we need to take the serde ref directly 405 * 406 * @param <T> 407 * @param serde 408 */ 409 public <T extends PhotonStructSerializable<T>> List<T> decodeList(PacketSerde<T> serde) { 410 byte length = decodeByte(); 411 412 var ret = new ArrayList<T>(); 413 ret.ensureCapacity(length); 414 415 for (int i = 0; i < length; i++) { 416 ret.add(serde.unpack(this)); 417 } 418 419 return ret; 420 } 421 422 public <T extends PhotonStructSerializable<T>> Optional<T> decodeOptional(PacketSerde<T> serde) { 423 var present = decodeBoolean(); 424 if (present) { 425 return Optional.of(serde.unpack(this)); 426 } 427 return Optional.empty(); 428 } 429 430 public List<Short> decodeShortList() { 431 byte length = decodeByte(); 432 433 var ret = new ArrayList<Short>(); 434 ret.ensureCapacity(length); 435 436 for (int i = 0; i < length; i++) { 437 ret.add(decodeShort()); 438 } 439 440 return ret; 441 } 442 443 public <T extends PhotonStructSerializable<T>> T decode(PhotonStructSerializable<T> t) { 444 return t.getSerde().unpack(this); 445 } 446}