/* $%BEGINLICENSE%$
Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; version 2 of the
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
$%ENDLICENSE%$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "network-queue.h"
#ifndef DISABLE_DEPRECATED_DECL
network_queue *network_queue_init() {
return network_queue_new();
}
#endif
network_queue *network_queue_new() {
network_queue *queue;
queue = g_new0(network_queue, 1);
queue->chunks = g_queue_new();
return queue;
}
void network_queue_free(network_queue *queue) {
GString *packet;
if (!queue) return;
while ((packet = g_queue_pop_head(queue->chunks))) g_string_free(packet, TRUE);
g_queue_free(queue->chunks);
g_free(queue);
}
int network_queue_append(network_queue *queue, GString *s) {
queue->len += s->len;
g_queue_push_tail(queue->chunks, s);
return 0;
}
/**
* get a string from the head of the queue and leave the queue unchanged
*
* @param queue the queue to read from
* @param peek_len bytes to collect
* @param dest GString to write it. If NULL, we allow a new one and return it
* @return NULL if not enough data
* if dest is not NULL, dest, otherwise a new GString containing the data
*/
GString *network_queue_peek_string(network_queue *queue, gsize peek_len, GString *dest) {
gsize we_want = peek_len;
GList *node;
/* TODO: convert to DTrace probe
g_debug("[%s] looking for %d bytes, queue has %d", G_STRLOC, peek_len, queue->len); */
if (queue->len < peek_len) {
return NULL;
}
if (!dest) {
/* no define */
dest = g_string_sized_new(peek_len);
}
g_assert_cmpint(dest->allocated_len, >, peek_len);
for (node = queue->chunks->head; node && we_want; node = node->next) {
GString *chunk = node->data;
if (node == queue->chunks->head) {
gsize we_have = we_want < (chunk->len - queue->offset) ? we_want : (chunk->len - queue->offset);
g_string_append_len(dest, chunk->str + queue->offset, we_have);
we_want -= we_have;
} else {
gsize we_have = we_want < chunk->len ? we_want : chunk->len;
g_string_append_len(dest, chunk->str, we_have);
we_want -= we_have;
}
}
return dest;
}
/**
* get a string from the head of the queue and remove the chunks from the queue
*/
GString *network_queue_pop_string(network_queue *queue, gsize steal_len, GString *dest) {
gsize we_want = steal_len;
GString *chunk;
if (queue->len < steal_len) {
return NULL;
}
while ((chunk = g_queue_peek_head(queue->chunks))) {
gsize we_have = we_want < (chunk->len - queue->offset) ? we_want : (chunk->len - queue->offset);
if (!dest && (queue->offset == 0) && (chunk->len == steal_len)) {
/* optimize the common case that we want to have to full chunk
*
* if dest is null, we can remove the GString from the queue and return it directly without
* copying it
*/
dest = g_queue_pop_head(queue->chunks);
queue->len -= we_have;
return dest;
}
if (!dest) {
/* if we don't have a dest-buffer yet, create one */
dest = g_string_sized_new(steal_len);
}
g_string_append_len(dest, chunk->str + queue->offset, we_have);
queue->offset += we_have;
queue->len -= we_have;
we_want -= we_have;
if (chunk->len == queue->offset) {
/* the chunk is done, remove it */
g_string_free(g_queue_pop_head(queue->chunks), TRUE);
queue->offset = 0;
} else {
break;
}
}
return dest;
}