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