90.00% Lines (36/40) 90.00% Functions (9/10)
TLA Baseline Branch
Line Hits Code Line Hits Code
1   // 1   //
2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) 2   // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3   // 3   //
4   // Distributed under the Boost Software License, Version 1.0. (See accompanying 4   // Distributed under the Boost Software License, Version 1.0. (See accompanying
5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5   // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6   // 6   //
7   // Official repository: https://github.com/cppalliance/http 7   // Official repository: https://github.com/cppalliance/http
8   // 8   //
9   9  
10   #ifndef BOOST_HTTP_JSON_JSON_SINK_HPP 10   #ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
11   #define BOOST_HTTP_JSON_JSON_SINK_HPP 11   #define BOOST_HTTP_JSON_JSON_SINK_HPP
12   12  
13   #include <boost/http/config.hpp> 13   #include <boost/http/config.hpp>
14   14  
15   #include <boost/capy/buffers.hpp> 15   #include <boost/capy/buffers.hpp>
16   #include <boost/capy/concept/const_buffer_sequence.hpp> 16   #include <boost/capy/concept/const_buffer_sequence.hpp>
17   #include <boost/capy/ex/immediate.hpp> 17   #include <boost/capy/ex/immediate.hpp>
18   #include <boost/capy/io_result.hpp> 18   #include <boost/capy/io_result.hpp>
19   #include <boost/json/stream_parser.hpp> 19   #include <boost/json/stream_parser.hpp>
20   #include <boost/json/value.hpp> 20   #include <boost/json/value.hpp>
21   21  
22   namespace boost { 22   namespace boost {
23   namespace http { 23   namespace http {
24   24  
25   /** A sink for streaming JSON data to a parser. 25   /** A sink for streaming JSON data to a parser.
26   26  
27   This class wraps a `boost::json::stream_parser` and satisfies the 27   This class wraps a `boost::json::stream_parser` and satisfies the
28   @ref capy::WriteSink concept, enabling incremental JSON parsing 28   @ref capy::WriteSink concept, enabling incremental JSON parsing
29   from any data source that produces buffer sequences. 29   from any data source that produces buffer sequences.
30   30  
31   Since JSON parsing is synchronous, all operations return 31   Since JSON parsing is synchronous, all operations return
32   @ref capy::immediate awaitables with zero suspension overhead. 32   @ref capy::immediate awaitables with zero suspension overhead.
33   33  
34   @par Example 34   @par Example
35   @code 35   @code
36   json_sink sink; 36   json_sink sink;
37   37  
38   // Write JSON data incrementally 38   // Write JSON data incrementally
39   auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":")); 39   auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":"));
40   auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true); 40   auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true);
41   41  
42   // Or use write_eof() separately 42   // Or use write_eof() separately
43   auto [ec3] = co_await sink.write_eof(); 43   auto [ec3] = co_await sink.write_eof();
44   44  
45   // Retrieve the parsed value 45   // Retrieve the parsed value
46   json::value v = sink.release(); 46   json::value v = sink.release();
47   @endcode 47   @endcode
48   48  
49   @par Thread Safety 49   @par Thread Safety
50   Distinct objects: Safe. 50   Distinct objects: Safe.
51   Shared objects: Unsafe. 51   Shared objects: Unsafe.
52   52  
53   @see capy::WriteSink, json::stream_parser 53   @see capy::WriteSink, json::stream_parser
54   */ 54   */
55   class json_sink 55   class json_sink
56   { 56   {
57   json::stream_parser parser_; 57   json::stream_parser parser_;
58   58  
59   template<capy::ConstBufferSequence CB> 59   template<capy::ConstBufferSequence CB>
60   capy::immediate<capy::io_result<std::size_t>> 60   capy::immediate<capy::io_result<std::size_t>>
HITCBC 61   12 write_impl(CB const& buffers, bool eof) 61   12 write_impl(CB const& buffers, bool eof)
62   { 62   {
HITCBC 63   12 std::error_code ec; 63   12 std::error_code ec;
HITCBC 64   12 std::size_t total = 0; 64   12 std::size_t total = 0;
HITCBC 65   12 auto const end = capy::end(buffers); 65   12 auto const end = capy::end(buffers);
HITCBC 66   23 for(auto it = capy::begin(buffers); it != end; ++it) 66   23 for(auto it = capy::begin(buffers); it != end; ++it)
67   { 67   {
HITCBC 68   12 capy::const_buffer buf(*it); 68   12 capy::const_buffer buf(*it);
HITCBC 69   24 auto n = parser_.write( 69   24 auto n = parser_.write(
HITCBC 70   12 static_cast<char const*>(buf.data()), 70   12 static_cast<char const*>(buf.data()),
71   buf.size(), 71   buf.size(),
72   ec); 72   ec);
HITCBC 73   12 total += n; 73   12 total += n;
HITCBC 74   12 if(ec) 74   12 if(ec)
HITCBC 75   1 return capy::ready(ec, total); 75   1 return capy::ready(ec, total);
76   } 76   }
HITCBC 77   11 if(eof) 77   11 if(eof)
78   { 78   {
HITCBC 79   6 parser_.finish(ec); 79   6 parser_.finish(ec);
HITCBC 80   6 if(ec) 80   6 if(ec)
MISUBC 81   return capy::ready(ec, total); 81   return capy::ready(ec, total);
82   } 82   }
HITCBC 83   11 return capy::ready(total); 83   11 return capy::ready(total);
84   } 84   }
85   85  
86   public: 86   public:
87   /** Default constructor. 87   /** Default constructor.
88   88  
89   Constructs a sink with a default-initialized stream parser. 89   Constructs a sink with a default-initialized stream parser.
90   */ 90   */
HITCBC 91   8 json_sink() = default; 91   8 json_sink() = default;
92   92  
93   /** Constructor with parse options. 93   /** Constructor with parse options.
94   94  
95   @param opt Options controlling JSON parsing behavior. 95   @param opt Options controlling JSON parsing behavior.
96   */ 96   */
97   explicit 97   explicit
HITCBC 98   1 json_sink(json::parse_options const& opt) 98   1 json_sink(json::parse_options const& opt)
HITCBC 99   1 : parser_(json::storage_ptr(), opt) 99   1 : parser_(json::storage_ptr(), opt)
100   { 100   {
HITCBC 101   1 } 101   1 }
102   102  
103   /** Constructor with storage and parse options. 103   /** Constructor with storage and parse options.
104   104  
105   @param sp The storage to use for parsed values. 105   @param sp The storage to use for parsed values.
106   @param opt Options controlling JSON parsing behavior. 106   @param opt Options controlling JSON parsing behavior.
107   */ 107   */
MISUBC 108   json_sink( 108   json_sink(
109   json::storage_ptr sp, 109   json::storage_ptr sp,
110   json::parse_options const& opt = {}) 110   json::parse_options const& opt = {})
MISUBC 111   : parser_(std::move(sp), opt) 111   : parser_(std::move(sp), opt)
112   { 112   {
MISUBC 113   } 113   }
114   114  
115   /** Write some data to the JSON parser. 115   /** Write some data to the JSON parser.
116   116  
117   Writes bytes from the buffer sequence to the stream parser. 117   Writes bytes from the buffer sequence to the stream parser.
118   118  
119   @param buffers Buffer sequence containing JSON data. 119   @param buffers Buffer sequence containing JSON data.
120   120  
121   @return An awaitable yielding `(error_code,std::size_t)`. 121   @return An awaitable yielding `(error_code,std::size_t)`.
122   On success, returns the total bytes written. 122   On success, returns the total bytes written.
123   */ 123   */
124   template<capy::ConstBufferSequence CB> 124   template<capy::ConstBufferSequence CB>
125   capy::immediate<capy::io_result<std::size_t>> 125   capy::immediate<capy::io_result<std::size_t>>
126   write_some(CB const& buffers) 126   write_some(CB const& buffers)
127   { 127   {
128   return write_impl(buffers, false); 128   return write_impl(buffers, false);
129   } 129   }
130   130  
131   /** Write data to the JSON parser. 131   /** Write data to the JSON parser.
132   132  
133   Writes all bytes from the buffer sequence to the stream parser. 133   Writes all bytes from the buffer sequence to the stream parser.
134   134  
135   @param buffers Buffer sequence containing JSON data. 135   @param buffers Buffer sequence containing JSON data.
136   136  
137   @return An awaitable yielding `(error_code,std::size_t)`. 137   @return An awaitable yielding `(error_code,std::size_t)`.
138   On success, returns the total bytes written. 138   On success, returns the total bytes written.
139   */ 139   */
140   template<capy::ConstBufferSequence CB> 140   template<capy::ConstBufferSequence CB>
141   capy::immediate<capy::io_result<std::size_t>> 141   capy::immediate<capy::io_result<std::size_t>>
HITCBC 142   5 write(CB const& buffers) 142   5 write(CB const& buffers)
143   { 143   {
HITCBC 144   5 return write_impl(buffers, false); 144   5 return write_impl(buffers, false);
145   } 145   }
146   146  
147   /** Write data with optional end-of-stream. 147   /** Write data with optional end-of-stream.
148   148  
149   Writes all bytes from the buffer sequence to the stream parser. 149   Writes all bytes from the buffer sequence to the stream parser.
150   If @p eof is true, also finishes parsing. 150   If @p eof is true, also finishes parsing.
151   151  
152   @param buffers Buffer sequence containing JSON data. 152   @param buffers Buffer sequence containing JSON data.
153   @param eof If true, signals end of JSON data after writing. 153   @param eof If true, signals end of JSON data after writing.
154   154  
155   @return An awaitable yielding `(error_code,std::size_t)`. 155   @return An awaitable yielding `(error_code,std::size_t)`.
156   On success, returns the total bytes written. 156   On success, returns the total bytes written.
157   */ 157   */
158   template<capy::ConstBufferSequence CB> 158   template<capy::ConstBufferSequence CB>
159   capy::immediate<capy::io_result<std::size_t>> 159   capy::immediate<capy::io_result<std::size_t>>
HITCBC 160   7 write(CB const& buffers, bool eof) 160   7 write(CB const& buffers, bool eof)
161   { 161   {
HITCBC 162   7 return write_impl(buffers, eof); 162   7 return write_impl(buffers, eof);
163   } 163   }
164   164  
165   /** Write final data and signal end of JSON data. 165   /** Write final data and signal end of JSON data.
166   166  
167   Writes all bytes from the buffer sequence to the stream 167   Writes all bytes from the buffer sequence to the stream
168   parser, then finishes parsing. 168   parser, then finishes parsing.
169   169  
170   @param buffers Buffer sequence containing JSON data. 170   @param buffers Buffer sequence containing JSON data.
171   171  
172   @return An awaitable yielding `(error_code,std::size_t)`. 172   @return An awaitable yielding `(error_code,std::size_t)`.
173   On success, returns the total bytes written. 173   On success, returns the total bytes written.
174   */ 174   */
175   template<capy::ConstBufferSequence CB> 175   template<capy::ConstBufferSequence CB>
176   capy::immediate<capy::io_result<std::size_t>> 176   capy::immediate<capy::io_result<std::size_t>>
177   write_eof(CB const& buffers) 177   write_eof(CB const& buffers)
178   { 178   {
179   return write_impl(buffers, true); 179   return write_impl(buffers, true);
180   } 180   }
181   181  
182   /** Signal end of JSON data. 182   /** Signal end of JSON data.
183   183  
184   Finishes parsing and validates the JSON is complete. 184   Finishes parsing and validates the JSON is complete.
185   185  
186   @return An awaitable yielding `(error_code)`. 186   @return An awaitable yielding `(error_code)`.
187   */ 187   */
188   capy::immediate<capy::io_result<>> 188   capy::immediate<capy::io_result<>>
HITCBC 189   2 write_eof() 189   2 write_eof()
190   { 190   {
HITCBC 191   2 std::error_code ec; 191   2 std::error_code ec;
HITCBC 192   2 parser_.finish(ec); 192   2 parser_.finish(ec);
HITCBC 193   2 if(ec) 193   2 if(ec)
HITCBC 194   1 return capy::ready(ec); 194   1 return capy::ready(ec);
HITCBC 195   1 return capy::ready(); 195   1 return capy::ready();
196   } 196   }
197   197  
198   /** Check if parsing is complete. 198   /** Check if parsing is complete.
199   199  
200   @return `true` if a complete JSON value has been parsed. 200   @return `true` if a complete JSON value has been parsed.
201   */ 201   */
202   bool 202   bool
HITCBC 203   11 done() const noexcept 203   11 done() const noexcept
204   { 204   {
HITCBC 205   11 return parser_.done(); 205   11 return parser_.done();
206   } 206   }
207   207  
208   /** Release the parsed JSON value. 208   /** Release the parsed JSON value.
209   209  
210   Returns the parsed value and resets the parser for reuse. 210   Returns the parsed value and resets the parser for reuse.
211   211  
212   @par Preconditions 212   @par Preconditions
213   `this->done() == true` 213   `this->done() == true`
214   214  
215   @return The parsed JSON value. 215   @return The parsed JSON value.
216   */ 216   */
217   json::value 217   json::value
HITCBC 218   7 release() 218   7 release()
219   { 219   {
HITCBC 220   7 return parser_.release(); 220   7 return parser_.release();
221   } 221   }
222   222  
223   /** Reset the parser for a new JSON value. 223   /** Reset the parser for a new JSON value.
224   224  
225   Clears all state and prepares to parse a new value. 225   Clears all state and prepares to parse a new value.
226   */ 226   */
227   void 227   void
HITCBC 228   1 reset() 228   1 reset()
229   { 229   {
HITCBC 230   1 parser_.reset(); 230   1 parser_.reset();
HITCBC 231   1 } 231   1 }
232   }; 232   };
233   233  
234   } // namespace http 234   } // namespace http
235   } // namespace boost 235   } // namespace boost
236   236  
237   #endif 237   #endif