packio
incremental_buffers.h
1 // This Source Code Form is subject to the terms of the Mozilla Public
2 // License, v. 2.0. If a copy of the MPL was not distributed with this
3 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 
5 #ifndef PACKIO_NL_JSON_RPC_INCREMENTAL_BUFFERS_H
6 #define PACKIO_NL_JSON_RPC_INCREMENTAL_BUFFERS_H
7 
8 #include <cassert>
9 #include <deque>
10 #include <optional>
11 #include <string>
12 #include <string_view>
13 #include <vector>
14 
15 namespace packio {
16 namespace nl_json_rpc {
17 
18 class incremental_buffers {
19 public:
20  std::size_t available_buffers() const
21  { //
22  return serialized_objects_.size();
23  }
24 
25  std::optional<std::string> get_parsed_buffer()
26  {
27  if (serialized_objects_.empty()) {
28  return std::nullopt;
29  }
30 
31  auto buffer = std::move(serialized_objects_.front());
32  serialized_objects_.pop_front();
33  return buffer;
34  }
35 
36  void feed(std::string_view data)
37  {
38  reserve_in_place_buffer(data.size());
39  std::copy(begin(data), end(data), in_place_buffer());
40  in_place_buffer_consumed(data.size());
41  }
42 
43  char* in_place_buffer()
44  { //
45  return raw_buffer_.data() + buffer_.size();
46  }
47 
48  std::size_t in_place_buffer_capacity() const
49  { //
50  return raw_buffer_.size() - buffer_.size();
51  }
52 
53  void in_place_buffer_consumed(std::size_t bytes)
54  {
55  if (bytes == 0) {
56  return;
57  }
58  incremental_parse(bytes);
59  }
60 
61  void reserve_in_place_buffer(std::size_t bytes)
62  {
63  if (in_place_buffer_capacity() >= bytes) {
64  return;
65  }
66  raw_buffer_.resize(buffer_.size() + bytes);
67  }
68 
69 private:
70  void incremental_parse(std::size_t bytes)
71  {
72  if (bytes == 0) {
73  return;
74  }
75 
76  if (buffer_.empty()) {
77  std::string_view new_data{in_place_buffer(), bytes};
78  auto first_pos = new_data.find_first_of("{[");
79  if (first_pos == std::string::npos) {
80  return;
81  }
82 
83  initialize(new_data[first_pos]);
84  }
85 
86  std::size_t search_pos = buffer_.size();
87  buffer_ = std::string_view{raw_buffer_.data(), buffer_.size() + bytes};
88 
89  while (true) {
90  auto token_pos = buffer_.find_first_of(tokens_, search_pos);
91  if (token_pos == std::string::npos) {
92  break;
93  }
94  search_pos = token_pos + 1;
95 
96  char token = buffer_[token_pos];
97  if (token == '"' && !is_escaped(token_pos)) {
98  in_string_ = !in_string_;
99  continue;
100  }
101 
102  if (in_string_) {
103  continue;
104  }
105 
106  if (token == last_char_) {
107  if (--depth_ == 0) {
108  // found objet, store the interesting part of the buffer
109  std::size_t object_size = token_pos + 1;
110  serialized_objects_.emplace_back(
111  raw_buffer_.data(), object_size);
112  std::size_t bytes_left = buffer_.size() - object_size;
113  std::copy(
114  raw_buffer_.begin() + object_size,
115  raw_buffer_.begin() + object_size + bytes_left,
116  raw_buffer_.begin());
117  buffer_ = std::string_view{raw_buffer_.data(), bytes_left};
118  token_pos -= object_size;
119  search_pos -= object_size;
120  }
121  }
122  else {
123  assert(token == first_char_);
124  ++depth_;
125  }
126  }
127  }
128 
129  bool is_escaped(std::size_t pos)
130  {
131  bool escaped = false;
132  while (pos-- > 0u) {
133  if (buffer_[pos] == '\\') {
134  escaped = !escaped;
135  }
136  else {
137  break;
138  }
139  }
140  return escaped;
141  }
142 
143  void initialize(char first_char)
144  {
145  first_char_ = first_char;
146  if (first_char_ == '{') {
147  last_char_ = '}';
148  tokens_ = "{}\"";
149  }
150  else {
151  assert(first_char_ == '[');
152  last_char_ = ']';
153  tokens_ = "[]\"";
154  }
155  depth_ = 0;
156  in_string_ = false;
157  }
158 
159  bool in_string_;
160  int depth_;
161  char first_char_;
162  char last_char_;
163  const char* tokens_;
164 
165  std::string_view buffer_;
166  std::vector<char> raw_buffer_;
167 
168  std::deque<std::string> serialized_objects_;
169 };
170 
171 } // nl_json_rpc
172 } // packio
173 
174 #endif // PACKIO_NL_JSON_RPC_INCREMENTAL_BUFFERS_H
The packio namespace.
Definition: arg.h:14