/* * Copyright (c) 2015 Frekk van Blagh * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef __WINDOW_H__ #define __WINDOW_H__ #define MAX_SEQ_ID 256 #define MAX_FRAGSIZE 2048 #define ACK_TIMEOUT 5 #define WINDOW_SENDING 1 #define WINDOW_RECVING 0 typedef struct fragment { size_t len; /* Length of fragment data */ unsigned seqID; /* fragment sequence ID */ int ack_other; /* other way ACK seqID (>=0) or unset (<0) */ int is_nack; /* 1 if other way ACK is a NACK */ int compressed; /* compression flag */ uint8_t start; /* start of chunk flag */ uint8_t end; /* end of chunk flag */ uint8_t data[MAX_FRAGSIZE]; /* fragment data */ unsigned retries; /* number of times fragment has been sent */ time_t lastsent; /* timestamp of most recent send attempt TODO: millisecond precision*/ int acks; /* number of times packet has been ack'd (should be <= 1) */ } fragment; struct frag_buffer { fragment *frags; /* pointer to array of data fragments */ unsigned windowsize; /* Max number of packets in flight */ unsigned maxfraglen; /* Max fragment size */ size_t length; /* Length of buffer */ size_t numitems; /* number of non-empty fragments stored in buffer */ size_t window_start; /* Start of window */ size_t window_end; /* End of window (index) */ // size_t last_sent; /* Last fragment sent (index) */ size_t last_write; /* Last fragment read/written */ size_t chunk_start; /* Start of current chunk of fragments, ie where fragno = 0 */ unsigned cur_seq_id; /* Most recent sequence ID */ unsigned start_seq_id; /* Start of window sequence ID */ unsigned resends; /* number of fragments resent */ int direction; /* Sending or recving */ }; #define AFTER(w, o) ((w->window_start + o) % w->length) // Distance (going forwards) between a and b in window of length l #define DISTF(l, a, b) (((a > b) ? a-b : l-a+b-1) % l) // Distance backwards between a and b in window of length l #define DISTB(l, a, b) (((a < b) ? l-b+a-1 : a-b) % l) #define INWINDOW_INDEX(w, a) ((w->window_start < w->window_end) ? \ (a >= w->window_start && a <= w->window_end) : \ ((a >= w->window_start && a <= w->length - 1) || \ (a >= 0 && a <= w->window_end))) #define INWINDOW_SEQ(start, end, a) ((start < end) ? \ (a >= start && a <= end) : \ ((a >= start && a <= MAX_SEQ_ID - 1) || \ (a <= end))) #define SEQ_OFFSET(start, a) ((a >= start) ? a - start : MAX_SEQ_ID + start - a - 1) #define WRAP(x) ((x) % w->length) struct frag_buffer *window_buffer_init(size_t length, unsigned windowsize, unsigned fragsize, int dir); void window_buffer_destroy(struct frag_buffer *w); /* Returns number of available fragment slots (NOT BYTES) */ size_t window_buffer_available(struct frag_buffer *w); /* Places a fragment in the window after the last one */ int window_append_fragment(struct frag_buffer *w, fragment *src); /* Handles fragment received from the sending side (RECV) */ int window_process_incoming_fragment(struct frag_buffer *w, fragment *f); /* Reassembles first complete sequence of fragments into data. (RECV) * Returns length of data reassembled, or 0 if no data reassembled */ size_t window_reassemble_data(struct frag_buffer *w, uint8_t *data, unsigned maxlen, int *compression); /* Returns next fragment to be sent or NULL if nothing (SEND) */ fragment *window_get_next_sending_fragment(struct frag_buffer *w, int other_ack); /* Gets the seqid of next fragment to be ACK'd (RECV) */ int window_get_next_ack(struct frag_buffer *w); /* Sets the fragment with seqid to be ACK'd (SEND) */ void window_ack(struct frag_buffer *w, unsigned seqid); /* To be called after all other processing has been done * when anything happens (moves window etc) (SEND/RECV) */ void window_tick(struct frag_buffer *w); /* Splits data into fragments and adds to the end of the window buffer for sending * All fragment meta-data is created here (SEND) */ int window_add_outgoing_data(struct frag_buffer *w, uint8_t *data, size_t len, int compressed); #endif /* __WINDOW_H__ */