/* Write sz bytes from cp into a newly allocated buffer buf. * Returns NULL when passed a NULL cp or zero sz. * Asserts on failure: only for use in unit tests. * buf must be freed using buf_free(). */ buf_t * buf_new_with_data(const char *cp, size_t sz) { /* Validate arguments */ if (!cp || sz <= 0) { return NULL; } tor_assert(sz < SSIZE_T_CEILING); /* Allocate a buffer */ buf_t *buf = buf_new_with_capacity(sz); tor_assert(buf); buf_assert_ok(buf); tor_assert(!buf->head); /* Allocate a chunk that is sz bytes long */ buf->head = chunk_new_with_alloc_size(CHUNK_ALLOC_SIZE(sz)); buf->tail = buf->head; tor_assert(buf->head); buf_assert_ok(buf); tor_assert(buf_allocation(buf) >= sz); /* Copy the data and size the buffers */ tor_assert(sz <= buf_slack(buf)); tor_assert(sz <= CHUNK_REMAINING_CAPACITY(buf->head)); memcpy(&buf->head->mem[0], cp, sz); buf->datalen = sz; buf->head->datalen = sz; buf->head->data = &buf->head->mem[0]; buf_assert_ok(buf); /* Make sure everything is large enough */ tor_assert(buf_allocation(buf) >= sz); tor_assert(buf_allocation(buf) >= buf_datalen(buf) + buf_slack(buf)); /* Does the buffer implementation allocate more than the requested size? * (for example, by rounding up). If so, these checks will fail. */ tor_assert(buf_datalen(buf) == sz); tor_assert(buf_slack(buf) == 0); return buf; }
static void test_buffer_allocation_tracking(void *arg) { char *junk = tor_malloc(16384); buf_t *buf1 = NULL, *buf2 = NULL; int i; (void)arg; crypto_rand(junk, 16384); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); buf1 = buf_new(); tt_assert(buf1); buf2 = buf_new(); tt_assert(buf2); tt_int_op(buf_allocation(buf1), OP_EQ, 0); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); tt_int_op(buf_allocation(buf1), OP_EQ, 16384); fetch_from_buf(junk, 100, buf1); tt_int_op(buf_allocation(buf1), OP_EQ, 16384); /* still 4 4k chunks */ tt_int_op(buf_get_total_allocation(), OP_EQ, 16384); fetch_from_buf(junk, 4096, buf1); /* drop a 1k chunk... */ tt_int_op(buf_allocation(buf1), OP_EQ, 3*4096); /* now 3 4k chunks */ tt_int_op(buf_get_total_allocation(), OP_EQ, 12288); /* that chunk was really freed. */ write_to_buf(junk, 4000, buf2); tt_int_op(buf_allocation(buf2), OP_EQ, 4096); /* another 4k chunk. */ /* * We bounce back up to 16384 by allocating a new chunk. */ tt_int_op(buf_get_total_allocation(), OP_EQ, 16384); write_to_buf(junk, 4000, buf2); tt_int_op(buf_allocation(buf2), OP_EQ, 8192); /* another 4k chunk. */ tt_int_op(buf_get_total_allocation(), OP_EQ, 5*4096); /* that chunk was new. */ /* Make a really huge buffer */ for (i = 0; i < 1000; ++i) { write_to_buf(junk, 4000, buf2); } tt_int_op(buf_allocation(buf2), OP_GE, 4008000); tt_int_op(buf_get_total_allocation(), OP_GE, 4008000); buf_free(buf2); buf2 = NULL; tt_int_op(buf_get_total_allocation(), OP_LT, 4008000); tt_int_op(buf_get_total_allocation(), OP_EQ, buf_allocation(buf1)); buf_free(buf1); buf1 = NULL; tt_int_op(buf_get_total_allocation(), OP_EQ, 0); done: buf_free(buf1); buf_free(buf2); tor_free(junk); }
static void test_buffer_allocation_tracking(void *arg) { char *junk = tor_malloc(16384); buf_t *buf1 = NULL, *buf2 = NULL; int i; (void)arg; crypto_rand(junk, 16384); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); buf1 = buf_new(); tt_assert(buf1); buf2 = buf_new(); tt_assert(buf2); tt_int_op(buf_allocation(buf1), OP_EQ, 0); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); write_to_buf(junk, 4000, buf1); tt_int_op(buf_allocation(buf1), OP_EQ, 16384); fetch_from_buf(junk, 100, buf1); tt_int_op(buf_allocation(buf1), OP_EQ, 16384); /* still 4 4k chunks */ tt_int_op(buf_get_total_allocation(), OP_EQ, 16384); fetch_from_buf(junk, 4096, buf1); /* drop a 1k chunk... */ tt_int_op(buf_allocation(buf1), OP_EQ, 3*4096); /* now 3 4k chunks */ #ifdef ENABLE_BUF_FREELISTS tt_int_op(buf_get_total_allocation(), OP_EQ, 16384); /* that chunk went onto the freelist. */ #else tt_int_op(buf_get_total_allocation(), OP_EQ, 12288); /* that chunk was really freed. */ #endif write_to_buf(junk, 4000, buf2); tt_int_op(buf_allocation(buf2), OP_EQ, 4096); /* another 4k chunk. */ /* * If we're using freelists, size stays at 16384 because we just pulled a * chunk from the freelist. If we aren't, we bounce back up to 16384 by * allocating a new chunk. */ tt_int_op(buf_get_total_allocation(), OP_EQ, 16384); write_to_buf(junk, 4000, buf2); tt_int_op(buf_allocation(buf2), OP_EQ, 8192); /* another 4k chunk. */ tt_int_op(buf_get_total_allocation(), OP_EQ, 5*4096); /* that chunk was new. */ /* Make a really huge buffer */ for (i = 0; i < 1000; ++i) { write_to_buf(junk, 4000, buf2); } tt_int_op(buf_allocation(buf2), OP_GE, 4008000); tt_int_op(buf_get_total_allocation(), OP_GE, 4008000); buf_free(buf2); buf2 = NULL; tt_int_op(buf_get_total_allocation(), OP_LT, 4008000); buf_shrink_freelists(1); tt_int_op(buf_get_total_allocation(), OP_EQ, buf_allocation(buf1)); buf_free(buf1); buf1 = NULL; buf_shrink_freelists(1); tt_int_op(buf_get_total_allocation(), OP_EQ, 0); done: buf_free(buf1); buf_free(buf2); buf_shrink_freelists(1); tor_free(junk); }