Reuse buffers when unpacking frames.

This commit is contained in:
Tatsuhiro Tsujikawa 2012-02-16 22:01:34 +09:00
parent 050f33e8f9
commit ac1629e61b
9 changed files with 160 additions and 96 deletions

View File

@ -29,7 +29,9 @@
void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity)
{
spdylay_queue_init(&buffer->q);
buffer->root.data = NULL;
buffer->root.next = NULL;
buffer->current = &buffer->root;
buffer->capacity = chunk_capacity;
buffer->len = 0;
/*
@ -41,22 +43,35 @@ void spdylay_buffer_init(spdylay_buffer *buffer, size_t chunk_capacity)
void spdylay_buffer_free(spdylay_buffer *buffer)
{
while(!spdylay_queue_empty(&buffer->q)) {
free(spdylay_queue_front(&buffer->q));
spdylay_queue_pop(&buffer->q);
spdylay_buffer_chunk *p = buffer->root.next;
while(p) {
spdylay_buffer_chunk *next = p->next;
free(p->data);
free(p);
p = next;
}
}
int spdylay_buffer_alloc(spdylay_buffer *buffer)
{
int r;
uint8_t *buf = (uint8_t*)malloc(buffer->capacity);
if(buf == NULL) {
return SPDYLAY_ERR_NOMEM;
}
if((r = spdylay_queue_push(&buffer->q, buf)) != 0) {
free(buf);
return r;
if(buffer->current->next == NULL) {
spdylay_buffer_chunk *chunk;
uint8_t *buf;
chunk = malloc(sizeof(spdylay_buffer_chunk));
if(chunk == NULL) {
return SPDYLAY_ERR_NOMEM;
}
buf = malloc(buffer->capacity);
if(buf == NULL) {
free(chunk);
return SPDYLAY_ERR_NOMEM;
}
chunk->data = buf;
chunk->next = NULL;
buffer->current->next = chunk;
buffer->current = chunk;
} else {
buffer->current = buffer->current->next;
}
buffer->len += buffer->capacity-buffer->last_offset;
buffer->last_offset = 0;
@ -65,10 +80,10 @@ int spdylay_buffer_alloc(spdylay_buffer *buffer)
uint8_t* spdylay_buffer_get(spdylay_buffer *buffer)
{
if(spdylay_queue_empty(&buffer->q)) {
if(buffer->current->data == NULL) {
return NULL;
} else {
return spdylay_queue_back(&buffer->q)+buffer->last_offset;
return buffer->current->data+buffer->last_offset;
}
}
@ -89,40 +104,6 @@ size_t spdylay_buffer_length(spdylay_buffer *buffer)
return buffer->len;
}
size_t spdylay_buffer_front_length(spdylay_buffer *buffer)
{
if(spdylay_queue_empty(&buffer->q)) {
return 0;
} else if(buffer->len >= buffer->capacity) {
return buffer->capacity;
} else {
return buffer->len;
}
}
uint8_t* spdylay_buffer_front_data(spdylay_buffer *buffer)
{
if(spdylay_queue_empty(&buffer->q)) {
return NULL;
} else {
return spdylay_queue_front(&buffer->q);
}
}
void spdylay_buffer_pop(spdylay_buffer *buffer)
{
if(!spdylay_queue_empty(&buffer->q)) {
if(buffer->len >= buffer->capacity) {
buffer->len -= buffer->capacity;
} else {
buffer->len = 0;
buffer->last_offset = buffer->capacity;
}
free(spdylay_queue_front(&buffer->q));
spdylay_queue_pop(&buffer->q);
}
}
size_t spdylay_buffer_capacity(spdylay_buffer *buffer)
{
return buffer->capacity;
@ -130,10 +111,22 @@ size_t spdylay_buffer_capacity(spdylay_buffer *buffer)
void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf)
{
while(spdylay_buffer_length(buffer)) {
size_t len = spdylay_buffer_front_length(buffer);
memcpy(buf, spdylay_buffer_front_data(buffer), len);
spdylay_buffer_chunk *p = buffer->root.next;
for(; p; p = p->next) {
size_t len;
if(p == buffer->current) {
len = buffer->last_offset;
} else {
len = buffer->capacity;
}
memcpy(buf, p->data, len);
buf += len;
spdylay_buffer_pop(buffer);
}
}
void spdylay_buffer_reset(spdylay_buffer *buffer)
{
buffer->current = &buffer->root;
buffer->len = 0;
buffer->last_offset = buffer->capacity;
}

View File

@ -32,14 +32,22 @@
#include <spdylay/spdylay.h>
#include "spdylay_queue.h"
typedef struct spdylay_buffer_chunk {
uint8_t *data;
struct spdylay_buffer_chunk *next;
} spdylay_buffer_chunk;
/*
* Fixed sized buffers backed by queue.
* List of fixed sized chunks
*/
typedef struct {
/* Capacity of each chunk buffer */
size_t capacity;
/* Queue of chunk buffers */
spdylay_queue q;
/* Root of list of chunk buffers. The root is dummy and its data
member is always NULL. */
spdylay_buffer_chunk root;
/* Points to the current chunk to write */
spdylay_buffer_chunk *current;
/* Total length of this buffer */
size_t len;
/* Offset of last chunk buffer */
@ -66,20 +74,17 @@ int spdylay_buffer_alloc(spdylay_buffer *buffer);
/* Returns total length of buffer */
size_t spdylay_buffer_length(spdylay_buffer *buffer);
/* Returns first chunk buffer length. If the chunk buffer is the last
one, last_offset will be returned. */
size_t spdylay_buffer_front_length(spdylay_buffer *buffer);
/* Returns first chunk buffer pointer */
uint8_t* spdylay_buffer_front_data(spdylay_buffer *buffer);
/* Pops first chunk buffer and decreases total length of buffer by the
size of poped chunk buffer. */
void spdylay_buffer_pop(spdylay_buffer *buffer);
/* Returns capacity of each fixed chunk buffer */
size_t spdylay_buffer_capacity(spdylay_buffer *buffer);
/* Stores the contents of buffer into buf. buf must be at least
/* Stores the contents of buffer into |buf|. |buf| must be at least
spdylay_buffer_length(buffer) bytes long. */
void spdylay_buffer_serialize(spdylay_buffer *buffer, uint8_t *buf);
/* Reset |buffer| for reuse. Set the total length of buffer to 0.
Next spdylay_buffer_avail() returns 0. This function does not free
allocated memory space; they are reused. */
void spdylay_buffer_reset(spdylay_buffer *buffer);
#endif /* SPDYLAY_BUFFER_H */

View File

@ -193,26 +193,24 @@ int spdylay_frame_unpack_nv(char ***nv_ptr, const uint8_t *in, size_t inlen)
}
static int spdylay_frame_alloc_unpack_nv(char ***nv_ptr,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *in, size_t inlen,
spdylay_zlib *inflater)
{
ssize_t r;
spdylay_buffer outbuffer;
spdylay_buffer_init(&outbuffer, 4096);
r = spdylay_zlib_inflate_hd(inflater, &outbuffer, in, inlen);
if(r < 0) {
spdylay_buffer_free(&outbuffer);
return r;
ssize_t nvspace;
int r;
nvspace = spdylay_zlib_inflate_hd(inflater, inflatebuf, in, inlen);
if(nvspace < 0) {
return nvspace;
} else {
uint8_t *buf = malloc(r);
if(buf == NULL) {
spdylay_buffer_free(&outbuffer);
r = spdylay_reserve_buffer(nvbuf_ptr, nvbuflen_ptr, nvspace);
if(r != 0) {
return SPDYLAY_ERR_NOMEM;
}
spdylay_buffer_serialize(&outbuffer, buf);
spdylay_buffer_free(&outbuffer);
r = spdylay_frame_unpack_nv(nv_ptr, buf, r);
free(buf);
spdylay_buffer_serialize(inflatebuf, *nvbuf_ptr);
r = spdylay_frame_unpack_nv(nv_ptr, *nvbuf_ptr, nvspace);
return r;
}
}
@ -485,6 +483,9 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,
}
int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
@ -498,7 +499,9 @@ int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
frame->assoc_stream_id =
spdylay_get_uint32(payload+4) & SPDYLAY_STREAM_ID_MASK;
frame->pri = spdylay_unpack_pri(payload+8);
r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+10, payloadlen-10,
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+10, payloadlen-10,
inflater);
return r;
}
@ -528,6 +531,9 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
}
int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
@ -538,7 +544,9 @@ int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6,
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+6, payloadlen-6,
inflater);
return r;
}
@ -621,6 +629,9 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,
}
int spdylay_frame_unpack_headers(spdylay_headers *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater)
@ -631,7 +642,9 @@ int spdylay_frame_unpack_headers(spdylay_headers *frame,
}
spdylay_frame_unpack_ctrl_hd(&frame->hd, head);
frame->stream_id = spdylay_get_uint32(payload) & SPDYLAY_STREAM_ID_MASK;
r = spdylay_frame_alloc_unpack_nv(&frame->nv, payload+6, payloadlen-6,
r = spdylay_frame_alloc_unpack_nv(&frame->nv, inflatebuf,
nvbuf_ptr, nvbuflen_ptr,
payload+6, payloadlen-6,
inflater);
return r;
}

View File

@ -70,9 +70,19 @@ ssize_t spdylay_frame_pack_syn_stream(uint8_t **buf_ptr,
* Unpacks SYN_STREAM frame byte sequence into |frame|. Header is
* given in head and headlen. In spdy/2 spec, headlen is 8
* bytes. |payload| is the data after length field of the header.
*
* |inflatebuf| is used to buffer name/value pairs while inflating
* them using |inflater|. The caller must reset |inflatebuf| before
* the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
* necessary and updates these variables.
*
* This function returns 0 if it succeeds or negative error code.
*/
int spdylay_frame_unpack_syn_stream(spdylay_syn_stream *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater);
@ -99,10 +109,20 @@ ssize_t spdylay_frame_pack_syn_reply(uint8_t **buf_ptr,
spdylay_zlib *deflater);
/*
* Unpacks SYN_REPLY frame byte sequence into |frame|. This function
* returns 0 if it succeeds or negative error code.
* Unpacks SYN_REPLY frame byte sequence into |frame|.
*
* |inflatebuf| is used to buffer name/value pairs while inflating
* them using |inflater|. The caller must reset |inflatebuf| before
* the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
* necessary and updates these variables.
*
* This function returns 0 if it succeeds or negative error code.
*/
int spdylay_frame_unpack_syn_reply(spdylay_syn_reply *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater);
@ -163,10 +183,20 @@ ssize_t spdylay_frame_pack_headers(uint8_t **buf_ptr, size_t *buflen_ptr,
spdylay_zlib *deflater);
/*
* Unpacks HEADERS wire format into |frame|. This function returns 0
* if it succeeds or negative error code.
* Unpacks HEADERS wire format into |frame|.
*
* |inflatebuf| is used to buffer name/value pairs while inflating
* them using |inflater|. The caller must reset |inflatebuf| before
* the call. |*nvbuf_ptr|, |*nvbuflen_ptr| is used to store temporal
* inflated name/value pairs. This function expands |*nvbuf_ptr| as
* necessary and updates these variables.
*
* This function returns 0 if it succeeds or negative error code.
*/
int spdylay_frame_unpack_headers(spdylay_headers *frame,
spdylay_buffer *inflatebuf,
uint8_t **nvbuf_ptr,
size_t *nvbuflen_ptr,
const uint8_t *head, size_t headlen,
const uint8_t *payload, size_t payloadlen,
spdylay_zlib *inflater);

View File

@ -155,6 +155,8 @@ static int spdylay_session_new(spdylay_session **session_ptr,
}
(*session_ptr)->nvbuflen = SPDYLAY_INITIAL_NV_BUFFER_LENGTH;
spdylay_buffer_init(&(*session_ptr)->inflatebuf, 4096);
memset((*session_ptr)->settings, 0, sizeof((*session_ptr)->settings));
(*session_ptr)->settings[SPDYLAY_SETTINGS_MAX_CONCURRENT_STREAMS] =
SPDYLAY_CONCURRENT_STREAMS_MAX;
@ -271,6 +273,7 @@ void spdylay_session_del(spdylay_session *session)
free(session->iframe.buf);
free(session->aob.framebuf);
free(session->nvbuf);
spdylay_buffer_free(&session->inflatebuf);
free(session);
}
@ -1305,7 +1308,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
type = ntohs(type);
switch(type) {
case SPDYLAY_SYN_STREAM:
spdylay_buffer_reset(&session->inflatebuf);
r = spdylay_frame_unpack_syn_stream(&frame.syn_stream,
&session->inflatebuf,
&session->nvbuf,
&session->nvbuflen,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,
@ -1321,7 +1328,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
}
break;
case SPDYLAY_SYN_REPLY:
spdylay_buffer_reset(&session->inflatebuf);
r = spdylay_frame_unpack_syn_reply(&frame.syn_reply,
&session->inflatebuf,
&session->nvbuf,
&session->nvbuflen,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,
@ -1379,7 +1390,11 @@ static int spdylay_session_process_ctrl_frame(spdylay_session *session)
}
break;
case SPDYLAY_HEADERS:
spdylay_buffer_reset(&session->inflatebuf);
r = spdylay_frame_unpack_headers(&frame.headers,
&session->inflatebuf,
&session->nvbuf,
&session->nvbuflen,
session->iframe.headbuf,
sizeof(session->iframe.headbuf),
session->iframe.buf,

View File

@ -35,6 +35,7 @@
#include "spdylay_frame.h"
#include "spdylay_zlib.h"
#include "spdylay_stream.h"
#include "spdylay_buffer.h"
typedef struct {
spdylay_frame_type frame_type;
@ -130,6 +131,9 @@ struct spdylay_session {
uint8_t *nvbuf;
/* The number of bytes allocated for nvbuf */
size_t nvbuflen;
/* Buffer used to store name/value pairs while inflating them using
zlib on unpack */
spdylay_buffer inflatebuf;
spdylay_zlib hd_deflater;
spdylay_zlib hd_inflater;

View File

@ -33,6 +33,7 @@
void test_spdylay_buffer()
{
spdylay_buffer buffer;
uint8_t out[1024];
spdylay_buffer_init(&buffer, 8);
CU_ASSERT(0 == spdylay_buffer_length(&buffer));
CU_ASSERT(0 == spdylay_buffer_avail(&buffer));
@ -58,26 +59,23 @@ void test_spdylay_buffer()
CU_ASSERT(1 == spdylay_buffer_avail(&buffer));
CU_ASSERT(8 == spdylay_buffer_front_length(&buffer));
CU_ASSERT(memcmp("01234567", spdylay_buffer_front_data(&buffer), 8) == 0);
spdylay_buffer_pop(&buffer);
spdylay_buffer_serialize(&buffer, out);
CU_ASSERT(0 == memcmp("0123456789ABCDE", out, 15));
CU_ASSERT(7 == spdylay_buffer_length(&buffer));
CU_ASSERT(memcmp("89ABCDE", spdylay_buffer_front_data(&buffer), 7) == 0);
spdylay_buffer_pop(&buffer);
spdylay_buffer_reset(&buffer);
CU_ASSERT(0 == spdylay_buffer_length(&buffer));
CU_ASSERT(0 == spdylay_buffer_avail(&buffer));
CU_ASSERT(NULL == spdylay_buffer_get(&buffer));
CU_ASSERT(0 == spdylay_buffer_alloc(&buffer));
CU_ASSERT(8 == spdylay_buffer_avail(&buffer));
memcpy(spdylay_buffer_get(&buffer), "34567", 5);
memcpy(spdylay_buffer_get(&buffer), "Hello", 5);
spdylay_buffer_advance(&buffer, 5);
CU_ASSERT(5 == spdylay_buffer_length(&buffer));
CU_ASSERT(memcmp("34567", spdylay_buffer_front_data(&buffer), 5) == 0);
spdylay_buffer_serialize(&buffer, out);
CU_ASSERT(0 == memcmp("Hello", out, 5));
spdylay_buffer_free(&buffer);
}

View File

@ -140,7 +140,9 @@ void test_spdylay_frame_pack_headers()
spdylay_frame frame, oframe;
uint8_t *buf = NULL, *nvbuf = NULL;
size_t buflen = 0, nvbuflen = 0;
spdylay_buffer inflatebuf;
ssize_t framelen;
spdylay_buffer_init(&inflatebuf, 4096);
spdylay_zlib_deflate_hd_init(&deflater);
spdylay_zlib_inflate_hd_init(&inflater);
spdylay_frame_headers_init(&frame.headers, SPDYLAY_FLAG_FIN, 3,
@ -150,6 +152,8 @@ void test_spdylay_frame_pack_headers()
&frame.headers, &deflater);
CU_ASSERT(0 == spdylay_frame_unpack_headers
(&oframe.headers,
&inflatebuf,
&nvbuf, &nvbuflen,
&buf[0], SPDYLAY_FRAME_HEAD_LENGTH,
&buf[SPDYLAY_FRAME_HEAD_LENGTH],
framelen-SPDYLAY_FRAME_HEAD_LENGTH,
@ -168,6 +172,7 @@ void test_spdylay_frame_pack_headers()
spdylay_frame_headers_free(&frame.headers);
spdylay_zlib_inflate_free(&inflater);
spdylay_zlib_deflate_free(&deflater);
spdylay_buffer_free(&inflatebuf);
}
void test_spdylay_frame_pack_settings()

View File

@ -64,7 +64,8 @@ void test_spdylay_zlib()
free(deflatebuf);
spdylay_buffer_serialize(&buf, inflatebuf);
spdylay_zlib_deflate_free(&deflater);
spdylay_zlib_inflate_free(&inflater);
spdylay_buffer_free(&buf);
}