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}