summaryrefslogtreecommitdiffstats
path: root/deps/jemalloc/src/buf_writer.c
blob: 7c6f79403c00168c669abf2ff7c3f176a63f24a1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"

#include "jemalloc/internal/buf_writer.h"
#include "jemalloc/internal/malloc_io.h"

static void *
buf_writer_allocate_internal_buf(tsdn_t *tsdn, size_t buf_len) {
#ifdef JEMALLOC_JET
	if (buf_len > SC_LARGE_MAXCLASS) {
		return NULL;
	}
#else
	assert(buf_len <= SC_LARGE_MAXCLASS);
#endif
	return iallocztm(tsdn, buf_len, sz_size2index(buf_len), false, NULL,
	    true, arena_get(tsdn, 0, false), true);
}

static void
buf_writer_free_internal_buf(tsdn_t *tsdn, void *buf) {
	if (buf != NULL) {
		idalloctm(tsdn, buf, NULL, NULL, true, true);
	}
}

static void
buf_writer_assert(buf_writer_t *buf_writer) {
	assert(buf_writer != NULL);
	assert(buf_writer->write_cb != NULL);
	if (buf_writer->buf != NULL) {
		assert(buf_writer->buf_size > 0);
	} else {
		assert(buf_writer->buf_size == 0);
		assert(buf_writer->internal_buf);
	}
	assert(buf_writer->buf_end <= buf_writer->buf_size);
}

bool
buf_writer_init(tsdn_t *tsdn, buf_writer_t *buf_writer, write_cb_t *write_cb,
    void *cbopaque, char *buf, size_t buf_len) {
	if (write_cb != NULL) {
		buf_writer->write_cb = write_cb;
	} else {
		buf_writer->write_cb = je_malloc_message != NULL ?
		    je_malloc_message : wrtmessage;
	}
	buf_writer->cbopaque = cbopaque;
	assert(buf_len >= 2);
	if (buf != NULL) {
		buf_writer->buf = buf;
		buf_writer->internal_buf = false;
	} else {
		buf_writer->buf = buf_writer_allocate_internal_buf(tsdn,
		    buf_len);
		buf_writer->internal_buf = true;
	}
	if (buf_writer->buf != NULL) {
		buf_writer->buf_size = buf_len - 1; /* Allowing for '\0'. */
	} else {
		buf_writer->buf_size = 0;
	}
	buf_writer->buf_end = 0;
	buf_writer_assert(buf_writer);
	return buf_writer->buf == NULL;
}

void
buf_writer_flush(buf_writer_t *buf_writer) {
	buf_writer_assert(buf_writer);
	if (buf_writer->buf == NULL) {
		return;
	}
	buf_writer->buf[buf_writer->buf_end] = '\0';
	buf_writer->write_cb(buf_writer->cbopaque, buf_writer->buf);
	buf_writer->buf_end = 0;
	buf_writer_assert(buf_writer);
}

void
buf_writer_cb(void *buf_writer_arg, const char *s) {
	buf_writer_t *buf_writer = (buf_writer_t *)buf_writer_arg;
	buf_writer_assert(buf_writer);
	if (buf_writer->buf == NULL) {
		buf_writer->write_cb(buf_writer->cbopaque, s);
		return;
	}
	size_t i, slen, n;
	for (i = 0, slen = strlen(s); i < slen; i += n) {
		if (buf_writer->buf_end == buf_writer->buf_size) {
			buf_writer_flush(buf_writer);
		}
		size_t s_remain = slen - i;
		size_t buf_remain = buf_writer->buf_size - buf_writer->buf_end;
		n = s_remain < buf_remain ? s_remain : buf_remain;
		memcpy(buf_writer->buf + buf_writer->buf_end, s + i, n);
		buf_writer->buf_end += n;
		buf_writer_assert(buf_writer);
	}
	assert(i == slen);
}

void
buf_writer_terminate(tsdn_t *tsdn, buf_writer_t *buf_writer) {
	buf_writer_assert(buf_writer);
	buf_writer_flush(buf_writer);
	if (buf_writer->internal_buf) {
		buf_writer_free_internal_buf(tsdn, buf_writer->buf);
	}
}

void
buf_writer_pipe(buf_writer_t *buf_writer, read_cb_t *read_cb,
    void *read_cbopaque) {
	/*
	 * A tiny local buffer in case the buffered writer failed to allocate
	 * at init.
	 */
	static char backup_buf[16];
	static buf_writer_t backup_buf_writer;

	buf_writer_assert(buf_writer);
	assert(read_cb != NULL);
	if (buf_writer->buf == NULL) {
		buf_writer_init(TSDN_NULL, &backup_buf_writer,
		    buf_writer->write_cb, buf_writer->cbopaque, backup_buf,
		    sizeof(backup_buf));
		buf_writer = &backup_buf_writer;
	}
	assert(buf_writer->buf != NULL);
	ssize_t nread = 0;
	do {
		buf_writer->buf_end += nread;
		buf_writer_assert(buf_writer);
		if (buf_writer->buf_end == buf_writer->buf_size) {
			buf_writer_flush(buf_writer);
		}
		nread = read_cb(read_cbopaque,
		    buf_writer->buf + buf_writer->buf_end,
		    buf_writer->buf_size - buf_writer->buf_end);
	} while (nread > 0);
	buf_writer_flush(buf_writer);
}