コード例 #1
0
ファイル: GQueueTest.cpp プロジェクト: FabianHahn/fakeglib
TEST_F(GQueueTest, pushTailLink)
{
	int testData1 = 42;
	int testData2 = 1337;

	GList *list = g_list_prepend(NULL, &testData1);
	g_queue_push_tail_link(queue, list);
	ASSERT_TRUE(queue->head != NULL) << "queue head should not be NULL after inserting an element";
	ASSERT_EQ(queue->head, queue->tail) << "queue tail should be equal to head after inserting an element";
	ASSERT_EQ(&testData1, queue->head->data) << "queue element data should be set correctly";
	ASSERT_TRUE(queue->head->next == NULL) << "queue head should not have a next element after inserting one";
	ASSERT_TRUE(queue->head->prev == NULL) << "queue tail should not have a next element after inserting one";
	ASSERT_EQ(1, queue->length) << "queue length should be one after inserting an element";

	list = g_list_prepend(NULL, &testData2);
	g_queue_push_tail_link(queue, list);
	ASSERT_TRUE(queue->head != NULL) << "queue head should not be NULL after inserting another element";
	ASSERT_NE(queue->head, queue->tail) << "queue tail should not be equal to head after inserting another element";
	ASSERT_EQ(&testData1, queue->head->data) << "second queue element data should be set correctly";
	ASSERT_EQ(queue->tail, queue->head->next) << "second queue head should have tail as next element";
	ASSERT_TRUE(queue->head->prev == NULL) << "queue head should not have a previous element";
	ASSERT_EQ(&testData2, queue->tail->data) << "queue tail data should be set correctly";
	ASSERT_EQ(queue->head, queue->tail->prev) << "queue tail should have head as previous element";
	ASSERT_TRUE(queue->tail->next == NULL) << "queue tail should not have a next element";
	ASSERT_EQ(2, queue->length) << "queue length should be two after inserting another element";
}
コード例 #2
0
ファイル: throttle.c プロジェクト: Aivaras/lighttpd2
static void throttle_register(liThrottlePoolWorkerState *pwstate, liThrottlePoolState *pstate) {
	if (NULL == pstate->pool_link.data) {
		g_queue_push_tail_link(&pwstate->waiting, &pstate->pool_link);
		pstate->pool_link.data = &pwstate->waiting;
		g_atomic_int_inc((gint*) &pwstate->connections);
	}
}
コード例 #3
0
ファイル: chassis-filter.c プロジェクト: api1024/DBProxy
void
sql_reserved_query_move_to_tail(sql_reserved_query *srq, reserved_query_item *rqi)
{
    GList   *to_tail = rqi->list_pos;
    GQueue  *gq = NULL;
    reserved_query_item *rm_rqi = NULL;
    int     ret = 0;

    if (srq == NULL || to_tail == NULL) return;

    ret = g_queue_link_index(srq->gq_reserved_long_query, to_tail);
    gq = (ret != -1) ? (srq->gq_reserved_long_query) : (srq->gq_reserved_short_query);

    if (g_queue_get_length(gq) == 0) return ;

    if (to_tail != gq->tail)
    {
        g_queue_unlink(gq, to_tail);
        g_queue_push_tail_link(gq, to_tail);
        rqi->list_pos = g_queue_peek_tail_link(gq);
    }
#ifdef FILTER_DEBUG
    g_queue_travel(srq->gq_reserved_long_query);
    g_queue_travel(srq->gq_reserved_short_query);
#endif

    return ;
}
コード例 #4
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
/* steal the first chunk from in and append it to out, return number of bytes stolen */
goffset li_chunkqueue_steal_chunk(liChunkQueue *out, liChunkQueue *in) {
	liChunk *c;
	goffset length;
	GList *l = g_queue_pop_head_link(&in->queue);
	if (!l) return 0;
	g_queue_push_tail_link(&out->queue, l);

	c = (liChunk*) l->data;
	length = li_chunk_length(c);
	in->bytes_out += length;
	in->length -= length;
	out->bytes_in += length;
	out->length += length;
	if (in->limit != out->limit) {
		if (c->type == STRING_CHUNK) {
			cqlimit_update(out, c->data.str->len);
			cqlimit_update(in, - (goffset)c->data.str->len);
		} else if (c->type == MEM_CHUNK) {
			cqlimit_update(out, c->mem->len);
			cqlimit_update(in, - (goffset)c->mem->len);
		} else if (c->type == BUFFER_CHUNK) {
			cqlimit_update(out, c->data.buffer.length);
			cqlimit_update(in, - (goffset)c->data.buffer.length);
		}
	}
	return length;
}
コード例 #5
0
static void
queue_insert_after_link (GQueue *queue,
                         GList  *sibling,
                         GList  *link_)
{
	if (sibling == queue->tail)
		g_queue_push_tail_link (queue, link_);
	else
		queue_insert_before_link (queue, sibling->next, link_);
}
コード例 #6
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
static void __chunkqueue_append_file(liChunkQueue *cq, GString *filename, off_t start, off_t length, int fd, gboolean is_temp) {
	liChunk *c = chunk_new();
	c->type = FILE_CHUNK;
	c->data.file.file = li_chunkfile_new(filename, fd, is_temp);
	c->data.file.start = start;
	c->data.file.length = length;

	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += length;
	cq->bytes_in += length;
}
コード例 #7
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
/* memory gets copied */
void li_chunkqueue_append_mem(liChunkQueue *cq, const void *mem, gssize len) {
	liChunk *c;
	if (!len) return;
	c = chunk_new();
	c->type = MEM_CHUNK;
	c->mem = g_byte_array_sized_new(len);
	g_byte_array_append(c->mem, mem, len);
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += c->mem->len;
	cq->bytes_in += c->mem->len;
	cqlimit_update(cq, c->mem->len);
}
コード例 #8
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
 /* pass ownership of mem to chunkqueue, do not free/modify it afterwards
  * you may modify the data (not the length) if you are sure it isn't sent before.
  * if the length is NULL, mem is destroyed immediately
  */
void li_chunkqueue_append_bytearr(liChunkQueue *cq, GByteArray *mem) {
	liChunk *c;
	if (!mem->len) {
		g_byte_array_free(mem, TRUE);
		return;
	}
	c = chunk_new();
	c->type = MEM_CHUNK;
	c->mem = mem;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += mem->len;
	cq->bytes_in += mem->len;
	cqlimit_update(cq, mem->len);
}
コード例 #9
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
 /* pass ownership of str to chunkqueue, do not free/modify it afterwards
  * you may modify the data (not the length) if you are sure it isn't sent before.
  * if the length is NULL, str is destroyed immediately
  */
void li_chunkqueue_append_string(liChunkQueue *cq, GString *str) {
	liChunk *c;
	if (!str->len) {
		g_string_free(str, TRUE);
		return;
	}
	c = chunk_new();
	c->type = STRING_CHUNK;
	c->data.str = str;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += str->len;
	cq->bytes_in += str->len;
	cqlimit_update(cq, str->len);
}
コード例 #10
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
/* increases reference for cf (if length > 0) */
void li_chunkqueue_append_chunkfile(liChunkQueue *cq, liChunkFile *cf, off_t start, off_t length) {
	if (length) {
		liChunk *c = chunk_new();
		li_chunkfile_acquire(cf);

		c->type = FILE_CHUNK;
		c->data.file.file = cf;
		c->data.file.start = start;
		c->data.file.length = length;

		g_queue_push_tail_link(&cq->queue, &c->cq_link);
		cq->length += length;
		cq->bytes_in += length;
	}
}
コード例 #11
0
ファイル: memcached.c プロジェクト: AlexShiLucky/lighttpd2
static gboolean push_request(liMemcachedCon *con, int_request *req, GError **err) {
	UNUSED(err);

	li_memcached_con_acquire(con);

	send_request(con, req);

	req->iter.data = req;
	g_queue_push_tail_link(&con->req_queue, &req->iter);

	memcached_start_io(con);
	li_event_io_set_events(&con->con_watcher, LI_EV_READ | LI_EV_WRITE);

	return TRUE;
}
コード例 #12
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
 /* pass ownership of one buffer reference to chunkqueue
  * if the length is NULL, reference is released immediately
  */
void li_chunkqueue_append_buffer(liChunkQueue *cq, liBuffer *buffer) {
	liChunk *c;
	if (!buffer->used) {
		li_buffer_release(buffer);
		return;
	}
	c = chunk_new();
	c->type = BUFFER_CHUNK;
	c->data.buffer.buffer = buffer;
	c->data.buffer.offset = 0;
	c->data.buffer.length = buffer->used;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += buffer->used;
	cq->bytes_in += buffer->used;
	cqlimit_update(cq, buffer->used);
}
コード例 #13
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
void li_chunkqueue_append_buffer2(liChunkQueue *cq, liBuffer *buffer, gsize offset, gsize length) {
	liChunk *c;
	if (length == 0) {
		li_buffer_release(buffer);
		return;
	}
	assert(offset + length <= buffer->used);
	c = chunk_new();
	c->type = BUFFER_CHUNK;
	c->data.buffer.buffer = buffer;
	c->data.buffer.offset = offset;
	c->data.buffer.length = length;
	g_queue_push_tail_link(&cq->queue, &c->cq_link);
	cq->length += length;
	cq->bytes_in += length;
	cqlimit_update(cq, length);
}
コード例 #14
0
ファイル: screen.c プロジェクト: msteinert/joybubbles
void
joy_gfx3d_screen_lower_window(JoyScreen *self, JoyBubble *window)
{
	struct Private *priv = GET_PRIVATE(self);
	if (G_UNLIKELY(!priv->windows)) {
		return;
	}
	gint n = g_queue_index(priv->windows, window);
	if (G_UNLIKELY(-1 == n)) {
		return;
	}
	GList *node = g_queue_pop_nth_link(priv->windows, n);
	if (G_UNLIKELY(!node)) {
		return;
	}
	g_queue_push_tail_link(priv->windows, node);
}
コード例 #15
0
ファイル: events.c プロジェクト: AlexShiLucky/lighttpd2
void li_event_add_closing_socket(liEventLoop *loop, int fd) {
	closing_socket *cs;

	if (-1 == fd) return;

	shutdown(fd, SHUT_WR);
	if (loop->end) {
		close(fd);
		return;
	}

	cs = g_slice_new0(closing_socket);
	cs->loop = loop;
	cs->fd = fd;
	g_queue_push_tail_link(&loop->closing_sockets, &cs->sockets_link);
	cs->close_timeout = li_event_now(loop) + 10.0;

	ev_once(loop->loop, fd, EV_READ, 10.0, closing_socket_cb, cs);
}
コード例 #16
0
ファイル: mod_lua.c プロジェクト: lzw978/lighttpd2
static lua_config* lua_config_new(liServer *srv, liPlugin *p, GString *filename, guint ttl, liValue *args) {
	module_config *mc = p->data;
	lua_config *conf = g_slice_new0(lua_config);

	conf->filename = filename;
	conf->ttl = ttl;
	conf->p = p;
	conf->args = args;

	lua_find_file(filename);

	if (LI_SERVER_INIT != g_atomic_int_get(&srv->state)) {
		conf->worker_config = g_slice_alloc0(sizeof(lua_worker_config) * srv->worker_count);
	} else {
		conf->mconf_link.data = conf;
		g_queue_push_tail_link(&mc->lua_configs, &conf->mconf_link);
	}

	return conf;
}
コード例 #17
0
ファイル: cogland.c プロジェクト: recrack/cogl-android
static void
cogland_surface_frame (struct wl_client *client,
                       struct wl_resource *surface_resource,
                       uint32_t callback_id)
{
  CoglandFrameCallback *callback;
  CoglandSurface *surface = surface_resource->data;

  callback = g_slice_new0 (CoglandFrameCallback);
  callback->compositor = surface->compositor;
  callback->node.data = callback;
  callback->resource.object.interface = &wl_callback_interface;
  callback->resource.object.id = callback_id;
  callback->resource.destroy = destroy_frame_callback;
  callback->resource.data = callback;

  wl_client_add_resource (client, &callback->resource);

  g_queue_push_tail_link (&surface->compositor->frame_callbacks,
                          &callback->node);
}
コード例 #18
0
ファイル: gqueue.c プロジェクト: antono/glib
/**
 * g_queue_push_nth_link:
 * @queue: a #GQueue
 * @n: the position to insert the link. If this is negative or larger than
 *     the number of elements in @queue, the link is added to the end of
 *     @queue.
 * @link_: the link to add to @queue
 * 
 * Inserts @link into @queue at the given position.
 * 
 * Since: 2.4
 **/
void
g_queue_push_nth_link  (GQueue  *queue,
			gint     n,
			GList   *link_)
{
  GList *next;
  GList *prev;
  
  g_return_if_fail (queue != NULL);
  g_return_if_fail (link_ != NULL);

  if (n < 0 || n >= queue->length)
    {
      g_queue_push_tail_link (queue, link_);
      return;
    }

  g_assert (queue->head);
  g_assert (queue->tail);

  next = g_queue_peek_nth_link (queue, n);
  prev = next->prev;

  if (prev)
    prev->next = link_;
  next->prev = link_;

  link_->next = next;
  link_->prev = prev;

  if (queue->head->prev)
    queue->head = queue->head->prev;

  if (queue->tail->next)
    queue->tail = queue->tail->next;
  
  queue->length++;
}
コード例 #19
0
ファイル: jobqueue.c プロジェクト: Aivaras/lighttpd2
void li_job_later(liJobQueue *jq, liJob *job) {
	if (NULL != job->link.data) return; /* already queued */

	job->link.data = jq;
	g_queue_push_tail_link(&jq->queue, &job->link);
}
コード例 #20
0
ファイル: mod_memcached.c プロジェクト: Aivaras/lighttpd2
static memcached_ctx* mc_ctx_parse(liServer *srv, liPlugin *p, liValue *config) {
	memcached_ctx *ctx;
	memcached_config *mconf = p->data;
	GString def_server = li_const_gstring(CONST_STR_LEN("127.0.0.1:11211"));

	if (config && config->type != LI_VALUE_HASH) {
		ERROR(srv, "%s", "memcache expects an optional hash of options");
		return NULL;
	}

	ctx = g_slice_new0(memcached_ctx);
	ctx->srv = srv;
	ctx->refcount = 1;
	ctx->p = p;

	ctx->addr = li_sockaddr_from_string(&def_server, 11211);

	ctx->pattern = li_pattern_new(srv, "%{req.path}");

	ctx->flags = 0;
	ctx->ttl = 30;
	ctx->maxsize = 64*1024; /* 64 kB */
	ctx->headers = FALSE;

	if (config) {
		GHashTable *ht = config->data.hash;
		GHashTableIter it;
		gpointer pkey, pvalue;

		g_hash_table_iter_init(&it, ht);
		while (g_hash_table_iter_next(&it, &pkey, &pvalue)) {
			GString *key = pkey;
			liValue *value = pvalue;

			if (g_string_equal(key, &mon_server)) {
				if (value->type != LI_VALUE_STRING) {
					ERROR(srv, "memcache option '%s' expects string as parameter", mon_server.str);
					goto option_failed;
				}
				li_sockaddr_clear(&ctx->addr);
				ctx->addr = li_sockaddr_from_string(value->data.string, 11211);
				if (NULL == ctx->addr.addr) {
					ERROR(srv, "invalid socket address: '%s'", value->data.string->str);
					goto option_failed;
				}
			} else if (g_string_equal(key, &mon_key)) {
				if (value->type != LI_VALUE_STRING) {
					ERROR(srv, "memcache option '%s' expects string as parameter", mon_key.str);
					goto option_failed;
				}
				li_pattern_free(ctx->pattern);
				ctx->pattern = li_pattern_new(srv,  value->data.string->str);
				if (NULL == ctx->pattern) {
					ERROR(srv, "memcache: couldn't parse pattern for key '%s'", value->data.string->str);
					goto option_failed;
				}
			} else if (g_string_equal(key, &mon_flags)) {
				if (value->type != LI_VALUE_NUMBER || value->data.number <= 0) {
					ERROR(srv, "memcache option '%s' expects positive integer as parameter", mon_flags.str);
					goto option_failed;
				}
				ctx->flags = value->data.number;
			} else if (g_string_equal(key, &mon_ttl)) {
				if (value->type != LI_VALUE_NUMBER || value->data.number < 0) {
					ERROR(srv, "memcache option '%s' expects non-negative integer as parameter", mon_ttl.str);
					goto option_failed;
				}
				ctx->ttl = value->data.number;
			} else if (g_string_equal(key, &mon_maxsize)) {
				if (value->type != LI_VALUE_NUMBER || value->data.number <= 0) {
					ERROR(srv, "memcache option '%s' expects positive integer as parameter", mon_maxsize.str);
					goto option_failed;
				}
				ctx->maxsize = value->data.number;
			} else if (g_string_equal(key, &mon_headers)) {
				if (value->type != LI_VALUE_BOOLEAN) {
					ERROR(srv, "memcache option '%s' expects boolean as parameter", mon_headers.str);
					goto option_failed;
				}
				ctx->headers = value->data.boolean;
				if (ctx->headers) {
					ERROR(srv, "%s", "memcache: lookup/storing headers not supported yet");
					goto option_failed;
				}
			} else {
				ERROR(srv, "unknown option for memcache '%s'", key->str);
				goto option_failed;
			}
		}
	}

	if (LI_SERVER_INIT != g_atomic_int_get(&srv->state)) {
		ctx->worker_client_ctx = g_slice_alloc0(sizeof(liMemcachedCon*) * srv->worker_count);
	} else {
		ctx->mconf_link.data = ctx;
		g_queue_push_tail_link(&mconf->prepare_ctx, &ctx->mconf_link);
	}

	return ctx;

option_failed:
	mc_ctx_release(NULL, ctx);
	return NULL;
}
コード例 #21
0
ファイル: notification.c プロジェクト: sardemff7/eventd
static void
_eventd_nd_notification_refresh_list(EventdPluginContext *context, EventdNdQueue *queue)
{
    if ( queue->more_notification != NULL )
    {
        g_queue_pop_tail_link(queue->queue);
        queue->more_notification->visible = FALSE;
    }

    while ( ( g_queue_get_length(queue->queue) < queue->limit ) && ( ! g_queue_is_empty(queue->wait_queue) ) )
    {
        GList *link;
        link = g_queue_pop_head_link(queue->wait_queue);
        if ( queue->reverse )
            g_queue_push_tail_link(queue->queue, link);
        else
            g_queue_push_head_link(queue->queue, link);

        EventdNdNotification *self = link->data;
        gint timeout;
        timeout = eventd_nd_style_get_bubble_timeout(self->style);
        if ( timeout > 0 )
            self->timeout = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout, _eventd_nd_event_timedout, self, NULL);
        self->visible = TRUE;
    }

    if ( queue->more_indicator )
    {
        if ( ! g_queue_is_empty(queue->wait_queue) )
        {
            if ( queue->more_notification == NULL )
                queue->more_notification = eventd_nd_notification_new(context, NULL, context->style);
            else
            {
                _eventd_nd_notification_update(queue->more_notification, NULL);
                g_queue_push_tail_link(queue->queue, queue->more_notification->link);
            }
            queue->more_notification->visible = TRUE;
        }
        else if ( queue->more_notification != NULL )
            eventd_nd_notification_free(queue->more_notification);
    }

    gpointer data = NULL;
    if ( context->backend->move_begin != NULL )
        data = context->backend->move_begin(context->backend->context, g_queue_get_length(queue->queue));

    gboolean right, center, bottom;
    right = ( queue->anchor == EVENTD_ND_ANCHOR_TOP_RIGHT ) || ( queue->anchor == EVENTD_ND_ANCHOR_BOTTOM_RIGHT );
    center = ( queue->anchor == EVENTD_ND_ANCHOR_TOP ) || ( queue->anchor == EVENTD_ND_ANCHOR_BOTTOM );
    bottom = ( queue->anchor == EVENTD_ND_ANCHOR_BOTTOM_LEFT ) || ( queue->anchor == EVENTD_ND_ANCHOR_BOTTOM ) || ( queue->anchor == EVENTD_ND_ANCHOR_BOTTOM_RIGHT );

    gint bx, by;
    bx = queue->margin_x;
    by = queue->margin_y;
    if ( center )
        bx = context->geometry.w;
    else if ( right )
        bx = context->geometry.w - bx;
    if ( bottom )
        by = context->geometry.h - by;
    GList *self_;
    for ( self_ = g_queue_peek_head_link(queue->queue) ; self_ != NULL ; self_ = g_list_next(self_) )
    {
        EventdNdNotification *self = self_->data;

        if ( bottom )
            by -= self->border_size.height;

        gint x, y;
        x = center ? ( ( bx / 2 ) - ( self->border_size.width / 2 ) ) : right ? ( bx - self->border_size.width ) : bx;
        y = by;
        x -= self->offset.x;
        y -= self->offset.y;
        context->backend->move_surface(self->surface, x, y, data);

        if ( bottom )
            by -= queue->spacing;
        else
            by += self->border_size.height + queue->spacing;
    }

    if ( context->backend->move_end != NULL )
        context->backend->move_end(context->backend->context, data);
}
コード例 #22
0
static void
dspy_introspection_model_init_parse_cb (GObject      *object,
                                        GAsyncResult *result,
                                        gpointer      user_data)
{
  DspyIntrospectionModel *self = (DspyIntrospectionModel *)object;
  g_autoptr(Introspect) state = user_data;
  g_autoptr(GError) error = NULL;
  DspyNodeInfo *info = NULL;
  GCancellable *cancellable;
  gint *n_active;

  g_assert (DSPY_IS_INTROSPECTION_MODEL (self));
  g_assert (G_IS_ASYNC_RESULT (result));
  g_assert (state != NULL);
  g_assert (G_IS_TASK (state->task));
  g_assert (state->path != NULL);

  self = g_task_get_source_object (state->task);
  n_active = g_task_get_task_data (state->task);
  cancellable = g_task_get_cancellable (state->task);

  g_assert (self != NULL);
  g_assert (DSPY_IS_INTROSPECTION_MODEL (self));
  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
  g_assert (n_active != NULL);
  g_assert (*n_active > 0);

  if ((info = parse_xml_finish (self, result, &error)))
    {
      g_assert (DSPY_IS_NODE (info));
      g_assert (info->kind == DSPY_NODE_KIND_NODE);

      /* First, queue a bunch of sub-path reads based on any discovered
       * nodes from querying this specific node.
       */
      for (const GList *iter = info->nodes.head; iter; iter = iter->next)
        {
          DspyNodeInfo *child = iter->data;
          g_autofree gchar *child_path = NULL;

          g_assert (child != NULL);
          g_assert (DSPY_IS_NODE (child));
          g_assert (child->kind == DSPY_NODE_KIND_NODE);

          child_path = g_build_path ("/", state->path, child->path, NULL);
          dspy_introspection_model_introspect (state->task, state->connection, child_path);
        }

      /* Now add this node to our root if it contains any intefaces. */
      if (info->interfaces->interfaces.length > 0)
        {
          g_autofree gchar *abs_path = g_build_path ("/", state->path, info->path, NULL);

          g_mutex_lock (&self->chunks_mutex);
          info->path = g_string_chunk_insert_const (self->chunks, abs_path);
          g_mutex_unlock (&self->chunks_mutex);

          g_queue_push_tail_link (&self->root->nodes, &info->link);
          info->parent = (DspyNode *)self->root;

          emit_row_inserted_for_tree (self, (DspyNode *)info);

          /* Stolen */
          info = NULL;
        }

      g_clear_pointer (&info, _dspy_node_free);
    }

  if (--(*n_active) == 0)
    g_task_return_boolean (state->task, TRUE);
}
コード例 #23
0
ファイル: chunk.c プロジェクト: soarpenguin/lighttpd2
/* steal up to length bytes from in and put them into out, return number of bytes stolen */
goffset li_chunkqueue_steal_len(liChunkQueue *out, liChunkQueue *in, goffset length) {
	liChunk *c, *cnew;
	GList* l;
	goffset bytes = 0, meminbytes = 0, memoutbytes = 0;
	goffset we_have;

	while ( (NULL != (c = li_chunkqueue_first_chunk(in))) && length > 0 ) {
		we_have = li_chunk_length(c);
		if (!we_have) { /* remove empty chunks */
			if (c->type == STRING_CHUNK) meminbytes -= c->data.str->len;
			else if (c->type == MEM_CHUNK) meminbytes -= c->mem->len;
			else if (c->type == BUFFER_CHUNK) meminbytes -= c->data.buffer.length;
			chunk_free(in, c);
			continue;
		}
		if (we_have <= length) { /* move complete chunk */
			l = g_queue_pop_head_link(&in->queue);
			g_queue_push_tail_link(&out->queue, l);
			bytes += we_have;
			if (c->type == STRING_CHUNK) {
				meminbytes -= c->data.str->len;
				memoutbytes += c->data.str->len;
			} else if (c->type == MEM_CHUNK) {
				meminbytes -= c->mem->len;
				memoutbytes += c->mem->len;
			} else if (c->type == BUFFER_CHUNK) {
				meminbytes -= c->data.buffer.length;
				memoutbytes += c->data.buffer.length;
			}
			length -= we_have;
		} else { /* copy first part of a chunk */
			cnew = chunk_new();
			switch (c->type) {
			case UNUSED_CHUNK: /* impossible, has length 0 */
				/* remove "empty" chunks */
				chunk_free(in, c);
				chunk_free(NULL, cnew);
				continue;
			case STRING_CHUNK: /* change type to MEM_CHUNK, as we copy it anyway */
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->data.str->str + c->offset, length);
				memoutbytes += length;
				break;
			case MEM_CHUNK:
				cnew->type = MEM_CHUNK;
				cnew->mem = g_byte_array_sized_new(length);
				g_byte_array_append(cnew->mem, (guint8*) c->mem->data + c->offset, length);
				memoutbytes += length;
				break;
			case FILE_CHUNK:
				cnew->type = FILE_CHUNK;
				li_chunkfile_acquire(c->data.file.file);
				cnew->data.file.file = c->data.file.file;
				cnew->data.file.start = c->data.file.start + c->offset;
				cnew->data.file.length = length;
				break;
			case BUFFER_CHUNK:
				cnew->type = BUFFER_CHUNK;
				li_buffer_acquire(c->data.buffer.buffer);
				cnew->data.buffer.buffer = c->data.buffer.buffer;
				cnew->data.buffer.offset = c->data.buffer.offset + c->offset;
				cnew->data.buffer.length = length;
				memoutbytes += length;
				break;
			}
			c->offset += length;
			bytes += length;
			length = 0;
			g_queue_push_tail_link(&out->queue, &cnew->cq_link);
		}
	}

	in->bytes_out += bytes;
	in->length -= bytes;
	out->bytes_in += bytes;
	out->length += bytes;
	cqlimit_update(out, memoutbytes);
	cqlimit_update(in, meminbytes);

	return bytes;
}