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}