1*7bdf38e5Schristos #include "test/jemalloc_test.h" 2*7bdf38e5Schristos 3*7bdf38e5Schristos #include "jemalloc/internal/buf_writer.h" 4*7bdf38e5Schristos 5*7bdf38e5Schristos #define TEST_BUF_SIZE 16 6*7bdf38e5Schristos #define UNIT_MAX (TEST_BUF_SIZE * 3) 7*7bdf38e5Schristos 8*7bdf38e5Schristos static size_t test_write_len; 9*7bdf38e5Schristos static char test_buf[TEST_BUF_SIZE]; 10*7bdf38e5Schristos static uint64_t arg; 11*7bdf38e5Schristos static uint64_t arg_store; 12*7bdf38e5Schristos 13*7bdf38e5Schristos static void 14*7bdf38e5Schristos test_write_cb(void *cbopaque, const char *s) { 15*7bdf38e5Schristos size_t prev_test_write_len = test_write_len; 16*7bdf38e5Schristos test_write_len += strlen(s); /* only increase the length */ 17*7bdf38e5Schristos arg_store = *(uint64_t *)cbopaque; /* only pass along the argument */ 18*7bdf38e5Schristos assert_zu_le(prev_test_write_len, test_write_len, 19*7bdf38e5Schristos "Test write overflowed"); 20*7bdf38e5Schristos } 21*7bdf38e5Schristos 22*7bdf38e5Schristos static void 23*7bdf38e5Schristos test_buf_writer_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { 24*7bdf38e5Schristos char s[UNIT_MAX + 1]; 25*7bdf38e5Schristos size_t n_unit, remain, i; 26*7bdf38e5Schristos ssize_t unit; 27*7bdf38e5Schristos 28*7bdf38e5Schristos assert(buf_writer->buf != NULL); 29*7bdf38e5Schristos memset(s, 'a', UNIT_MAX); 30*7bdf38e5Schristos arg = 4; /* Starting value of random argument. */ 31*7bdf38e5Schristos arg_store = arg; 32*7bdf38e5Schristos for (unit = UNIT_MAX; unit >= 0; --unit) { 33*7bdf38e5Schristos /* unit keeps decreasing, so strlen(s) is always unit. */ 34*7bdf38e5Schristos s[unit] = '\0'; 35*7bdf38e5Schristos for (n_unit = 1; n_unit <= 3; ++n_unit) { 36*7bdf38e5Schristos test_write_len = 0; 37*7bdf38e5Schristos remain = 0; 38*7bdf38e5Schristos for (i = 1; i <= n_unit; ++i) { 39*7bdf38e5Schristos arg = prng_lg_range_u64(&arg, 64); 40*7bdf38e5Schristos buf_writer_cb(buf_writer, s); 41*7bdf38e5Schristos remain += unit; 42*7bdf38e5Schristos if (remain > buf_writer->buf_size) { 43*7bdf38e5Schristos /* Flushes should have happened. */ 44*7bdf38e5Schristos assert_u64_eq(arg_store, arg, "Call " 45*7bdf38e5Schristos "back argument didn't get through"); 46*7bdf38e5Schristos remain %= buf_writer->buf_size; 47*7bdf38e5Schristos if (remain == 0) { 48*7bdf38e5Schristos /* Last flush should be lazy. */ 49*7bdf38e5Schristos remain += buf_writer->buf_size; 50*7bdf38e5Schristos } 51*7bdf38e5Schristos } 52*7bdf38e5Schristos assert_zu_eq(test_write_len + remain, i * unit, 53*7bdf38e5Schristos "Incorrect length after writing %zu strings" 54*7bdf38e5Schristos " of length %zu", i, unit); 55*7bdf38e5Schristos } 56*7bdf38e5Schristos buf_writer_flush(buf_writer); 57*7bdf38e5Schristos expect_zu_eq(test_write_len, n_unit * unit, 58*7bdf38e5Schristos "Incorrect length after flushing at the end of" 59*7bdf38e5Schristos " writing %zu strings of length %zu", n_unit, unit); 60*7bdf38e5Schristos } 61*7bdf38e5Schristos } 62*7bdf38e5Schristos buf_writer_terminate(tsdn, buf_writer); 63*7bdf38e5Schristos } 64*7bdf38e5Schristos 65*7bdf38e5Schristos TEST_BEGIN(test_buf_write_static) { 66*7bdf38e5Schristos buf_writer_t buf_writer; 67*7bdf38e5Schristos tsdn_t *tsdn = tsdn_fetch(); 68*7bdf38e5Schristos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 69*7bdf38e5Schristos test_buf, TEST_BUF_SIZE), 70*7bdf38e5Schristos "buf_writer_init() should not encounter error on static buffer"); 71*7bdf38e5Schristos test_buf_writer_body(tsdn, &buf_writer); 72*7bdf38e5Schristos } 73*7bdf38e5Schristos TEST_END 74*7bdf38e5Schristos 75*7bdf38e5Schristos TEST_BEGIN(test_buf_write_dynamic) { 76*7bdf38e5Schristos buf_writer_t buf_writer; 77*7bdf38e5Schristos tsdn_t *tsdn = tsdn_fetch(); 78*7bdf38e5Schristos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 79*7bdf38e5Schristos NULL, TEST_BUF_SIZE), "buf_writer_init() should not OOM"); 80*7bdf38e5Schristos test_buf_writer_body(tsdn, &buf_writer); 81*7bdf38e5Schristos } 82*7bdf38e5Schristos TEST_END 83*7bdf38e5Schristos 84*7bdf38e5Schristos TEST_BEGIN(test_buf_write_oom) { 85*7bdf38e5Schristos buf_writer_t buf_writer; 86*7bdf38e5Schristos tsdn_t *tsdn = tsdn_fetch(); 87*7bdf38e5Schristos assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 88*7bdf38e5Schristos NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); 89*7bdf38e5Schristos assert(buf_writer.buf == NULL); 90*7bdf38e5Schristos 91*7bdf38e5Schristos char s[UNIT_MAX + 1]; 92*7bdf38e5Schristos size_t n_unit, i; 93*7bdf38e5Schristos ssize_t unit; 94*7bdf38e5Schristos 95*7bdf38e5Schristos memset(s, 'a', UNIT_MAX); 96*7bdf38e5Schristos arg = 4; /* Starting value of random argument. */ 97*7bdf38e5Schristos arg_store = arg; 98*7bdf38e5Schristos for (unit = UNIT_MAX; unit >= 0; unit -= UNIT_MAX / 4) { 99*7bdf38e5Schristos /* unit keeps decreasing, so strlen(s) is always unit. */ 100*7bdf38e5Schristos s[unit] = '\0'; 101*7bdf38e5Schristos for (n_unit = 1; n_unit <= 3; ++n_unit) { 102*7bdf38e5Schristos test_write_len = 0; 103*7bdf38e5Schristos for (i = 1; i <= n_unit; ++i) { 104*7bdf38e5Schristos arg = prng_lg_range_u64(&arg, 64); 105*7bdf38e5Schristos buf_writer_cb(&buf_writer, s); 106*7bdf38e5Schristos assert_u64_eq(arg_store, arg, 107*7bdf38e5Schristos "Call back argument didn't get through"); 108*7bdf38e5Schristos assert_zu_eq(test_write_len, i * unit, 109*7bdf38e5Schristos "Incorrect length after writing %zu strings" 110*7bdf38e5Schristos " of length %zu", i, unit); 111*7bdf38e5Schristos } 112*7bdf38e5Schristos buf_writer_flush(&buf_writer); 113*7bdf38e5Schristos expect_zu_eq(test_write_len, n_unit * unit, 114*7bdf38e5Schristos "Incorrect length after flushing at the end of" 115*7bdf38e5Schristos " writing %zu strings of length %zu", n_unit, unit); 116*7bdf38e5Schristos } 117*7bdf38e5Schristos } 118*7bdf38e5Schristos buf_writer_terminate(tsdn, &buf_writer); 119*7bdf38e5Schristos } 120*7bdf38e5Schristos TEST_END 121*7bdf38e5Schristos 122*7bdf38e5Schristos static int test_read_count; 123*7bdf38e5Schristos static size_t test_read_len; 124*7bdf38e5Schristos static uint64_t arg_sum; 125*7bdf38e5Schristos 126*7bdf38e5Schristos ssize_t 127*7bdf38e5Schristos test_read_cb(void *cbopaque, void *buf, size_t limit) { 128*7bdf38e5Schristos static uint64_t rand = 4; 129*7bdf38e5Schristos 130*7bdf38e5Schristos arg_sum += *(uint64_t *)cbopaque; 131*7bdf38e5Schristos assert_zu_gt(limit, 0, "Limit for read_cb must be positive"); 132*7bdf38e5Schristos --test_read_count; 133*7bdf38e5Schristos if (test_read_count == 0) { 134*7bdf38e5Schristos return -1; 135*7bdf38e5Schristos } else { 136*7bdf38e5Schristos size_t read_len = limit; 137*7bdf38e5Schristos if (limit > 1) { 138*7bdf38e5Schristos rand = prng_range_u64(&rand, (uint64_t)limit); 139*7bdf38e5Schristos read_len -= (size_t)rand; 140*7bdf38e5Schristos } 141*7bdf38e5Schristos assert(read_len > 0); 142*7bdf38e5Schristos memset(buf, 'a', read_len); 143*7bdf38e5Schristos size_t prev_test_read_len = test_read_len; 144*7bdf38e5Schristos test_read_len += read_len; 145*7bdf38e5Schristos assert_zu_le(prev_test_read_len, test_read_len, 146*7bdf38e5Schristos "Test read overflowed"); 147*7bdf38e5Schristos return read_len; 148*7bdf38e5Schristos } 149*7bdf38e5Schristos } 150*7bdf38e5Schristos 151*7bdf38e5Schristos static void 152*7bdf38e5Schristos test_buf_writer_pipe_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { 153*7bdf38e5Schristos arg = 4; /* Starting value of random argument. */ 154*7bdf38e5Schristos for (int count = 5; count > 0; --count) { 155*7bdf38e5Schristos arg = prng_lg_range_u64(&arg, 64); 156*7bdf38e5Schristos arg_sum = 0; 157*7bdf38e5Schristos test_read_count = count; 158*7bdf38e5Schristos test_read_len = 0; 159*7bdf38e5Schristos test_write_len = 0; 160*7bdf38e5Schristos buf_writer_pipe(buf_writer, test_read_cb, &arg); 161*7bdf38e5Schristos assert(test_read_count == 0); 162*7bdf38e5Schristos expect_u64_eq(arg_sum, arg * count, ""); 163*7bdf38e5Schristos expect_zu_eq(test_write_len, test_read_len, 164*7bdf38e5Schristos "Write length should be equal to read length"); 165*7bdf38e5Schristos } 166*7bdf38e5Schristos buf_writer_terminate(tsdn, buf_writer); 167*7bdf38e5Schristos } 168*7bdf38e5Schristos 169*7bdf38e5Schristos TEST_BEGIN(test_buf_write_pipe) { 170*7bdf38e5Schristos buf_writer_t buf_writer; 171*7bdf38e5Schristos tsdn_t *tsdn = tsdn_fetch(); 172*7bdf38e5Schristos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 173*7bdf38e5Schristos test_buf, TEST_BUF_SIZE), 174*7bdf38e5Schristos "buf_writer_init() should not encounter error on static buffer"); 175*7bdf38e5Schristos test_buf_writer_pipe_body(tsdn, &buf_writer); 176*7bdf38e5Schristos } 177*7bdf38e5Schristos TEST_END 178*7bdf38e5Schristos 179*7bdf38e5Schristos TEST_BEGIN(test_buf_write_pipe_oom) { 180*7bdf38e5Schristos buf_writer_t buf_writer; 181*7bdf38e5Schristos tsdn_t *tsdn = tsdn_fetch(); 182*7bdf38e5Schristos assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 183*7bdf38e5Schristos NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); 184*7bdf38e5Schristos test_buf_writer_pipe_body(tsdn, &buf_writer); 185*7bdf38e5Schristos } 186*7bdf38e5Schristos TEST_END 187*7bdf38e5Schristos 188*7bdf38e5Schristos int 189*7bdf38e5Schristos main(void) { 190*7bdf38e5Schristos return test( 191*7bdf38e5Schristos test_buf_write_static, 192*7bdf38e5Schristos test_buf_write_dynamic, 193*7bdf38e5Schristos test_buf_write_oom, 194*7bdf38e5Schristos test_buf_write_pipe, 195*7bdf38e5Schristos test_buf_write_pipe_oom); 196*7bdf38e5Schristos } 197