@@ -38,36 +38,65 @@ template <WriteMode write_mode> struct Mode {
3838#endif
3939};
4040
41+ template <WriteMode write_mode> class Writer ;
42+
4143template <WriteMode write_mode> struct WriteBuffer {
42- using StreamWriter = int (*)(cpp::string_view, void *);
4344 char *buff;
44- const char *init_buff; // for checking when resize.
4545 size_t buff_len;
4646 size_t buff_cur = 0 ;
47-
48- // The stream writer will be called when the buffer is full. It will be passed
49- // string_views to write to the stream.
50- const StreamWriter stream_writer;
51- void *output_target;
52-
5347 // The current writing mode in case the user wants runtime dispatch of the
5448 // stream writer with function pointers.
5549 [[maybe_unused]] WriteMode write_mode_;
5650
57- LIBC_INLINE WriteBuffer (char *buff, size_t buff_len, StreamWriter hook,
58- void *target)
59- : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
60- output_target(target), write_mode_(WriteMode::FLUSH_TO_STREAM) {}
51+ protected:
52+ LIBC_INLINE WriteBuffer (char *buff, size_t buff_len, WriteMode mode)
53+ : buff(buff), buff_len(buff_len), write_mode_(mode) {}
54+
55+ private:
56+ friend class Writer <write_mode>;
57+ // The overflow_write method will handle the case when adding new_str to
58+ // the buffer would overflow it. Specific actions will depend on the buffer
59+ // type / write_mode.
60+ LIBC_INLINE int overflow_write (cpp::string_view new_str);
61+ };
62+
63+ // Buffer variant that discards characters that don't fit into the buffer.
64+ struct DropOverflowBuffer
65+ : public WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> {
66+ LIBC_INLINE DropOverflowBuffer (char *buff, size_t buff_len)
67+ : WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>(
68+ buff, buff_len, WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
69+
70+ LIBC_INLINE int fill_remaining_to_buff (cpp::string_view new_str) {
71+ if (buff_cur < buff_len) {
72+ size_t bytes_to_write = buff_len - buff_cur;
73+ if (bytes_to_write > new_str.size ()) {
74+ bytes_to_write = new_str.size ();
75+ }
76+ inline_memcpy (buff + buff_cur, new_str.data (), bytes_to_write);
77+ buff_cur += bytes_to_write;
78+ }
79+ return WRITE_OK;
80+ }
81+ };
6182
62- LIBC_INLINE WriteBuffer (char *buff, size_t buff_len)
63- : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr ),
64- output_target(nullptr ),
65- write_mode_(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
83+ // Buffer variant that flushes to stream when it gets full.
84+ struct FlushingBuffer
85+ : public WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> {
86+ // The stream writer will be called when the buffer is full. It will be passed
87+ // string_views to write to the stream.
88+ using StreamWriter = int (*)(cpp::string_view, void *);
89+ const StreamWriter stream_writer;
90+ void *output_target;
6691
67- LIBC_INLINE WriteBuffer (char *buff, size_t buff_len, StreamWriter hook)
68- : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
69- output_target(this ), write_mode_(WriteMode::RESIZE_AND_FILL_BUFF) {}
92+ LIBC_INLINE FlushingBuffer (char *buff, size_t buff_len, StreamWriter hook,
93+ void *target)
94+ : WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value>(
95+ buff, buff_len, WriteMode::FLUSH_TO_STREAM),
96+ stream_writer(hook), output_target(target) {}
7097
98+ // Flushes the entire current buffer to stream, followed by the new_str (if
99+ // non-empty).
71100 LIBC_INLINE int flush_to_stream (cpp::string_view new_str) {
72101 if (buff_cur > 0 ) {
73102 int retval = stream_writer ({buff, buff_cur}, output_target);
@@ -83,48 +112,61 @@ template <WriteMode write_mode> struct WriteBuffer {
83112 return WRITE_OK;
84113 }
85114
86- LIBC_INLINE int fill_remaining_to_buff (cpp::string_view new_str) {
87- if (buff_cur < buff_len) {
88- size_t bytes_to_write = buff_len - buff_cur;
89- if (bytes_to_write > new_str.size ()) {
90- bytes_to_write = new_str.size ();
91- }
92- inline_memcpy (buff + buff_cur, new_str.data (), bytes_to_write);
93- buff_cur += bytes_to_write;
94- }
95- return WRITE_OK;
96- }
115+ LIBC_INLINE int flush_to_stream () { return flush_to_stream ({}); }
116+ };
97117
98- LIBC_INLINE int resize_and_write (cpp::string_view new_str) {
99- return stream_writer (new_str, output_target);
100- }
118+ // Buffer variant that calls a resizing callback when it gets full.
119+ struct ResizingBuffer
120+ : public WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> {
121+ using ResizeWriter = int (*)(cpp::string_view, ResizingBuffer *);
122+ const ResizeWriter resize_writer;
123+ const char *init_buff; // for checking when resize.
101124
102- // The overflow_write method is intended to be called to write the contents of
103- // the buffer and new_str to the stream_writer if it exists. If a resizing
104- // hook is provided, it will resize the buffer and write the contents. If
105- // neither a stream_writer nor a resizing hook is provided, it will fill the
106- // remaining space in the buffer with new_str and drop the overflow. Calling
107- // this with an empty string will flush the buffer if relevant.
108-
109- LIBC_INLINE int overflow_write (cpp::string_view new_str) {
110- if constexpr (write_mode == WriteMode::RUNTIME_DISPATCH) {
111- if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
112- return fill_remaining_to_buff (new_str);
113- else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
114- return flush_to_stream (new_str);
115- else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
116- return resize_and_write (new_str);
117- } else if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {
118- return fill_remaining_to_buff (new_str);
119- } else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM) {
120- return flush_to_stream (new_str);
121- } else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF) {
122- return resize_and_write (new_str);
123- }
124- __builtin_unreachable ();
125+ LIBC_INLINE ResizingBuffer (char *buff, size_t buff_len, ResizeWriter hook)
126+ : WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value>(
127+ buff, buff_len, WriteMode::RESIZE_AND_FILL_BUFF),
128+ resize_writer(hook), init_buff(buff) {}
129+
130+ // Invokes the callback that is supposed to resize the buffer and make
131+ // it large enough to fit the new_str addition.
132+ LIBC_INLINE int resize_and_write (cpp::string_view new_str) {
133+ return resize_writer (new_str, this );
125134 }
126135};
127136
137+ template <>
138+ LIBC_INLINE int WriteBuffer<WriteMode::RUNTIME_DISPATCH>::overflow_write(
139+ cpp::string_view new_str) {
140+ if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
141+ return reinterpret_cast <DropOverflowBuffer *>(this )->fill_remaining_to_buff (
142+ new_str);
143+ else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
144+ return reinterpret_cast <FlushingBuffer *>(this )->flush_to_stream (new_str);
145+ else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
146+ return reinterpret_cast <ResizingBuffer *>(this )->resize_and_write (new_str);
147+ __builtin_unreachable ();
148+ }
149+
150+ template <>
151+ LIBC_INLINE int
152+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::overflow_write(
153+ cpp::string_view new_str) {
154+ return reinterpret_cast <DropOverflowBuffer *>(this )->fill_remaining_to_buff (
155+ new_str);
156+ }
157+
158+ template <>
159+ LIBC_INLINE int WriteBuffer<WriteMode::FLUSH_TO_STREAM>::overflow_write(
160+ cpp::string_view new_str) {
161+ return reinterpret_cast <FlushingBuffer *>(this )->flush_to_stream (new_str);
162+ }
163+
164+ template <>
165+ LIBC_INLINE int WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>::overflow_write(
166+ cpp::string_view new_str) {
167+ return reinterpret_cast <ResizingBuffer *>(this )->resize_and_write (new_str);
168+ }
169+
128170template <WriteMode write_mode> class Writer final {
129171 WriteBuffer<write_mode> &wb;
130172 size_t chars_written = 0 ;
0 commit comments