/* * STFU (S)ort (T)ransportable (F)ramed (U)tterances * Copyright (c) 2007 Anthony Minessale II * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * THOSE WHO DISAGREE MAY CERTIANLY STFU */ #include "stfu.h" #ifdef _MSC_VER /* warning C4706: assignment within conditional expression*/ #pragma warning(disable: 4706) #endif struct stfu_queue { struct stfu_frame *array; struct stfu_frame int_frame; uint32_t array_size; uint32_t array_len; uint32_t wr_len; uint32_t last_index; }; typedef struct stfu_queue stfu_queue_t; struct stfu_instance { struct stfu_queue a_queue; struct stfu_queue b_queue; struct stfu_queue *in_queue; struct stfu_queue *out_queue; uint32_t last_ts; uint32_t interval; uint32_t miss_count; uint8_t running; }; static void stfu_n_init_aqueue(stfu_queue_t *queue, uint32_t qlen) { queue->array = calloc(qlen, sizeof(struct stfu_frame)); assert(queue->array != NULL); memset(queue->array, 0, sizeof(struct stfu_frame) * qlen); queue->array_size = qlen; queue->int_frame.plc = 1; } void stfu_n_destroy(stfu_instance_t **i) { stfu_instance_t *ii; if (i && *i) { ii = *i; *i = NULL; free(ii->a_queue.array); free(ii->b_queue.array); free(ii); } } stfu_instance_t *stfu_n_init(uint32_t qlen) { struct stfu_instance *i; i = malloc(sizeof(*i)); if (!i) { return NULL; } memset(i, 0, sizeof(*i)); stfu_n_init_aqueue(&i->a_queue, qlen); stfu_n_init_aqueue(&i->b_queue, qlen); i->in_queue = &i->a_queue; i->out_queue = &i->b_queue; return i; } void stfu_n_reset(stfu_instance_t *i) { i->in_queue = &i->a_queue; i->out_queue = &i->b_queue; i->in_queue->array_len = 0; i->out_queue->array_len = 0; i->out_queue->wr_len = 0; i->out_queue->last_index = 0; i->miss_count = 0; i->last_ts = 0; i->running = 0; i->miss_count = 0; i->interval = 0; } static int32_t stfu_n_measure_interval(stfu_queue_t *queue) { uint32_t index; int32_t d, most = 0, last = 0, this, track[STFU_MAX_TRACK] = {0}; for(index = 0; index < queue->array_len; index++) { this = queue->array[index].ts; if (last) { if ((d = this - last) > 0 && d / 10 < STFU_MAX_TRACK) { track[(d/10)]++; } } last = this; } for(index = 0; index < STFU_MAX_TRACK; index++) { if (track[index] > most) { most = index; } } return most * 10; } static int16_t stfu_n_process(stfu_instance_t *i, stfu_queue_t *queue) { if (!i->interval && !(i->interval = stfu_n_measure_interval(queue))) { return -1; } return 0; } stfu_status_t stfu_n_add_data(stfu_instance_t *i, uint32_t ts, void *data, size_t datalen, int last) { uint32_t index; stfu_frame_t *frame; size_t cplen = 0; if (last || i->in_queue->array_len == i->in_queue->array_size) { stfu_queue_t *other_queue; if (i->out_queue->wr_len < i->out_queue->array_len) { return STFU_IT_FAILED; } other_queue = i->in_queue; i->in_queue = i->out_queue; i->out_queue = other_queue; i->in_queue->array_len = 0; i->out_queue->wr_len = 0; i->out_queue->last_index = 0; i->miss_count = 0; if (stfu_n_process(i, i->out_queue) < 0) { return STFU_IT_FAILED; } for(index = 0; index < i->out_queue->array_len; index++) { i->out_queue->array[index].was_read = 0; } } if (last) { return STFU_IM_DONE; } index = i->in_queue->array_len++; frame = &i->in_queue->array[index]; if ((cplen = datalen) > sizeof(frame->data)) { cplen = sizeof(frame->data); } memcpy(frame->data, data, cplen); frame->ts = ts; frame->dlen = cplen; frame->was_read = 0; return STFU_IT_WORKED; } stfu_frame_t *stfu_n_read_a_frame(stfu_instance_t *i) { uint32_t index, index2; uint32_t should_have = 0; stfu_frame_t *frame = NULL, *rframe = NULL; if (((i->out_queue->wr_len == i->out_queue->array_len) || !i->out_queue->array_len)) { return NULL; } if (i->running) { should_have = i->last_ts + i->interval; } else { should_have = i->out_queue->array[0].ts; } for(index = 0; index < i->out_queue->array_len; index++) { if (i->out_queue->array[index].was_read) { continue; } frame = &i->out_queue->array[index]; if (frame->ts != should_have) { unsigned int tried = 0; for (index2 = 0; index2 < i->out_queue->array_len; index2++) { if (i->out_queue->array[index2].was_read) { continue; } tried++; if (i->out_queue->array[index2].ts == should_have) { rframe = &i->out_queue->array[index2]; i->out_queue->last_index = index2; goto done; } } for (index2 = 0; index2 < i->in_queue->array_len; index2++) { if (i->in_queue->array[index2].was_read) { continue; } tried++; if (i->in_queue->array[index2].ts == should_have) { rframe = &i->in_queue->array[index2]; goto done; } } i->miss_count++; if (i->miss_count > 10 || (i->in_queue->array_len == i->in_queue->array_size) || tried >= i->in_queue->array_size) { i->running = 0; i->interval = 0; i->out_queue->wr_len = i->out_queue->array_size; return NULL; } i->last_ts = should_have; rframe = &i->out_queue->int_frame; rframe->dlen = i->out_queue->array[i->out_queue->last_index].dlen; /* poor man's plc.. Copy the last frame, but we flag it so you can use a better one if you wish */ memcpy(rframe->data, i->out_queue->array[i->out_queue->last_index].data, rframe->dlen); rframe->ts = should_have; i->out_queue->wr_len++; i->running = 1; return rframe; } else { rframe = &i->out_queue->array[index]; i->out_queue->last_index = index; goto done; } } done: if (rframe) { i->out_queue->wr_len++; i->last_ts = rframe->ts; rframe->was_read = 1; i->running = 1; i->miss_count = 0; } return rframe; }