xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/buf_writer.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
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