5 // Created by James McIlree on 5/7/14.
9 #ifndef __system_cmds__PrintBuffer__
10 #define __system_cmds__PrintBuffer__
13 // Okay, here is how snprintf works.
17 // snprintf(buf, 0, "a"); // Returns 1, buf is unchanged.
18 // snprintf(buf, 1, "a"); // Returns 1, buf = \0
19 // snprintf(buf, 2, "a"); // Returns 1, buf = 'a', \0
21 // So... For a buffer of size N, each print is valid if and only if
22 // it consumes N-1 bytes.
29 size_t _buffer_capacity;
30 size_t _flush_boundary;
34 PrintBuffer(size_t capacity, size_t flush_boundary, int flush_fd) :
35 _buffer((char*)malloc(capacity)),
37 _buffer_capacity(capacity),
38 _flush_boundary(flush_boundary),
41 ASSERT(capacity > 0, "Sanity");
42 ASSERT(_buffer, "Sanity");
43 ASSERT(flush_boundary < capacity, "Sanity");
44 ASSERT(flush_fd != 0, "Must be a valid fd");
52 void set_capacity(size_t capacity) {
53 ASSERT(_buffer_size == 0, "Attempt to reallocate buffer while it still contains data");
59 _buffer = (char*)malloc(capacity);
61 _buffer_capacity = capacity;
66 write(_flush_fd, _buffer, _buffer_size);
71 void printf(const char* format, ...) __attribute__((format(printf, 2, 3))) {
73 size_t remaining_bytes = _buffer_capacity - _buffer_size;
76 va_start(list, format);
77 int bytes_needed = vsnprintf(&_buffer[_buffer_size], remaining_bytes, format, list);
80 // There are three levels of "end" detection.
82 // 1) If bytes_needed is >= capacity, we must flush, grow capacity, and repeat.
83 // 2) If bytes_needed is >= remaining_bytes, we must flush, and repeat.
84 // 3) If bytes_needed + _buffer_size comes within _flush_boundary bytes of the end, flush.
86 // NOTE snprintf behavior, we need bytes_needed+1 bytes
87 // to actually fully output all string characters.
89 // NOTE for any repeat condition, we do not commit the bytes that were written to the buffer.
93 if (bytes_needed >= remaining_bytes) {
96 // Save a common path if test by checking this only inside Condition 2
99 if (bytes_needed >= _buffer_capacity) {
100 set_capacity(bytes_needed+1);
106 // Commit the snprintf
107 _buffer_size += bytes_needed;
110 if (remaining_bytes - bytes_needed <= _flush_boundary) {
116 #endif /* defined(__system_cmds__PrintBuffer__) */