1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef _LIBCPP___FORMAT_BUFFER_H
11 #define _LIBCPP___FORMAT_BUFFER_H
12 
13 #include <__algorithm/copy_n.h>
14 #include <__algorithm/unwrap_iter.h>
15 #include <__config>
16 #include <__format/enable_insertable.h>
17 #include <__format/formatter.h> // for __char_type TODO FMT Move the concept?
18 #include <__iterator/back_insert_iterator.h>
19 #include <__iterator/concepts.h>
20 #include <__iterator/iterator_traits.h>
21 #include <__iterator/wrap_iter.h>
22 #include <__utility/move.h>
23 #include <concepts>
24 #include <cstddef>
25 #include <type_traits>
26 
27 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
28 #  pragma GCC system_header
29 #endif
30 
31 _LIBCPP_BEGIN_NAMESPACE_STD
32 
33 #if _LIBCPP_STD_VER > 17
34 
35 namespace __format {
36 
37 /// A "buffer" that handles writing to the proper iterator.
38 ///
39 /// This helper is used together with the @ref back_insert_iterator to offer
40 /// type-erasure for the formatting functions. This reduces the number to
41 /// template instantiations.
42 template <__formatter::__char_type _CharT>
43 class _LIBCPP_TEMPLATE_VIS __output_buffer {
44 public:
45   using value_type = _CharT;
46 
47   template <class _Tp>
48   _LIBCPP_HIDE_FROM_ABI explicit __output_buffer(_CharT* __ptr,
49                                                  size_t __capacity, _Tp* __obj)
50       : __ptr_(__ptr), __capacity_(__capacity),
51         __flush_([](_CharT* __p, size_t __size, void* __o) {
52           static_cast<_Tp*>(__o)->flush(__p, __size);
53         }),
54         __obj_(__obj) {}
55 
56   _LIBCPP_HIDE_FROM_ABI void reset(_CharT* __ptr, size_t __capacity) {
57     __ptr_ = __ptr;
58     __capacity_ = __capacity;
59   }
60 
61   _LIBCPP_HIDE_FROM_ABI auto make_output_iterator() {
62     return back_insert_iterator{*this};
63   }
64 
65   // TODO FMT It would be nice to have an overload taking a
66   // basic_string_view<_CharT> and append it directly.
67   _LIBCPP_HIDE_FROM_ABI void push_back(_CharT __c) {
68     __ptr_[__size_++] = __c;
69 
70     // Profiling showed flushing after adding is more efficient than flushing
71     // when entering the function.
72     if (__size_ == __capacity_)
73       flush();
74   }
75 
76   _LIBCPP_HIDE_FROM_ABI void flush() {
77     __flush_(__ptr_, __size_, __obj_);
78     __size_ = 0;
79   }
80 
81 private:
82   _CharT* __ptr_;
83   size_t __capacity_;
84   size_t __size_{0};
85   void (*__flush_)(_CharT*, size_t, void*);
86   void* __obj_;
87 };
88 
89 /// A storage using an internal buffer.
90 ///
91 /// This storage is used when writing a single element to the output iterator
92 /// is expensive.
93 template <__formatter::__char_type _CharT>
94 class _LIBCPP_TEMPLATE_VIS __internal_storage {
95 public:
96   _LIBCPP_HIDE_FROM_ABI _CharT* begin() { return __buffer_; }
97   _LIBCPP_HIDE_FROM_ABI size_t capacity() { return __buffer_size_; }
98 
99 private:
100   static constexpr size_t __buffer_size_ = 256 / sizeof(_CharT);
101   _CharT __buffer_[__buffer_size_];
102 };
103 
104 /// A storage writing directly to the storage.
105 ///
106 /// This requires the storage to be a contiguous buffer of \a _CharT.
107 /// Since the output is directly written to the underlying storage this class
108 /// is just an empty class.
109 template <__formatter::__char_type _CharT>
110 class _LIBCPP_TEMPLATE_VIS __direct_storage {};
111 
112 template <class _OutIt, class _CharT>
113 concept __enable_direct_output = __formatter::__char_type<_CharT> &&
114     (same_as<_OutIt, _CharT*>
115 #if _LIBCPP_DEBUG_LEVEL < 2
116      || same_as<_OutIt, __wrap_iter<_CharT*>>
117 #endif
118     );
119 
120 /// Write policy for directly writing to the underlying output.
121 template <class _OutIt, __formatter::__char_type _CharT>
122 class _LIBCPP_TEMPLATE_VIS __writer_direct {
123 public:
124   _LIBCPP_HIDE_FROM_ABI explicit __writer_direct(_OutIt __out_it)
125       : __out_it_(__out_it) {}
126 
127   _LIBCPP_HIDE_FROM_ABI auto out() { return __out_it_; }
128 
129   _LIBCPP_HIDE_FROM_ABI void flush(_CharT*, size_t __size) {
130     // _OutIt can be a __wrap_iter<CharT*>. Therefore the original iterator
131     // is adjusted.
132     __out_it_ += __size;
133   }
134 
135 private:
136   _OutIt __out_it_;
137 };
138 
139 /// Write policy for copying the buffer to the output.
140 template <class _OutIt, __formatter::__char_type _CharT>
141 class _LIBCPP_TEMPLATE_VIS __writer_iterator {
142 public:
143   _LIBCPP_HIDE_FROM_ABI explicit __writer_iterator(_OutIt __out_it)
144       : __out_it_{_VSTD::move(__out_it)} {}
145 
146   _LIBCPP_HIDE_FROM_ABI auto out() { return __out_it_; }
147 
148   _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) {
149     __out_it_ = _VSTD::copy_n(__ptr, __size, _VSTD::move(__out_it_));
150   }
151 
152 private:
153   _OutIt __out_it_;
154 };
155 
156 /// Concept to see whether a \a _Container is insertable.
157 ///
158 /// The concept is used to validate whether multiple calls to a
159 /// \ref back_insert_iterator can be replace by a call to \c _Container::insert.
160 ///
161 /// \note a \a _Container needs to opt-in to the concept by specializing
162 /// \ref __enable_insertable.
163 template <class _Container>
164 concept __insertable =
165     __enable_insertable<_Container> && __formatter::__char_type<typename _Container::value_type> &&
166     requires(_Container& __t, add_pointer_t<typename _Container::value_type> __first,
167              add_pointer_t<typename _Container::value_type> __last) { __t.insert(__t.end(), __first, __last); };
168 
169 /// Extract the container type of a \ref back_insert_iterator.
170 template <class _It>
171 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container {
172   using type = void;
173 };
174 
175 template <__insertable _Container>
176 struct _LIBCPP_TEMPLATE_VIS __back_insert_iterator_container<back_insert_iterator<_Container>> {
177   using type = _Container;
178 };
179 
180 /// Write policy for inserting the buffer in a container.
181 template <class _Container>
182 class _LIBCPP_TEMPLATE_VIS __writer_container {
183 public:
184   using _CharT = typename _Container::value_type;
185 
186   _LIBCPP_HIDE_FROM_ABI explicit __writer_container(back_insert_iterator<_Container> __out_it)
187       : __container_{__out_it.__get_container()} {}
188 
189   _LIBCPP_HIDE_FROM_ABI auto out() { return back_inserter(*__container_); }
190 
191   _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) {
192     __container_->insert(__container_->end(), __ptr, __ptr + __size);
193   }
194 
195 private:
196   _Container* __container_;
197 };
198 
199 /// Selects the type of the writer used for the output iterator.
200 template <class _OutIt, class _CharT>
201 class _LIBCPP_TEMPLATE_VIS __writer_selector {
202   using _Container = typename __back_insert_iterator_container<_OutIt>::type;
203 
204 public:
205   using type = conditional_t<!same_as<_Container, void>, __writer_container<_Container>,
206                              conditional_t<__enable_direct_output<_OutIt, _CharT>, __writer_direct<_OutIt, _CharT>,
207                                            __writer_iterator<_OutIt, _CharT>>>;
208 };
209 
210 /// The generic formatting buffer.
211 template <class _OutIt, __formatter::__char_type _CharT>
212 requires(output_iterator<_OutIt, const _CharT&>) class _LIBCPP_TEMPLATE_VIS
213     __format_buffer {
214   using _Storage =
215       conditional_t<__enable_direct_output<_OutIt, _CharT>,
216                     __direct_storage<_CharT>, __internal_storage<_CharT>>;
217 
218 public:
219   _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) requires(
220       same_as<_Storage, __internal_storage<_CharT>>)
221       : __output_(__storage_.begin(), __storage_.capacity(), this),
222         __writer_(_VSTD::move(__out_it)) {}
223 
224   _LIBCPP_HIDE_FROM_ABI explicit __format_buffer(_OutIt __out_it) requires(
225       same_as<_Storage, __direct_storage<_CharT>>)
226       : __output_(_VSTD::__unwrap_iter(__out_it), size_t(-1), this),
227         __writer_(_VSTD::move(__out_it)) {}
228 
229   _LIBCPP_HIDE_FROM_ABI auto make_output_iterator() {
230     return __output_.make_output_iterator();
231   }
232 
233   _LIBCPP_HIDE_FROM_ABI void flush(_CharT* __ptr, size_t __size) {
234     __writer_.flush(__ptr, __size);
235   }
236 
237   _LIBCPP_HIDE_FROM_ABI _OutIt out() && {
238     __output_.flush();
239     return _VSTD::move(__writer_).out();
240   }
241 
242 private:
243   _LIBCPP_NO_UNIQUE_ADDRESS _Storage __storage_;
244   __output_buffer<_CharT> __output_;
245   typename __writer_selector<_OutIt, _CharT>::type __writer_;
246 };
247 
248 /// A buffer that counts the number of insertions.
249 ///
250 /// Since \ref formatted_size only needs to know the size, the output itself is
251 /// discarded.
252 template <__formatter::__char_type _CharT>
253 class _LIBCPP_TEMPLATE_VIS __formatted_size_buffer {
254 public:
255   _LIBCPP_HIDE_FROM_ABI auto make_output_iterator() { return __output_.make_output_iterator(); }
256 
257   _LIBCPP_HIDE_FROM_ABI void flush(const _CharT*, size_t __size) { __size_ += __size; }
258 
259   _LIBCPP_HIDE_FROM_ABI size_t result() && {
260     __output_.flush();
261     return __size_;
262   }
263 
264 private:
265   __internal_storage<_CharT> __storage_;
266   __output_buffer<_CharT> __output_{__storage_.begin(), __storage_.capacity(), this};
267   size_t __size_{0};
268 };
269 
270 } // namespace __format
271 
272 #endif //_LIBCPP_STD_VER > 17
273 
274 _LIBCPP_END_NAMESPACE_STD
275 
276 #endif // _LIBCPP___FORMAT_BUFFER_H
277