Github   Telegram
Loading...
Searching...
No Matches
array_parser.hpp
1#pragma once
2
3#include <userver/formats/common/path.hpp>
4#include <userver/formats/json/parser/typed_parser.hpp>
5#include <userver/utils/meta.hpp>
6
7USERVER_NAMESPACE_BEGIN
8
9namespace formats::json::parser {
10
11// Parser for array -> vector/set/unordered_set
12template <typename Item, typename ItemParser,
13 typename Array = std::vector<Item>>
14class ArrayParser final : public TypedParser<Array>, public Subscriber<Item> {
15 public:
16 explicit ArrayParser(ItemParser& item_parser) : item_parser_(item_parser) {
17 this->item_parser_.Subscribe(*this);
18 }
19
20 void Reset() override {
21 index_ = 0;
22 state_ = State::kStart;
23 storage_.clear();
24
25 if constexpr (meta::kIsVector<Array>) {
26 /*
27 * Heuristics:
28 * STL impls have a small initial capacity of vector.
29 * It leads to multiple reallocations during inserts.
30 * We intentionally give up some maybe-unused space to lower realloc
31 * count.
32 */
33 storage_.reserve(16);
34 }
35 }
36
37 protected:
38 void StartArray() override {
39 if (state_ == State::kStart) {
40 state_ = State::kInside;
41 } else {
42 PushParser("array");
43 Parser().StartArray();
44 }
45 }
46 void EndArray() override {
47 if (state_ == State::kInside) {
48 this->SetResult(std::move(storage_));
49 return;
50 }
51 // impossible?
52 this->Throw("end of array");
53 }
54
55 void Int64(int64_t i) override {
56 PushParser("integer");
57 Parser().Int64(i);
58 }
59 void Uint64(uint64_t i) override {
60 PushParser("integer");
61 Parser().Uint64(i);
62 }
63 void Null() override {
64 PushParser("null");
65 Parser().Null();
66 }
67 void Bool(bool b) override {
68 PushParser("bool");
69 Parser().Bool(b);
70 }
71 void Double(double d) override {
72 PushParser("double");
73 Parser().Double(d);
74 }
75 void String(std::string_view sw) override {
76 PushParser("string");
77 Parser().String(sw);
78 }
79 void StartObject() override {
80 PushParser("object");
81 Parser().StartObject();
82 }
83
84 std::string Expected() const override { return "array"; }
85
86 void PushParser(std::string_view what) {
87 if (state_ != State::kInside) {
88 // Error path must not include [x] - we're not inside an array yet
89 this->parser_state_->PopMe(*this);
90
91 this->Throw(std::string(what));
92 }
93
94 this->item_parser_.Reset();
95 this->parser_state_->PushParser(item_parser_.GetParser());
96 index_++;
97 }
98
99 void OnSend(Item&& item) override {
100 if constexpr (!meta::kIsVector<Array>) {
101 this->storage_.insert(std::move(item));
102 } else {
103 this->storage_.push_back(std::move(item));
104 }
105 }
106
107 std::string GetPathItem() const override {
108 return common::GetIndexString(index_ - 1);
109 }
110
111 BaseParser& Parser() { return item_parser_.GetParser(); }
112
113 private:
114 ItemParser& item_parser_;
115 std::optional<size_t> min_items_, max_items_;
116
117 enum class State {
118 kStart,
119 kInside,
120 };
121 size_t index_{0};
122 State state_{State::kStart};
123 Array storage_;
124};
125
126} // namespace formats::json::parser
127
128USERVER_NAMESPACE_END