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); }
// 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); }
int BufferDestroy(Buffer **buffer) { // If already NULL don't bother. if (!buffer || !*buffer) { return 0; } /* * Here is how it goes, if we are shared then we cannot destroy the buffer * we simply detach from it. If we are not shared we need to destroy the buffer * and the RefCount. */ if (RefCountIsShared((*buffer)->ref_count)) { RefCountDetach((*buffer)->ref_count, *buffer); } else { // We can destroy the buffer if ((*buffer)->buffer) { free ((*buffer)->buffer); } // Destroy the RefCount struct RefCountDestroy(&(*buffer)->ref_count); } free (*buffer); *buffer = NULL; return 0; }
int ListDestroy(List **list) { if (!list || !(*list)) { return 0; } int shared = RefCountIsShared((*list)->ref_count); if (shared) { /* * We just detach from the list. */ RefCountDetach((*list)->ref_count, (*list)); } else { // We are the only ones using the list, we can delete it. ListNode *node = NULL; ListNode *p = NULL; for (node = (*list)->first; node; node = p) { if ((*list)->destroy) (*list)->destroy(node->payload); p = node->next; free(node); } RefCountDestroy(&(*list)->ref_count); } free((*list)); *list = NULL; return 0; }
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); }
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 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); /* * 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; } 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); /* * 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; }