git-svn-id: https://protobuf-c.googlecode.com/svn/trunk@118 00440858-1255-0410-a3e6-75ea37f81c3a
This commit is contained in:
lahiker42 2009-01-25 13:51:30 +00:00
parent e135ebd217
commit f4e009d4bf
5 changed files with 556 additions and 296 deletions

View File

@ -0,0 +1,388 @@
/* We define three data structures:
* 1. a Stack. a singly-ended, singly-linked list.
* 2. a Queue. a doubly-ended, singly-linked list.
* 3. a List. a doubly-ended, doubly-linked list.
*
* Stack operations:
* PUSH(stack, node) [O(1)]
* POP(stack, rv_node) [O(1)]
* INSERT_AFTER(stack, above_node, new_node) [O(1)]
* IS_EMPTY(stack) [O(1)]
* REVERSE(stack) [O(N)]
* SORT(stack, comparator) [O(NlogN)]
* GET_BOTTOM(stack, rv_node) [O(N)]
* Queue operations:
* ENQUEUE(queue, node) [O(1)]
* DEQUEUE(queue, rv_node) [O(1)]
* PREPEND(queue, node) [O(1)]
* IS_EMPTY(queue) [O(1)]
* REVERSE(queue) [O(N)]
* SORT(queue, comparator) [O(NlogN)]
* List operations:
* PREPEND(list, node) [O(1)]
* APPEND(list, node) [O(1)]
* REMOVE_FIRST(list) [O(1)]
* REMOVE_LAST(list) [O(1)]
* REMOVE(list, node) [O(1)]
* INSERT_AFTER(list, position_node, new_node) [O(1)]
* INSERT_BEFORE(list, position_node, new_node) [O(1)]
* IS_EMPTY(list) [O(1)]
* REVERSE(list) [O(N)]
* SORT(list) [O(NlogN)]
*
* note: the SORT operation is stable, i.e. if two
* elements are equal according to the comparator,
* then their relative order in the list
* will be preserved.
*
* In the above, 'stack', 'queue', and 'list' are
* a comma-separated list of arguments, actually.
* In particular:
* stack => NodeType*, top, next_member_name
* queue => NodeType*, head, tail, next_member_name
* list => NodeType*, first, last, prev_member_name, next_member_name
* We recommend making macros that end in GET_STACK_ARGS, GET_QUEUE_ARGS,
* and GET_LIST_ARGS that return the relevant N-tuples.
*/
#define GSK_LOG2_MAX_LIST_SIZE (GLIB_SIZEOF_SIZE_T*8)
/* --- Stacks --- */
#define GSK_STACK_PUSH(stack, node) GSK_STACK_PUSH_(stack, node)
#define GSK_STACK_POP(stack, rv_node) GSK_STACK_POP_(stack, rv_node)
#define GSK_STACK_INSERT_AFTER(stack, above_node, new_node) \
GSK_STACK_INSERT_AFTER_(stack, above_node, new_node)
#define GSK_STACK_IS_EMPTY(stack) GSK_STACK_IS_EMPTY_(stack)
#define GSK_STACK_REVERSE(stack) GSK_STACK_REVERSE_(stack)
#define GSK_STACK_FOREACH(stack, iter_var, code) GSK_STACK_FOREACH_(stack, iter_var, code)
#define GSK_STACK_SORT(stack, comparator) GSK_STACK_SORT_(stack, comparator)
#define GSK_STACK_GET_BOTTOM(stack, rv_node) GSK_STACK_GET_BOTTOM_(stack, rv_node)
#define GSK_STACK_PUSH_(type, top, next, node) \
G_STMT_START{ \
type _gsk_tmp = (node); \
_gsk_tmp->next = (top); \
(top) = _gsk_tmp; \
}G_STMT_END
#define GSK_STACK_POP_(type, top, next, rv_node) \
G_STMT_START{ \
rv_node = (top); \
(top) = (top)->next; \
}G_STMT_END
#define GSK_STACK_INSERT_AFTER_(type, top, next, above_node, new_node) \
G_STMT_START{ \
type _gsk_tmp = (new_node); \
type _gsk_above = (above_node); \
_gsk_tmp->next = _gsk_above->next; \
_gsk_above->next = _gsk_tmp; \
}G_STMT_END
#define GSK_STACK_IS_EMPTY_(type, top, next) \
((top) == NULL)
#define GSK_STACK_REVERSE_(type, top, next) \
G_STMT_START{ \
type _gsk___prev = NULL; \
type _gsk___at = (top); \
while (_gsk___at != NULL) \
{ \
type _gsk__next = _gsk___at->next; \
_gsk___at->next = _gsk___prev; \
_gsk___prev = _gsk___at; \
_gsk___at = _gsk__next; \
} \
(top) = _gsk___prev; \
}G_STMT_END
#define GSK_STACK_FOREACH_(type, top, next, iter_var, code) \
for (iter_var = top; iter_var != NULL; ) \
{ \
type _gsk__next = iter_var->next; \
code; \
iter_var = _gsk__next; \
}
/* sort algorithm:
* in order to implement SORT in a macro, it cannot use recursion.
* but that's ok because you can just manually implement a stack,
* which is probably faster anyways.
*/
#define GSK_STACK_SORT_(type, top, next, comparator) \
G_STMT_START{ \
type _gsk_stack[GSK_LOG2_MAX_LIST_SIZE]; \
guint _gsk_stack_size = 0; \
guint _gsk_i; \
type _gsk_at; \
for (_gsk_at = top; _gsk_at != NULL; ) \
{ \
type _gsk_a = _gsk_at; \
type _gsk_b; \
type _gsk_cur_list; \
int _gsk_comparator_rv; \
_gsk_at = _gsk_at->next; \
if (_gsk_at) \
{ \
_gsk_b = _gsk_at; \
_gsk_at = _gsk_at->next; \
comparator (_gsk_a, _gsk_b, _gsk_comparator_rv); \
if (_gsk_comparator_rv > 0) \
{ \
/* sort first two elements */ \
type _gsk_swap = _gsk_b; \
_gsk_b = _gsk_a; \
_gsk_a = _gsk_swap; \
_gsk_a->next = _gsk_b; \
_gsk_b->next = NULL; \
} \
else \
{ \
/* first two elements already sorted */ \
_gsk_b->next = NULL; \
} \
} \
else \
{ \
/* only one element remains */ \
_gsk_a->next = NULL; \
_gsk_at = NULL; \
} \
_gsk_cur_list = _gsk_a; \
\
/* merge _gsk_cur_list up the stack */ \
for (_gsk_i = 0; TRUE; _gsk_i++) \
{ \
/* expanding the stack is marked unlikely, */ \
/* since in the case it matters (where the number */ \
/* of elements is big), the stack rarely grows. */ \
if (G_UNLIKELY (_gsk_i == _gsk_stack_size)) \
{ \
_gsk_stack[_gsk_stack_size++] = _gsk_cur_list; \
break; \
} \
else if (_gsk_stack[_gsk_i] == NULL) \
{ \
_gsk_stack[_gsk_i] = _gsk_cur_list; \
break; \
} \
else \
{ \
/* Merge _gsk_stack[_gsk_i] and _gsk_cur_list. */ \
type _gsk_merge_list = _gsk_stack[_gsk_i]; \
type _gsk_new_cur_list; \
_gsk_stack[_gsk_i] = NULL; \
\
_GSK_MERGE_NONEMPTY_LISTS (_gsk_merge_list, \
_gsk_cur_list, \
_gsk_new_cur_list, \
type, next, comparator); \
_gsk_cur_list = _gsk_new_cur_list; \
_gsk_stack[_gsk_i] = NULL; \
} \
} \
} \
\
/* combine all the elements on the stack into a final output */ \
top = NULL; \
for (_gsk_i = 0; _gsk_i < _gsk_stack_size; _gsk_i++) \
if (_gsk_stack[_gsk_i] != NULL) \
{ \
if (top == NULL) \
top = _gsk_stack[_gsk_i]; \
else \
{ \
type _gsk_new_top; \
_GSK_MERGE_NONEMPTY_LISTS (_gsk_stack[_gsk_i], \
top, \
_gsk_new_top, \
type, next, comparator); \
top = _gsk_new_top; \
} \
} \
}G_STMT_END
#define GSK_STACK_GET_BOTTOM_(type, top, next, rv_node) \
G_STMT_START{ \
rv_node = top; \
if (rv_node != NULL) \
while (rv_node->next) \
rv_node = rv_node->next; \
}G_STMT_END
/* --- Queues --- */
#define GSK_QUEUE_ENQUEUE(queue, node) GSK_QUEUE_ENQUEUE_(queue, node)
#define GSK_QUEUE_DEQUEUE(queue, rv_node) GSK_QUEUE_DEQUEUE_(queue, rv_node)
#define GSK_QUEUE_PREPEND(queue, node) GSK_QUEUE_PREPEND_(queue, node)
#define GSK_QUEUE_IS_EMPTY(queue) GSK_QUEUE_IS_EMPTY_(queue)
#define GSK_QUEUE_REVERSE(queue) GSK_QUEUE_REVERSE_(queue)
#define GSK_QUEUE_SORT(queue, comparator) GSK_QUEUE_SORT_(queue, comparator)
#define GSK_QUEUE_ENQUEUE_(type, head, tail, next, node) \
G_STMT_START{ \
type _gsk_tmp = (node); \
if (tail) \
tail->next = _gsk_tmp; \
else \
head = _gsk_tmp; \
tail = _gsk_tmp; \
node->next = NULL; \
}G_STMT_END
#define GSK_QUEUE_DEQUEUE_(type, head, tail, next, rv_node) \
G_STMT_START{ \
rv_node = head; \
if (head) \
{ \
head = head->next; \
if (head == NULL) \
tail = NULL; \
} \
}G_STMT_END
#define GSK_QUEUE_PREPEND_(type, head, tail, next, node) \
G_STMT_START{ \
type _gsk_tmp = (node); \
_gsk_tmp->next = head; \
head = _gsk_tmp; \
if (tail == NULL) \
tail = head; \
}G_STMT_END
#define GSK_QUEUE_IS_EMPTY_(type, head, tail, next) \
((head) == NULL)
#define GSK_QUEUE_REVERSE_(type, head, tail, next) \
G_STMT_START{ \
type _gsk_queue_new_tail = head; \
GSK_STACK_REVERSE_(type, head, next); \
tail = _gsk_queue_new_tail; \
}G_STMT_END
#define GSK_QUEUE_SORT_(type, head, tail, next, comparator) \
G_STMT_START{ \
GSK_STACK_SORT_(type, head, next, comparator); \
GSK_STACK_GET_BOTTOM_(type, head, next, tail); \
}G_STMT_END
/* --- List --- */
#define GSK_LIST_PREPEND(list, node) GSK_LIST_PREPEND_(list, node)
#define GSK_LIST_APPEND(list, node) GSK_LIST_APPEND_(list, node)
#define GSK_LIST_REMOVE_FIRST(list) GSK_LIST_REMOVE_FIRST_(list)
#define GSK_LIST_REMOVE_LAST(list) GSK_LIST_REMOVE_LAST_(list)
#define GSK_LIST_REMOVE(list, node) GSK_LIST_REMOVE_(list, node)
#define GSK_LIST_INSERT_AFTER(list, at, node) GSK_LIST_INSERT_AFTER_(list, at, node)
#define GSK_LIST_INSERT_BEFORE(list, at, node) GSK_LIST_INSERT_BEFORE_(list, at, node)
#define GSK_LIST_IS_EMPTY(list) GSK_LIST_IS_EMPTY_(list)
#define GSK_LIST_REVERSE(list) GSK_LIST_REVERSE_(list)
#define GSK_LIST_SORT(list, comparator) GSK_LIST_SORT_(list, comparator)
#define GSK_LIST_PREPEND_(type, first, last, prev, next, node) \
G_STMT_START{ \
type _gsk_tmp = (node); \
if (first) \
first->prev = (_gsk_tmp); \
else \
last = (_gsk_tmp); \
node->next = first; \
node->prev = NULL; \
first = node; \
}G_STMT_END
#define GSK_LIST_APPEND_(type, first, last, prev, next, node) \
GSK_LIST_PREPEND_(type, last, first, next, prev, node)
#define GSK_LIST_REMOVE_FIRST_(type, first, last, prev, next) \
G_STMT_START{ \
first = first->next; \
if (first == NULL) \
last = NULL; \
else \
first->prev = NULL; \
}G_STMT_END
#define GSK_LIST_REMOVE_LAST_(type, first, last, prev, next) \
GSK_LIST_REMOVE_FIRST_(type, last, first, next, prev)
#define GSK_LIST_REMOVE_(type, first, last, prev, next, node) \
G_STMT_START{ \
type _gsk_tmp = (node); \
if (_gsk_tmp->prev) \
_gsk_tmp->prev->next = _gsk_tmp->next; \
else \
first = _gsk_tmp->next; \
if (_gsk_tmp->next) \
_gsk_tmp->next->prev = _gsk_tmp->prev; \
else \
last = _gsk_tmp->prev; \
}G_STMT_END
#define GSK_LIST_INSERT_AFTER_(type, first, last, prev, next, at, node) \
G_STMT_START{ \
type _gsk_at = (at); \
type _gsk_node = (node); \
_gsk_node->prev = _gsk_at; \
_gsk_node->next = _gsk_at->next; \
if (_gsk_node->next) \
_gsk_node->next->prev = _gsk_node; \
else \
last = _gsk_node; \
_gsk_at->next = _gsk_node; \
}G_STMT_END
#define GSK_LIST_INSERT_BEFORE_(type, first, last, prev, next, at, node)\
GSK_LIST_INSERT_AFTER_(type, last, first, next, prev, at, node)
#define GSK_LIST_IS_EMPTY_(type, first, last, prev, next) \
((first) == NULL)
#define GSK_LIST_REVERSE_(type, first, last, prev, next) \
G_STMT_START{ \
type _gsk_at = first; \
first = last; \
last = _gsk_at; \
while (_gsk_at) \
{ \
type _gsk_old_next = _gsk_at->next; \
_gsk_at->next = _gsk_at->prev; \
_gsk_at->prev = _gsk_old_next; \
_gsk_at = _gsk_old_next; \
} \
}G_STMT_END
#define GSK_LIST_SORT_(type, first, last, prev, next, comparator) \
G_STMT_START{ \
type _gsk_prev = NULL; \
type _gsk_at; \
GSK_STACK_SORT_(type, first, next, comparator); \
for (_gsk_at = first; _gsk_at; _gsk_at = _gsk_at->next) \
{ \
_gsk_at->prev = _gsk_prev; \
_gsk_prev = _gsk_at; \
} \
last = _gsk_prev; \
}G_STMT_END
/* --- Internals --- */
#define _GSK_MERGE_NONEMPTY_LISTS(a,b,out,type,next,comparator) \
G_STMT_START{ \
type _gsk_out_at; \
int _gsk_comparator_rv; \
/* merge 'a' and 'b' into 'out' -- in order to make the sort stable,*/ \
/* always put elements if 'a' first in the event of a tie (i.e. */ \
/* when comparator_rv==0) */ \
comparator (a, b, _gsk_comparator_rv); \
if (_gsk_comparator_rv <= 0) \
{ \
out = a; \
a = a->next; \
} \
else \
{ \
out = b; \
b = b->next; \
} \
_gsk_out_at = out; \
while (a && b) \
{ \
comparator (a, b, _gsk_comparator_rv); \
if (_gsk_comparator_rv <= 0) \
{ \
_gsk_out_at->next = a; \
_gsk_out_at = a; \
a = a->next; \
} \
else \
{ \
_gsk_out_at->next = b; \
_gsk_out_at = b; \
b = b->next; \
} \
} \
_gsk_out_at->next = (a != NULL) ? a : b; \
}G_STMT_END

View File

@ -119,17 +119,19 @@ protobuf_c_data_buffer_cleanup_recycling_bin ()
/* --- Public methods --- */ /* --- Public methods --- */
/** /**
* protobuf_c_data_buffer_construct: * protobuf_c_data_buffer_init:
* @buffer: buffer to initialize (as empty). * @buffer: buffer to initialize (as empty).
* *
* Construct an empty buffer out of raw memory. * Construct an empty buffer out of raw memory.
* (This is equivalent to filling the buffer with 0s) * (This is equivalent to filling the buffer with 0s)
*/ */
void void
protobuf_c_data_buffer_construct(ProtobufCDataBuffer *buffer) protobuf_c_data_buffer_init(ProtobufCDataBuffer *buffer,
ProtobufCAllocator *allocator)
{ {
buffer->first_frag = buffer->last_frag = NULL; buffer->first_frag = buffer->last_frag = NULL;
buffer->size = 0; buffer->size = 0;
buffer->allocator = allocator;
} }
#if defined(GSK_DEBUG) || GSK_DEBUG_BUFFER_ALLOCATIONS #if defined(GSK_DEBUG) || GSK_DEBUG_BUFFER_ALLOCATIONS
@ -708,7 +710,7 @@ protobuf_c_data_buffer_clear(ProtobufCDataBuffer *to_destroy)
* returns: its index in the buffer, or -1 if the character * returns: its index in the buffer, or -1 if the character
* is not in the buffer. * is not in the buffer.
*/ */
ssize_t int
protobuf_c_data_buffer_index_of(ProtobufCDataBuffer *buffer, protobuf_c_data_buffer_index_of(ProtobufCDataBuffer *buffer,
char char_to_find) char char_to_find)
{ {
@ -1103,242 +1105,3 @@ protobuf_c_data_buffer_polystr_index_of (ProtobufCDataBuffer *buffer,
} }
#endif #endif
/* --- ProtobufCDataBufferIterator --- */
/**
* protobuf_c_data_buffer_iterator_construct:
* @iterator: to initialize.
* @to_iterate: the buffer to walk through.
*
* Initialize a new #ProtobufCDataBufferIterator.
*/
void
protobuf_c_data_buffer_iterator_construct (ProtobufCDataBufferIterator *iterator,
ProtobufCDataBuffer *to_iterate)
{
iterator->fragment = to_iterate->first_frag;
if (iterator->fragment != NULL)
{
iterator->in_cur = 0;
iterator->cur_data = (uint8_t*)protobuf_c_data_buffer_fragment_start (iterator->fragment);
iterator->cur_length = iterator->fragment->buf_length;
}
else
{
iterator->in_cur = 0;
iterator->cur_data = NULL;
iterator->cur_length = 0;
}
iterator->offset = 0;
}
/**
* protobuf_c_data_buffer_iterator_peek:
* @iterator: to peek data from.
* @out: to copy data into.
* @max_length: maximum number of bytes to write to @out.
*
* Peek data from the current position of an iterator.
* The iterator's position is not changed.
*
* returns: number of bytes peeked into @out.
*/
size_t
protobuf_c_data_buffer_iterator_peek (ProtobufCDataBufferIterator *iterator,
void *out,
size_t max_length)
{
ProtobufCDataBufferFragment *fragment = iterator->fragment;
size_t frag_length = iterator->cur_length;
const uint8_t *frag_data = iterator->cur_data;
size_t in_frag = iterator->in_cur;
size_t out_remaining = max_length;
uint8_t *out_at = out;
while (fragment != NULL)
{
size_t frag_remaining = frag_length - in_frag;
if (out_remaining <= frag_remaining)
{
memcpy (out_at, frag_data + in_frag, out_remaining);
out_remaining = 0;
break;
}
memcpy (out_at, frag_data + in_frag, frag_remaining);
out_remaining -= frag_remaining;
out_at += frag_remaining;
fragment = fragment->next;
if (fragment != NULL)
{
frag_data = (uint8_t *) protobuf_c_data_buffer_fragment_start (fragment);
frag_length = fragment->buf_length;
}
in_frag = 0;
}
return max_length - out_remaining;
}
/**
* protobuf_c_data_buffer_iterator_read:
* @iterator: to read data from.
* @out: to copy data into.
* @max_length: maximum number of bytes to write to @out.
*
* Peek data from the current position of an iterator.
* The iterator's position is updated to be at the end of
* the data read.
*
* returns: number of bytes read into @out.
*/
size_t
protobuf_c_data_buffer_iterator_read (ProtobufCDataBufferIterator *iterator,
void *out,
size_t max_length)
{
ProtobufCDataBufferFragment *fragment = iterator->fragment;
size_t frag_length = iterator->cur_length;
const uint8_t *frag_data = iterator->cur_data;
size_t in_frag = iterator->in_cur;
size_t out_remaining = max_length;
uint8_t *out_at = out;
while (fragment != NULL)
{
size_t frag_remaining = frag_length - in_frag;
if (out_remaining <= frag_remaining)
{
memcpy (out_at, frag_data + in_frag, out_remaining);
in_frag += out_remaining;
out_remaining = 0;
break;
}
memcpy (out_at, frag_data + in_frag, frag_remaining);
out_remaining -= frag_remaining;
out_at += frag_remaining;
fragment = fragment->next;
if (fragment != NULL)
{
frag_data = (uint8_t *) protobuf_c_data_buffer_fragment_start (fragment);
frag_length = fragment->buf_length;
}
in_frag = 0;
}
iterator->in_cur = in_frag;
iterator->fragment = fragment;
iterator->cur_length = frag_length;
iterator->cur_data = frag_data;
iterator->offset += max_length - out_remaining;
return max_length - out_remaining;
}
/**
* protobuf_c_data_buffer_iterator_find_char:
* @iterator: to advance.
* @c: the character to look for.
*
* If it exists,
* skip forward to the next instance of @c and return TRUE.
* Otherwise, do nothing and return FALSE.
*
* returns: whether the character was found.
*/
protobuf_c_boolean
protobuf_c_data_buffer_iterator_find_char (ProtobufCDataBufferIterator *iterator,
char c)
{
ProtobufCDataBufferFragment *fragment = iterator->fragment;
size_t frag_length = iterator->cur_length;
const uint8_t *frag_data = iterator->cur_data;
size_t in_frag = iterator->in_cur;
size_t new_offset = iterator->offset;
if (fragment == NULL)
return -1;
for (;;)
{
size_t frag_remaining = frag_length - in_frag;
const uint8_t * ptr = memchr (frag_data + in_frag, c, frag_remaining);
if (ptr != NULL)
{
iterator->offset = (ptr - frag_data) - in_frag + new_offset;
iterator->fragment = fragment;
iterator->in_cur = ptr - frag_data;
iterator->cur_length = frag_length;
iterator->cur_data = frag_data;
return TRUE;
}
fragment = fragment->next;
if (fragment == NULL)
return FALSE;
new_offset += frag_length - in_frag;
in_frag = 0;
frag_length = fragment->buf_length;
frag_data = protobuf_c_data_buffer_fragment_start (fragment);
}
}
/**
* protobuf_c_data_buffer_iterator_skip:
* @iterator: to advance.
* @max_length: maximum number of bytes to skip forward.
*
* Advance an iterator forward in the buffer,
* returning the number of bytes skipped.
*
* returns: number of bytes skipped forward.
*/
size_t
protobuf_c_data_buffer_iterator_skip (ProtobufCDataBufferIterator *iterator,
size_t max_length)
{
ProtobufCDataBufferFragment *fragment = iterator->fragment;
size_t frag_length = iterator->cur_length;
const uint8_t *frag_data = iterator->cur_data;
size_t in_frag = iterator->in_cur;
size_t out_remaining = max_length;
while (fragment != NULL)
{
size_t frag_remaining = frag_length - in_frag;
if (out_remaining <= frag_remaining)
{
in_frag += out_remaining;
out_remaining = 0;
break;
}
out_remaining -= frag_remaining;
fragment = fragment->next;
if (fragment != NULL)
{
frag_data = (uint8_t *) protobuf_c_data_buffer_fragment_start (fragment);
frag_length = fragment->buf_length;
}
else
{
frag_data = NULL;
frag_length = 0;
}
in_frag = 0;
}
iterator->in_cur = in_frag;
iterator->fragment = fragment;
iterator->cur_length = frag_length;
iterator->cur_data = frag_data;
iterator->offset += max_length - out_remaining;
return max_length - out_remaining;
}

View File

@ -45,6 +45,8 @@ char *protobuf_c_data_buffer_parse_string0 (ProtobufCDataBuffer *buf
int protobuf_c_data_buffer_peek_char (const ProtobufCDataBuffer *buffer); int protobuf_c_data_buffer_peek_char (const ProtobufCDataBuffer *buffer);
int protobuf_c_data_buffer_read_char (ProtobufCDataBuffer *buffer); int protobuf_c_data_buffer_read_char (ProtobufCDataBuffer *buffer);
int protobuf_c_data_buffer_index_of(ProtobufCDataBuffer *buffer,
char char_to_find);
/* /*
* Appending to the buffer. * Appending to the buffer.
*/ */
@ -72,13 +74,6 @@ void protobuf_c_data_buffer_append_string0 (ProtobufCDataBuffer *buf
const char *string); const char *string);
void protobuf_c_data_buffer_printf (ProtobufCDataBuffer *buffer,
const char *format,
...) PROTOBUF_C_GNUC_PRINTF(2,3);
void protobuf_c_data_buffer_vprintf (ProtobufCDataBuffer *buffer,
const char *format,
va_list args);
/* Take all the contents from src and append /* Take all the contents from src and append
* them to dst, leaving src empty. * them to dst, leaving src empty.
*/ */
@ -99,16 +94,6 @@ int protobuf_c_data_buffer_writev_len (ProtobufCDataBuffer *
int protobuf_c_data_buffer_read_in_fd (ProtobufCDataBuffer *write_to, int protobuf_c_data_buffer_read_in_fd (ProtobufCDataBuffer *write_to,
int read_from); int read_from);
/*
* Scanning the buffer.
*/
int protobuf_c_data_buffer_index_of (ProtobufCDataBuffer *buffer,
char char_to_find);
int protobuf_c_data_buffer_str_index_of (ProtobufCDataBuffer *buffer,
const char *str_to_find);
int protobuf_c_data_buffer_polystr_index_of (ProtobufCDataBuffer *buffer,
char **strings);
/* This deallocates memory used by the buffer-- you are responsible /* This deallocates memory used by the buffer-- you are responsible
* for the allocation and deallocation of the ProtobufCDataBuffer itself. */ * for the allocation and deallocation of the ProtobufCDataBuffer itself. */
void protobuf_c_data_buffer_destruct (ProtobufCDataBuffer *to_destroy); void protobuf_c_data_buffer_destruct (ProtobufCDataBuffer *to_destroy);
@ -116,32 +101,4 @@ void protobuf_c_data_buffer_destruct (ProtobufCDataBuffer *to_
/* Free all unused buffer fragments. */ /* Free all unused buffer fragments. */
void protobuf_c_data_buffer_cleanup_recycling_bin (); void protobuf_c_data_buffer_cleanup_recycling_bin ();
/* intended for use on the stack */
typedef struct _ProtobufCDataBufferIterator ProtobufCDataBufferIterator;
struct _ProtobufCDataBufferIterator
{
ProtobufCDataBufferFragment *fragment;
size_t in_cur;
size_t cur_length;
const uint8_t *cur_data;
size_t offset;
};
#define protobuf_c_data_buffer_iterator_offset(iterator) ((iterator)->offset)
void protobuf_c_data_buffer_iterator_construct (ProtobufCDataBufferIterator *iterator,
ProtobufCDataBuffer *to_iterate);
unsigned protobuf_c_data_buffer_iterator_peek (ProtobufCDataBufferIterator *iterator,
void *out,
unsigned max_length);
unsigned protobuf_c_data_buffer_iterator_read (ProtobufCDataBufferIterator *iterator,
void *out,
unsigned max_length);
unsigned protobuf_c_data_buffer_iterator_skip (ProtobufCDataBufferIterator *iterator,
unsigned max_length);
protobuf_c_boolean protobuf_c_data_buffer_iterator_find_char (ProtobufCDataBufferIterator *iterator,
char c);
#endif #endif

View File

@ -9,6 +9,7 @@
#include <errno.h> #include <errno.h>
#include "protobuf-c-dispatch.h" #include "protobuf-c-dispatch.h"
#include "gskrbtreemacros.h" #include "gskrbtreemacros.h"
#include "gsklistmacros.h"
#define protobuf_c_assert(condition) assert(condition) #define protobuf_c_assert(condition) assert(condition)
@ -70,6 +71,16 @@ struct _ProtobufCDispatchTimer
void *func_data; void *func_data;
}; };
struct _ProtobufCDispatchIdle
{
RealDispatch *dispatch;
ProtobufCDispatchIdle *prev, *next;
/* user callback */
ProtobufCDispatchIdleFunc func;
void *func_data;
};
/* Define the tree of timers, as per gskrbtreemacros.h */ /* Define the tree of timers, as per gskrbtreemacros.h */
#define TIMER_GET_IS_RED(n) ((n)->is_red) #define TIMER_GET_IS_RED(n) ((n)->is_red)
#define TIMER_SET_IS_RED(n,v) ((n)->is_red = (v)) #define TIMER_SET_IS_RED(n,v) ((n)->is_red = (v))
@ -87,6 +98,10 @@ struct _ProtobufCDispatchTimer
parent, left, right, \ parent, left, right, \
TIMERS_COMPARE TIMERS_COMPARE
/* declare the idle-handler list */
#define GET_IDLE_LIST(d) \
ProtobufCDispatchIdle *, d->first_idle, d->last_idle, prev, next
/* Create or destroy a Dispatch */ /* Create or destroy a Dispatch */
ProtobufCDispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator) ProtobufCDispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator)
{ {
@ -114,6 +129,13 @@ protobuf_c_dispatch_free(ProtobufCDispatch *dispatch)
FREE (d); FREE (d);
} }
ProtobufCAllocator *
protobuf_c_dispatch_peek_allocator (ProtobufCDispatch *dispatch)
{
RealDispatch *d = (RealDispatch *) dispatch;
return d->allocator;
}
static void static void
enlarge_fd_map (RealDispatch *d, enlarge_fd_map (RealDispatch *d,
unsigned fd) unsigned fd)
@ -550,3 +572,35 @@ void protobuf_c_dispatch_remove_timer (ProtobufCDispatchTimer *timer)
} }
} }
} }
ProtobufCDispatchIdle *
protobuf_c_dispatch_add_idle (ProtobufCDispatch *dispatch,
ProtobufCDispatchIdleFunc func,
void *func_data)
{
RealDispatch *d = (RealDispatch *) dispatch;
ProtobufCDispatchIdle *rv;
if (d->recycled_idles != NULL)
{
rv = d->recycled_idles;
d->recycled_idles = rv->next;
}
else
{
ProtobufCAllocator *allocator = d->allocator;
rv = ALLOC (sizeof (ProtobufCDispatchIdle));
}
GSK_LIST_APPEND (GET_IDLE_LIST (d), rv);
rv->func = func;
rv->func_data = func_data;
rv->dispatch = d;
return rv;
}
void
protobuf_c_dispatch_remove_idle (ProtobufCDispatchIdle *idle)
{
RealDispatch *d = idle->dispatch;
GSK_LIST_REMOVE (GET_IDLE_LIST (d), idle);
idle->next = d->recycled_idles;
d->recycled_idles = idle;
}

View File

@ -34,7 +34,8 @@ typedef enum
PROTOBUF_C_CLIENT_STATE_CONNECTING, PROTOBUF_C_CLIENT_STATE_CONNECTING,
PROTOBUF_C_CLIENT_STATE_CONNECTED, PROTOBUF_C_CLIENT_STATE_CONNECTED,
PROTOBUF_C_CLIENT_STATE_FAILED_WAITING, PROTOBUF_C_CLIENT_STATE_FAILED_WAITING,
PROTOBUF_C_CLIENT_STATE_FAILED /* if no autoretry */ PROTOBUF_C_CLIENT_STATE_FAILED, /* if no autoretry */
PROTOBUF_C_CLIENT_STATE_DESTROYED
} ProtobufC_RPC_ClientState; } ProtobufC_RPC_ClientState;
typedef struct _Closure Closure; typedef struct _Closure Closure;
@ -68,18 +69,19 @@ struct _ProtobufC_RPC_Client
} init; } init;
struct { struct {
protobuf_c_boolean pending; protobuf_c_boolean pending;
protobuf_c_boolean destroyed_while_pending;
uint16_t port; uint16_t port;
} name_lookup; } name_lookup;
struct {
ProtobufCDispatchTimer *timer;
char *error_message;
} failed_waiting;
struct { struct {
unsigned closures_alloced; unsigned closures_alloced;
unsigned first_free_request_id; unsigned first_free_request_id;
/* indexed by (request_id-1) */ /* indexed by (request_id-1) */
Closure *closures; Closure *closures;
} connected; } connected;
struct {
ProtobufCDispatchTimer *timer;
char *error_message;
} failed_waiting;
struct { struct {
char *error_message; char *error_message;
} failed; } failed;
@ -87,6 +89,7 @@ struct _ProtobufC_RPC_Client
}; };
static void begin_name_lookup (ProtobufC_RPC_Client *client); static void begin_name_lookup (ProtobufC_RPC_Client *client);
static void destroy_client_rpc (ProtobufCService *service);
static void static void
@ -132,6 +135,7 @@ client_failed (ProtobufC_RPC_Client *client,
case PROTOBUF_C_CLIENT_STATE_INIT: case PROTOBUF_C_CLIENT_STATE_INIT:
case PROTOBUF_C_CLIENT_STATE_FAILED_WAITING: case PROTOBUF_C_CLIENT_STATE_FAILED_WAITING:
case PROTOBUF_C_CLIENT_STATE_FAILED: case PROTOBUF_C_CLIENT_STATE_FAILED:
case PROTOBUF_C_CLIENT_STATE_DESTROYED:
protobuf_c_assert (FALSE); protobuf_c_assert (FALSE);
break; break;
} }
@ -292,6 +296,11 @@ handle_name_lookup_success (const uint8_t *address,
protobuf_c_assert (client->state == PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP); protobuf_c_assert (client->state == PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP);
protobuf_c_assert (client->info.name_lookup.pending); protobuf_c_assert (client->info.name_lookup.pending);
client->info.name_lookup.pending = 0; client->info.name_lookup.pending = 0;
if (client->info.name_lookup.destroyed_while_pending)
{
destroy_client_rpc (&client->base_service);
return;
}
addr.sin_family = PF_INET; addr.sin_family = PF_INET;
memcpy (&addr.sin_addr, address, 4); memcpy (&addr.sin_addr, address, 4);
addr.sin_port = htons (client->info.name_lookup.port); addr.sin_port = htons (client->info.name_lookup.port);
@ -306,6 +315,11 @@ handle_name_lookup_failure (const char *error_message,
protobuf_c_assert (client->state == PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP); protobuf_c_assert (client->state == PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP);
protobuf_c_assert (client->info.name_lookup.pending); protobuf_c_assert (client->info.name_lookup.pending);
client->info.name_lookup.pending = 0; client->info.name_lookup.pending = 0;
if (client->info.name_lookup.destroyed_while_pending)
{
destroy_client_rpc (&client->base_service);
return;
}
client_failed (client, "name lookup failed (for name from %s): %s", client->name, error_message); client_failed (client, "name lookup failed (for name from %s): %s", client->name, error_message);
} }
@ -348,6 +362,7 @@ begin_name_lookup (ProtobufC_RPC_Client *client)
port = atoi (colon + 1); port = atoi (colon + 1);
client->info.name_lookup.pending = 1; client->info.name_lookup.pending = 1;
client->info.name_lookup.destroyed_while_pending = 0;
client->info.name_lookup.port = port; client->info.name_lookup.port = port;
client->resolver (client->dispatch, client->resolver (client->dispatch,
host, host,
@ -521,16 +536,50 @@ handle_client_fd_events (int fd,
{ {
uint32_t header[3]; uint32_t header[3];
unsigned service_index, message_length, request_id; unsigned service_index, message_length, request_id;
Closure *closure;
uint8_t *packed_data;
ProtobufCMessage *msg;
protobuf_c_data_buffer_peek (&client->incoming, header, sizeof (header)); protobuf_c_data_buffer_peek (&client->incoming, header, sizeof (header));
service_index = uint32_from_le (header[0]); service_index = uint32_from_le (header[0]);
message_length = uint32_from_le (header[1]); message_length = uint32_from_le (header[1]);
request_id = header[2]; /* already native-endian */ request_id = header[2]; /* already native-endian */
if (12 + message_length > client.incoming.size) if (12 + message_length > client->incoming.size)
break; break;
/* lookup request by id */ /* lookup request by id */
... if (request_id >= client->info.connected.closures_alloced
|| request_id == 0
|| client->info.connected.closures[request_id-1].response_type == NULL)
{
client_failed (client, "bad request-id in response from server");
return;
}
closure = client->info.connected.closures + (request_id - 1);
/* read message and unpack */
protobuf_c_data_buffer_discard (&client->incoming, 12);
packed_data = client->allocator->alloc (client->allocator, message_length);
protobuf_c_data_buffer_read (&client->incoming, packed_data, message_length);
/* TODO: use fast temporary allocator */
msg = protobuf_c_message_unpack (closure->response_type,
client->allocator,
message_length,
packed_data);
if (msg == NULL)
{
client_failed (client, "failed to unpack message");
client->allocator->free (client->allocator, packed_data);
return;
}
/* invoke closure */
closure->closure (msg, closure->closure_data);
/* clean up */
protobuf_c_message_free_unpacked (msg, client->allocator);
client->allocator->free (client->allocator, packed_data);
} }
} }
} }
@ -577,7 +626,8 @@ invoke_client_rpc (ProtobufCService *service,
break; break;
case PROTOBUF_C_CLIENT_STATE_FAILED_WAITING: case PROTOBUF_C_CLIENT_STATE_FAILED_WAITING:
case PROTOBUF_C_CLIENT_STATE_FAILED: /* if no autoretry */ case PROTOBUF_C_CLIENT_STATE_FAILED:
case PROTOBUF_C_CLIENT_STATE_DESTROYED:
closure (NULL, closure_data); closure (NULL, closure_data);
break; break;
} }
@ -587,7 +637,55 @@ static void
destroy_client_rpc (ProtobufCService *service) destroy_client_rpc (ProtobufCService *service)
{ {
ProtobufC_RPC_Client *client = (ProtobufC_RPC_Client *) service; ProtobufC_RPC_Client *client = (ProtobufC_RPC_Client *) service;
... ProtobufC_RPC_ClientState state = client->state;
unsigned i;
unsigned n_closures = 0;
Closure *closures = NULL;
switch (state)
{
case PROTOBUF_C_CLIENT_STATE_INIT:
protobuf_c_dispatch_remove_idle (client->info.init.idle);
break;
case PROTOBUF_C_CLIENT_STATE_NAME_LOOKUP:
if (client->info.name_lookup.pending)
{
client->info.name_lookup.destroyed_while_pending = 1;
return;
}
break;
case PROTOBUF_C_CLIENT_STATE_CONNECTING:
break;
case PROTOBUF_C_CLIENT_STATE_CONNECTED:
n_closures = client->info.connected.closures_alloced;
closures = client->info.connected.closures;
break;
case PROTOBUF_C_CLIENT_STATE_FAILED_WAITING:
protobuf_c_dispatch_remove_timer (client->info.failed_waiting.timer);
client->allocator->free (client->allocator, client->info.failed_waiting.error_message);
break;
case PROTOBUF_C_CLIENT_STATE_FAILED:
client->allocator->free (client->allocator, client->info.failed.error_message);
break;
case PROTOBUF_C_CLIENT_STATE_DESTROYED:
protobuf_c_assert (0);
break;
}
if (client->fd >= 0)
{
protobuf_c_dispatch_close_fd (client->dispatch, client->fd);
client->fd = -1;
}
protobuf_c_data_buffer_clear (&client->incoming);
protobuf_c_data_buffer_clear (&client->outgoing);
client->state = PROTOBUF_C_CLIENT_STATE_DESTROYED;
/* free closures only once we are in the destroyed state */
for (i = 0; i < n_closures; i++)
closures[i].closure (NULL, closures[i].closure_data);
if (closures)
client->allocator->free (client->allocator, closures);
client->allocator->free (client->allocator, client);
} }
ProtobufCService *protobuf_c_rpc_client_new (ProtobufC_RPC_AddressType type, ProtobufCService *protobuf_c_rpc_client_new (ProtobufC_RPC_AddressType type,