1 #include "image_io/xml/xml_rule.h"
2
3 #include <string>
4 #include <utility>
5
6 #include "image_io/base/data_scanner.h"
7
8 namespace photos_editing_formats {
9 namespace image_io {
10
11 using std::string;
12 using std::unique_ptr;
13
14 namespace {
15
16 /// A scanner is reentrant if it ran out of data. In these cases, the next data
17 /// segment sent into the rule for parsing may be non-contiguous with the
18 /// previous one. If that is the case, update the scanner's token length to
19 /// account for the missing bytes. (Scanner token ranges represent a bounding
20 /// box around the token value - in these cases the actual token value is really
21 /// a vector of ranges. Client handlers are responsible for dealing with that
22 /// reality, not the scanner or rule).
23 /// @param scanner The current possibly reentrant scanner.
24 /// @param context_range The new data range that is to be parsed.
MaybeUpdateTokenLengthForReentrantScanner(DataScanner * scanner,const DataRange & context_range)25 void MaybeUpdateTokenLengthForReentrantScanner(DataScanner* scanner,
26 const DataRange& context_range) {
27 const auto& token_range = scanner->GetTokenRange();
28 if (scanner->GetScanCallCount() > 0 && token_range.IsValid() &&
29 context_range.GetBegin() > token_range.GetEnd()) {
30 size_t skipped_byte_count = context_range.GetBegin() - token_range.GetEnd();
31 scanner->ExtendTokenLength(skipped_byte_count);
32 }
33 }
34
35 } // namespace
36
XmlRule(const std::string & name)37 XmlRule::XmlRule(const std::string& name) : name_(name), terminal_index_(0) {}
38
AddLiteralTerminal(const std::string & literal)39 XmlTerminal& XmlRule::AddLiteralTerminal(const std::string& literal) {
40 terminals_.emplace_back(DataScanner::CreateLiteralScanner(literal));
41 return terminals_.back();
42 }
43
AddNameTerminal()44 XmlTerminal& XmlRule::AddNameTerminal() {
45 terminals_.emplace_back(DataScanner::CreateNameScanner());
46 return terminals_.back();
47 }
48
AddQuotedStringTerminal()49 XmlTerminal& XmlRule::AddQuotedStringTerminal() {
50 terminals_.emplace_back(DataScanner::CreateQuotedStringScanner());
51 return terminals_.back();
52 }
53
AddSentinelTerminal(const std::string & sentinels)54 XmlTerminal& XmlRule::AddSentinelTerminal(const std::string& sentinels) {
55 terminals_.emplace_back(DataScanner::CreateSentinelScanner(sentinels));
56 return terminals_.back();
57 }
58
AddThroughLiteralTerminal(const std::string & literal)59 XmlTerminal& XmlRule::AddThroughLiteralTerminal(const std::string& literal) {
60 terminals_.emplace_back(DataScanner::CreateThroughLiteralScanner(literal));
61 return terminals_.back();
62 }
63
AddWhitespaceTerminal()64 XmlTerminal& XmlRule::AddWhitespaceTerminal() {
65 terminals_.emplace_back(DataScanner::CreateWhitespaceScanner());
66 return terminals_.back();
67 }
68
AddOptionalWhitespaceTerminal()69 XmlTerminal& XmlRule::AddOptionalWhitespaceTerminal() {
70 terminals_.emplace_back(DataScanner::CreateOptionalWhitespaceScanner());
71 return terminals_.back();
72 }
73
GetTerminalIndexFromName(const std::string name) const74 size_t XmlRule::GetTerminalIndexFromName(const std::string name) const {
75 if (!name.empty()) {
76 for (size_t index = 0; index < terminals_.size(); ++index) {
77 if (terminals_[index].GetName() == name) {
78 return index;
79 }
80 }
81 }
82 return terminals_.size();
83 }
84
SetTerminalIndex(size_t terminal_index)85 void XmlRule::SetTerminalIndex(size_t terminal_index) {
86 terminal_index_ = terminal_index;
87 }
88
GetCurrentTerminal()89 XmlTerminal* XmlRule::GetCurrentTerminal() {
90 return terminal_index_ < terminals_.size() ? &terminals_[terminal_index_]
91 : nullptr;
92 }
93
GetTerminal(size_t index)94 XmlTerminal* XmlRule::GetTerminal(size_t index) {
95 return index < terminals_.size() ? &terminals_[index] : nullptr;
96 }
97
ResetTerminalScanners()98 void XmlRule::ResetTerminalScanners() {
99 for (auto& terminal : terminals_) {
100 terminal.GetScanner()->Reset();
101 }
102 }
103
IsPermissibleToFinish(std::string *) const104 bool XmlRule::IsPermissibleToFinish(std::string*) const {
105 return false;
106 }
107
Parse(XmlHandlerContext context)108 DataMatchResult XmlRule::Parse(XmlHandlerContext context) {
109 DataMatchResult result;
110 if (!context.IsValidLocationAndRange()) {
111 result.SetType(DataMatchResult::kError);
112 result.SetMessage(Message::kInternalError,
113 context.GetInvalidLocationAndRangeErrorText());
114 return result;
115 }
116 bool force_parse_return = false;
117 size_t bytes_available = context.GetBytesAvailable();
118 size_t current_terminal_index = GetTerminalIndex();
119 if (current_terminal_index < terminals_.size()) {
120 MaybeUpdateTokenLengthForReentrantScanner(
121 terminals_[current_terminal_index].GetScanner(), context.GetRange());
122 }
123 while (!force_parse_return && current_terminal_index < terminals_.size() &&
124 bytes_available > 0) {
125 SetTerminalIndex(current_terminal_index);
126 auto& terminal = terminals_[current_terminal_index];
127 DataMatchResult scanner_result = terminal.GetScanner()->Scan(context);
128 if (terminal.GetAction() &&
129 (scanner_result.GetType() == DataMatchResult::kFull ||
130 scanner_result.GetType() == DataMatchResult::kPartialOutOfData)) {
131 XmlActionContext action_context(context, &terminal, scanner_result);
132 scanner_result = terminal.GetAction()(action_context);
133 }
134 result.SetType(scanner_result.GetType());
135 result.IncrementBytesConsumed(scanner_result.GetBytesConsumed());
136 context.IncrementLocation(scanner_result.GetBytesConsumed());
137 bytes_available -= scanner_result.GetBytesConsumed();
138 switch (scanner_result.GetType()) {
139 case DataMatchResult::kError:
140 result.SetMessage(scanner_result.GetMessage());
141 force_parse_return = true;
142 break;
143 case DataMatchResult::kNone:
144 result.SetType(DataMatchResult::kError);
145 result.SetMessage(
146 Message::kInternalError,
147 context.GetErrorText("Invalid scanner match result",
148 terminal.GetScanner()->GetDescription()));
149 force_parse_return = true;
150 break;
151 case DataMatchResult::kPartial:
152 case DataMatchResult::kPartialOutOfData:
153 if (scanner_result.HasMessage()) {
154 result.SetMessage(scanner_result.GetMessage());
155 }
156 force_parse_return = true;
157 break;
158 case DataMatchResult::kFull:
159 if (scanner_result.HasMessage() && !result.HasMessage()) {
160 result.SetMessage(scanner_result.GetMessage());
161 }
162 current_terminal_index = current_terminal_index == GetTerminalIndex()
163 ? current_terminal_index + 1
164 : GetTerminalIndex();
165 SetTerminalIndex(current_terminal_index);
166 if (current_terminal_index < GetTerminalCount()) {
167 result.SetType(DataMatchResult::kPartial);
168 }
169 force_parse_return = HasNextRule();
170 break;
171 }
172 }
173 return result;
174 }
175
HasNextRule() const176 bool XmlRule::HasNextRule() const { return next_rule_ != nullptr; }
177
ReleaseNextRule()178 std::unique_ptr<XmlRule> XmlRule::ReleaseNextRule() {
179 return std::move(next_rule_);
180 }
181
SetNextRule(std::unique_ptr<XmlRule> next_rule)182 void XmlRule::SetNextRule(std::unique_ptr<XmlRule> next_rule) {
183 next_rule_ = std::move(next_rule);
184 }
185
186 } // namespace image_io
187 } // namespace photos_editing_formats
188