11#pragma once
22
3+ #include < algorithm>
34#include < cstdint>
45#include < iterator>
56#include < string_view>
6-
77#include < exception>
88
99namespace hpack {
@@ -21,7 +21,11 @@ struct protocol_error : std::exception {
2121
2222// thrown if there are not enough data for reading header
2323struct incomplete_data_error : hpack::protocol_error {
24- incomplete_data_error () : hpack::protocol_error(" incomplete data" ) {
24+ // approx value - how many bytes need to be readen for receiving next part (int or string)
25+ size_t required_bytes = 0 ;
26+
27+ explicit incomplete_data_error (size_t required_bytes_approx)
28+ : hpack::protocol_error(" incomplete data" ), required_bytes(required_bytes_approx) {
2529 }
2630};
2731
@@ -48,6 +52,8 @@ concept Out = std::output_iterator<T, byte_t>;
4852
4953namespace noexport {
5054
55+ // caches first byte for avoiding *it = x == push_back,
56+ // so *it | mask will be into next byte (may be with back_inserter)
5157template <typename T>
5258struct adapted_output_iterator {
5359 T base_it;
@@ -102,6 +108,54 @@ Original unadapt(byte_t* ptr) {
102108 return reinterpret_cast <Original>(ptr);
103109}
104110
111+ // standard interface (e.g. for vector) for inserting many values at back
112+ // Note: ignores fact, that someone can make super-bad type with push_back + insert making something wrong
113+ template <typename T>
114+ constexpr inline bool can_insert_many =
115+ requires (T& value, const char * p) { value.insert (value.end (), p, p); };
116+
117+ // standard back_insert_iterator rly uses protected field exactly for such accessing
118+ template <typename C>
119+ C& access_protected_container (std::back_insert_iterator<C> c) {
120+ static_assert (std::is_trivially_copyable_v<decltype (c)>);
121+ struct accessor : std::back_insert_iterator<C> {
122+ C* get () noexcept {
123+ return this ->container ;
124+ }
125+ };
126+ return *accessor (c).get ();
127+ }
128+
129+ template <typename C>
130+ requires (can_insert_many<C>)
131+ std::back_insert_iterator<C> do_copy_n_fast (const char * ptr, size_t sz, std::back_insert_iterator<C> it) {
132+ auto & c = access_protected_container (it);
133+ c.insert (c.end (), ptr, ptr + sz);
134+ return it; // back insert iterator does not change on ++/* etc
135+ }
136+
137+ template <typename C>
138+ requires (can_insert_many<C>)
139+ adapted_output_iterator<std::back_insert_iterator<C>> do_copy_n_fast (
140+ const char * ptr, size_t sz, adapted_output_iterator<std::back_insert_iterator<C>> it) {
141+ auto & c = access_protected_container (it.base_it );
142+ c.insert (c.end (), ptr, ptr + sz);
143+ return it; // adapted iterator must be unchanged, since base_it unchanged (its back inserter)
144+ }
145+
146+ // fallback
147+ template <typename It>
148+ It do_copy_n_fast (const char * ptr, size_t sz, It it) {
149+ return std::copy_n (ptr, sz, std::move (it));
150+ }
151+
152+ // makes copy_n, but for back_insert iterator makes insert(end, It, It + n)
153+ // this converts many push_backs into one uninitialized_copy_n
154+ template <typename It>
155+ It copy_n_fast (const char * ptr, size_t sz, It it) {
156+ return do_copy_n_fast (ptr, sz, std::move (it));
157+ }
158+
105159} // namespace noexport
106160
107161struct table_entry {
0 commit comments