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}