static void test_isEqualRefCount(void) { int data2 = 0xbad00bad; RefCount *refCount1 = NULL; RefCount *refCount2 = NULL; // initialize refcount1 RefCountNew(&refCount1); assert_int_equal(0, refCount1->user_count); assert_true(refCount1->last == NULL); assert_true(refCount1->users == NULL); // initialize refcount2 as a copy of refcount1 refCount2 = refCount1; // isEqual should return true assert_true(RefCountIsEqual(refCount1, refCount2)); /* Initialize refcount2 on its own */ RefCountNew(&refCount2); assert_int_equal(0, refCount2->user_count); assert_true(refCount2->last == NULL); assert_true(refCount2->users == NULL); // isEqual should return false assert_false(RefCountIsEqual(refCount1, refCount2)); // Add one to refcount1 RefCountAttach(refCount1, &data2); // isEqual should return false assert_false(RefCountIsEqual(refCount1, refCount2)); // Add the same to refcount2 RefCountAttach(refCount2, &data2); // isEqual should return false assert_false(RefCountIsEqual(refCount1, refCount2)); // Try one NULL assert_false(RefCountIsEqual(refCount1, NULL)); assert_false(RefCountIsEqual(NULL, refCount2)); // Both NULL assert_false(RefCountIsEqual(NULL, NULL)); // Destroy both refcounts RefCountDestroy(&refCount1); RefCountDestroy(&refCount2); }
/* * Helper method to detach lists. */ static void ListDetach(List *list) { int shared = RefCountIsShared(list->ref_count); if (shared) { /* * 1. Perform a deep copy (expensive!) * 2. Detach */ ListNode *p = NULL, *q = NULL, *newList = NULL; for (p = list->list; p; p = p->next) { if (newList) { q->next = (ListNode *)malloc(sizeof(ListNode)); q->next->previous = q; q->next->next = NULL; q = q->next; if (p->payload) { if (list->copy) { list->copy(p->payload, &q->payload); } else { q->payload = p->payload; } } } else { // First element newList = (ListNode *)malloc(sizeof(ListNode)); newList->next = NULL; newList->previous = NULL; if (p->payload) { if (list->copy) { list->copy(p->payload, &newList->payload); } else { newList->payload = p->payload; } } q = newList; } } list->list = newList; // Ok, we have our own copy of the list. Now we detach. RefCountDetach(list->ref_count, list); list->ref_count = NULL; RefCountNew(&list->ref_count); RefCountAttach(list->ref_count, list); } }
static void test_isSharedRefCount(void) { int data1 = 0xdeadbeef; int data2 = 0xbad00bad; RefCount *refCount = NULL; // initialize the refcount RefCountNew(&refCount); assert_int_equal(0, refCount->user_count); assert_true(refCount->last == NULL); assert_true(refCount->users == NULL); // isShared should return false assert_false(RefCountIsShared(refCount)); // attach it to the first data RefCountAttach(refCount, &data1); // Check the result assert_int_equal(1, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous == NULL); assert_true(refCount->last->user == (void *)&data1); // isShared should return false assert_false(RefCountIsShared(refCount)); // Attach the second data RefCountAttach(refCount, &data2); // Check the result assert_int_equal(2, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous != NULL); assert_true(refCount->last->user == (void *)&data2); // isShared should return true assert_true(RefCountIsShared(refCount)); // Detach and try again RefCountDetach(refCount, &data1); // Check the result assert_int_equal(1, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous == NULL); assert_true(refCount->last->user == (void *)&data2); // isShared should return false assert_false(RefCountIsShared(refCount)); // Try isShared with a NULL refCount assert_false(RefCountIsShared(NULL)); // Destroy the refcount RefCountDestroy(&refCount); }
Buffer* BufferNewFrom(const char *data, unsigned int length) { /* * Are we going to go over the limit? */ if (length > general_memory_cap) { return NULL; } Buffer *buffer = (Buffer *)xmalloc(sizeof(Buffer)); buffer->capacity = DEFAULT_BUFFER_SIZE; buffer->buffer = (char *)xmalloc(buffer->capacity); /* * Check if we have enough space, otherwise create a larger buffer */ if (length >= buffer->capacity) { unsigned int required_blocks = (length / DEFAULT_BUFFER_SIZE) + 1; buffer->buffer = (char *)xrealloc(buffer->buffer, required_blocks * DEFAULT_BUFFER_SIZE); buffer->capacity = required_blocks * DEFAULT_BUFFER_SIZE; buffer->used = 0; } buffer->mode = BUFFER_BEHAVIOR_CSTRING; buffer->used = 0; buffer->beginning = 0; buffer->end = 0; buffer->memory_cap = general_memory_cap; RefCountNew(&(buffer->ref_count)); RefCountAttach(buffer->ref_count, buffer); /* * We have a buffer that is large enough, copy the data. */ unsigned int c = 0; unsigned int total = 0; for (c = 0; c < length; ++c) { buffer->buffer[c] = data[c]; if ((data[c] == '\0') && (buffer->mode == BUFFER_BEHAVIOR_CSTRING)) { break; } ++total; } buffer->used = total; if (buffer->mode == BUFFER_BEHAVIOR_CSTRING) { buffer->buffer[buffer->used] = '\0'; } return buffer; }
// Simple initialization test static void test_init_destroy_RefCount(void) { RefCount *refCount = NULL; RefCountNew(&refCount); assert_int_equal(0, refCount->user_count); assert_true(refCount->last == NULL); assert_true(refCount->users == NULL); // Now we destroy the refcount. RefCountDestroy(&refCount); assert_true(refCount == NULL); // Try to destroy a NULL refCount RefCountDestroy(&refCount); }
Buffer *BufferNew(void) { Buffer *buffer = (Buffer *)xmalloc(sizeof(Buffer)); buffer->capacity = DEFAULT_BUFFER_SIZE; buffer->buffer = (char *)xmalloc(buffer->capacity); buffer->buffer[0] = '\0'; buffer->mode = BUFFER_BEHAVIOR_CSTRING; buffer->used = 0; buffer->beginning = 0; buffer->end = 0; buffer->memory_cap = general_memory_cap; RefCountNew(&(buffer->ref_count)); RefCountAttach(buffer->ref_count, buffer); return buffer; }
List *ListNew(int (*compare)(const void *, const void *), void (*copy)(const void *, void **), void (*destroy)(void *)) { List *list = NULL; list = (List *)xmalloc(sizeof(List)); list->list = NULL; list->first = NULL; list->last = NULL; list->node_count = 0; list->iterator = NULL; list->state = 0; list->compare = compare; list->destroy = destroy; list->copy = copy; RefCountNew(&list->ref_count); RefCountAttach(list->ref_count, list); return list; }
int BufferNew(Buffer **buffer) { if (!buffer) { return -1; } *buffer = (Buffer *)xmalloc(sizeof(Buffer)); (*buffer)->capacity = DEFAULT_BUFFER_SIZE; (*buffer)->buffer = (char *)xmalloc((*buffer)->capacity); (*buffer)->mode = BUFFER_BEHAVIOR_CSTRING; (*buffer)->used = 0; (*buffer)->beginning = 0; (*buffer)->end = 0; (*buffer)->memory_cap = general_memory_cap; RefCountNew(&(*buffer)->ref_count); RefCountAttach((*buffer)->ref_count, (*buffer)); return 0; }
void BufferZero(Buffer *buffer) { /* * 1. Detach if shared, allocate a new buffer * 2. Mark used as zero. */ if (!buffer) { return; } if (RefCountIsShared(buffer->ref_count)) { RefCountDetach(buffer->ref_count, buffer); buffer->buffer = (char *)xmalloc(buffer->capacity); RefCountNew(&buffer->ref_count); RefCountAttach(buffer->ref_count, buffer); } buffer->used = 0; }
int ListNew(List **list, int (*compare)(const void *, const void *), void (*copy)(const void *source, void **destination), void (*destroy)(void *)) { if (!list) { return -1; } *list = (List *)malloc(sizeof(List)); if (!(*list)) { return -1; } (*list)->list = NULL; (*list)->first = NULL; (*list)->last = NULL; (*list)->node_count = 0; (*list)->iterator = NULL; (*list)->state = 0; (*list)->compare = compare; (*list)->destroy = destroy; (*list)->copy = copy; RefCountNew(&(*list)->ref_count); RefCountAttach((*list)->ref_count, (*list)); return 0; }
int BufferVPrintf(Buffer *buffer, const char *format, va_list ap) { va_list aq; va_copy(aq, ap); if (!buffer || !format) { return -1; } int printed = 0; /* * We don't know how big of a buffer we will need. It might be that we have enough space * or it might be that we don't have enough space. Unfortunately, we cannot reiterate over * a va_list, so our only solution is to tell the caller to retry the call. We signal this * by returning zero. Before doing that we increase the buffer to a suitable size. * The tricky part is the implicit sharing and the reference counting, if we are not shared then * everything is easy, however if we are shared then we need a different strategy. */ if (RefCountIsShared(buffer->ref_count)) { char *new_buffer = NULL; new_buffer = (char *)xmalloc(buffer->capacity); RefCount *ref_count = buffer->ref_count; buffer->ref_count = NULL; RefCountNew(&buffer->ref_count); RefCountAttach(buffer->ref_count, buffer); RefCountDetach(ref_count, buffer); /* * Ok, now we need to take care of the buffer. */ unsigned int i = 0; unsigned int used = 0; for (i = 0; i < buffer->used; ++i) { new_buffer[i] = buffer->buffer[i]; if ((buffer->buffer[i] == '\0') && (buffer->mode == BUFFER_BEHAVIOR_CSTRING)) { break; } ++used; } buffer->buffer = new_buffer; buffer->used = used; } printed = vsnprintf(buffer->buffer, buffer->capacity, format, aq); if (printed >= buffer->capacity) { /* * Allocate a larger buffer and retry. * We use the copy of the list. */ if (printed > buffer->memory_cap) { /* * We would go over the memory_cap limit. */ return -1; } unsigned int required_blocks = (printed / DEFAULT_BUFFER_SIZE) + 1; buffer->buffer = (char *)xrealloc(buffer->buffer, required_blocks * DEFAULT_BUFFER_SIZE); buffer->capacity = required_blocks * DEFAULT_BUFFER_SIZE; buffer->used = 0; printed = vsnprintf(buffer->buffer, buffer->capacity, format, ap); buffer->used = printed; } else { buffer->used = printed; } return printed; }
int BufferAppend(Buffer *buffer, const char *bytes, unsigned int length) { if (!buffer || !bytes) { return -1; } if (length + buffer->used > buffer->memory_cap) { return -1; } if (RefCountIsShared(buffer->ref_count)) { char *new_buffer = NULL; new_buffer = (char *)xmalloc(buffer->capacity); RefCount *ref_count = buffer->ref_count; buffer->ref_count = NULL; RefCountNew(&buffer->ref_count); RefCountAttach(buffer->ref_count, buffer); RefCountDetach(ref_count, buffer); /* * Ok, now we need to take care of the buffer. */ unsigned int i = 0; unsigned int used = 0; for (i = 0; i < buffer->used; ++i) { new_buffer[i] = buffer->buffer[i]; if ((buffer->buffer[i] == '\0') && (buffer->mode == BUFFER_BEHAVIOR_CSTRING)) { break; } ++used; } buffer->buffer = new_buffer; buffer->used = used; } /* * Check if we have enough space, otherwise create a larger buffer */ if (buffer->used + length >= buffer->capacity) { unsigned int required_blocks = ((buffer->used + length)/ DEFAULT_BUFFER_SIZE) + 1; buffer->buffer = (char *)xrealloc(buffer->buffer, required_blocks * DEFAULT_BUFFER_SIZE); buffer->capacity = required_blocks * DEFAULT_BUFFER_SIZE; } /* * We have a buffer that is large enough, copy the data. */ unsigned int c = 0; unsigned int total = 0; for (c = 0; c < length; ++c) { buffer->buffer[c + buffer->used] = bytes[c]; if ((bytes[c] == '\0') && (buffer->mode = BUFFER_BEHAVIOR_CSTRING)) { break; } ++total; } buffer->used += total; if (buffer->mode == BUFFER_BEHAVIOR_CSTRING) { buffer->buffer[buffer->used] = '\0'; } return buffer->used; }
static void test_attach_detach_RefCount(void) { /* * This test does not check for NULL pointers, otherwise asserts will * be triggered. Neither does it check for non-existent owners. */ int data1 = 0xdeadbeef; int data2 = 0xbad00bad; int data3 = 0x55aaaa55; RefCount *refCount = NULL; // initialize the refcount RefCountNew(&refCount); assert_int_equal(0, refCount->user_count); assert_true(refCount->last == NULL); assert_true(refCount->users == NULL); // attach it to the first data RefCountAttach(refCount, &data1); // Check the result assert_int_equal(1, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous == NULL); assert_true(refCount->last->user == (void *)&data1); // Attach the second data RefCountAttach(refCount, &data2); // Check the result assert_int_equal(2, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous != NULL); assert_true(refCount->last->user == (void *)&data2); // Detach the first data RefCountDetach(refCount, &data1); // Check the result assert_int_equal(1, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous == NULL); assert_true(refCount->last->user == (void *)&data2); // Attach the third data RefCountAttach(refCount, &data3); // Check the result assert_int_equal(2, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous != NULL); assert_true(refCount->last->user == (void *)&data3); // Attach the first data RefCountAttach(refCount, &data1); // Check the result assert_int_equal(3, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous != NULL); assert_true(refCount->last->user == (void *)&data1); // Detach the third data RefCountDetach(refCount, &data3); // Check the result assert_int_equal(2, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous != NULL); assert_true(refCount->last->user == (void *)&data1); // Detach the first data RefCountDetach(refCount, &data1); // Check the result assert_int_equal(1, refCount->user_count); assert_true(refCount->last->next == NULL); assert_true(refCount->last->previous == NULL); assert_true(refCount->last->user == (void *)&data2); /* * We cannot detach the last element because that will assert. * Whenever there is only one element the only thing is to destroy * the refcount. */ // Destroy the refcount RefCountDestroy(&refCount); }
int BufferPrintf(Buffer *buffer, const char *format, ...) { /* * We declare two lists, in case we need to reiterate over the list because the buffer was * too small. */ va_list ap; va_list aq; va_start(ap, format); va_copy(aq, ap); if (!buffer || !format) { va_end(aq); va_end(ap); return -1; } int printed = 0; /* * We don't know how big of a buffer we will need. It might be that we have enough space * or it might be that we don't have enough space. Unfortunately, we cannot reiterate over * a va_list, so our only solution is to tell the caller to retry the call. We signal this * by returning zero. Before doing that we increase the buffer to a suitable size. * The tricky part is the implicit sharing and the reference counting, if we are not shared then * everything is easy, however if we are shared then we need a different strategy. */ if (RefCountIsShared(buffer->ref_count)) { char *new_buffer = NULL; new_buffer = (char *)xmalloc(buffer->capacity); /* * Make a local copy of the variables that are required to restore to normality. */ RefCount *ref_count = buffer->ref_count; /* * We try to attach first, since it is more likely that Attach might fail than * detach. */ int result = 0; buffer->ref_count = NULL; RefCountNew(&buffer->ref_count); result = RefCountAttach(buffer->ref_count, buffer); if (result < 0) { /* * Restore and signal the error. */ free (new_buffer); RefCountDestroy(&buffer->ref_count); buffer->ref_count = ref_count; va_end(aq); va_end(ap); return -1; } /* * Detach. This operation might fail, although it is very rare. */ result = RefCountDetach(ref_count, buffer); if (result < 0) { /* * The ref_count structure has not been modified, therefore * we can reuse it. * We need to destroy the other ref_count though. */ free (new_buffer); RefCountDestroy(&buffer->ref_count); buffer->ref_count = ref_count; va_end(aq); va_end(ap); return -1; } /* * Ok, now we need to take care of the buffer. */ unsigned int i = 0; unsigned int used = 0; for (i = 0; i < buffer->used; ++i) { new_buffer[i] = buffer->buffer[i]; if ((buffer->buffer[i] == '\0') && (buffer->mode == BUFFER_BEHAVIOR_CSTRING)) { break; } ++used; } buffer->buffer = new_buffer; buffer->used = used; } printed = vsnprintf(buffer->buffer, buffer->capacity, format, aq); if (printed >= buffer->capacity) { /* * Allocate a larger buffer and retry. * Now is when having a copy of the list pays off :-) */ if (printed > buffer->memory_cap) { /* * We would go over the memory_cap limit. */ va_end(aq); va_end(ap); return -1; } unsigned int required_blocks = (printed / DEFAULT_BUFFER_SIZE) + 1; buffer->buffer = (char *)xrealloc(buffer->buffer, required_blocks * DEFAULT_BUFFER_SIZE); buffer->capacity = required_blocks * DEFAULT_BUFFER_SIZE; buffer->used = 0; printed = vsnprintf(buffer->buffer, buffer->capacity, format, ap); buffer->used = printed; } else { buffer->used = printed; } va_end(aq); va_end(ap); return printed; }
int BufferAppend(Buffer *buffer, const char *bytes, unsigned int length) { if (!buffer || !bytes) { return -1; } if (length + buffer->used > buffer->memory_cap) { return -1; } if (RefCountIsShared(buffer->ref_count)) { char *new_buffer = NULL; new_buffer = (char *)xmalloc(buffer->capacity); /* * Make a local copy of the variables that are required to restore to normality. */ RefCount *ref_count = buffer->ref_count; /* * We try to attach first, since it is more likely that Attach might fail than * detach. */ int result = 0; buffer->ref_count = NULL; RefCountNew(&buffer->ref_count); result = RefCountAttach(buffer->ref_count, buffer); if (result < 0) { /* * Restore and signal the error. */ free (new_buffer); RefCountDestroy(&buffer->ref_count); buffer->ref_count = ref_count; return -1; } /* * Detach. This operation might fail, although it is very rare. */ result = RefCountDetach(ref_count, buffer); if (result < 0) { /* * The ref_count structure has not been modified, therefore * we can reuse it. * We need to destroy the other ref_count though. */ free (new_buffer); RefCountDestroy(&buffer->ref_count); buffer->ref_count = ref_count; return -1; } /* * Ok, now we need to take care of the buffer. */ unsigned int i = 0; unsigned int used = 0; for (i = 0; i < buffer->used; ++i) { new_buffer[i] = buffer->buffer[i]; if ((buffer->buffer[i] == '\0') && (buffer->mode == BUFFER_BEHAVIOR_CSTRING)) { break; } ++used; } buffer->buffer = new_buffer; buffer->used = used; } /* * Check if we have enough space, otherwise create a larger buffer */ if (buffer->used + length >= buffer->capacity) { unsigned int required_blocks = ((buffer->used + length)/ DEFAULT_BUFFER_SIZE) + 1; buffer->buffer = (char *)xrealloc(buffer->buffer, required_blocks * DEFAULT_BUFFER_SIZE); buffer->capacity = required_blocks * DEFAULT_BUFFER_SIZE; } /* * We have a buffer that is large enough, copy the data. */ unsigned int c = 0; unsigned int total = 0; for (c = 0; c < length; ++c) { buffer->buffer[c + buffer->used] = bytes[c]; if ((bytes[c] == '\0') && (buffer->mode = BUFFER_BEHAVIOR_CSTRING)) { break; } ++total; } buffer->used += total; if (buffer->mode == BUFFER_BEHAVIOR_CSTRING) { buffer->buffer[buffer->used] = '\0'; } return buffer->used; }