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