OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be found | |
3 // in the LICENSE file. | |
4 | |
5 #include "webgtt/parser.h" | |
6 | |
7 #include <boost/bind.hpp> | |
8 | |
9 #include <cassert> | |
10 #include <cctype> | |
11 #include <cmath> | |
12 #include <cstdlib> | |
13 #include <cstring> | |
14 #include <string> | |
15 #include <vector> | |
16 | |
17 #include "webgtt/graph.h" | |
18 #include "webgtt/taskmap.h" | |
19 | |
20 /// The sentinel/delimiter string that is used in the message format. | |
21 const char kSentinel[] = "::"; | |
22 | |
23 namespace webgtt { | |
24 | |
25 Parser::Parser(const std::string& message) | |
26 : message_(message), | |
27 is_valid_(false), | |
28 task_ID_(kInvalidValue) {} | |
29 | |
30 /// This function decodes the message. A valid message is of the form: | |
31 /// "kSentinel<adjacency_matrix_>kSentinel<task_ID_>kSentinel<args_>kSentinel" | |
32 /// If at any stage during the decoding process, the message is found to be | |
33 /// invalid (not conforming to the above format), parsing is aborted, and the | |
34 /// is_valid_ bit contains false. Upon successful completion of the decoding | |
35 /// process, the is_valid_ bit is set to true. | |
36 bool Parser::DecodeMessage() { | |
37 assert(!is_valid_); | |
38 | |
39 const size_t message_length = message_.size(); | |
40 // We start parsing from the beginning of the message. | |
41 int parse_position = 0; | |
42 | |
43 // The shortest valid message is for a coloring request on a graph with | |
44 // exactly one vertex, which would look like: | |
45 // "<kSentinel>0<kSentinel>0<kSentinel>" | |
46 if (message_length < 2 + (3 * strlen(kSentinel))) { | |
47 return false; | |
48 } | |
49 // Look for the first sentinel. | |
50 if (message_.substr(0, strlen(kSentinel)).compare(kSentinel) != 0) { | |
51 return false; | |
52 } | |
53 // Skip over the sentinel that we just found, and continue parsing. | |
54 parse_position += strlen(kSentinel); | |
55 | |
56 std::string adjacency_matrix = GetNextChunk(message_, &parse_position); | |
57 if (adjacency_matrix.compare(kSentinel) == 0) { | |
58 return false; | |
59 } | |
60 // adjacency_matrix should be in CSV format; obtain the positions of commas. | |
61 std::vector<int> comma_positions = GetCommaPositions(adjacency_matrix); | |
62 // There should be a total of (number_of_vertices)^2 - 1 commas, since | |
63 // adjacency_matrix is a square matrix. | |
64 int number_of_vertices = sqrt(comma_positions.size() + 1); | |
65 if (static_cast<int>(comma_positions.size()) != | |
66 (number_of_vertices * number_of_vertices) - 1) { | |
67 return false; | |
68 } | |
69 // Decode the adjacency matrix | |
70 int adj_position = 0; | |
71 for (int i = 1; i <= number_of_vertices; ++i) { | |
72 int next_comma_position; | |
73 // There is no comma at the end | |
74 if (i == number_of_vertices) { | |
75 next_comma_position = adjacency_matrix.size(); | |
76 } else { | |
77 next_comma_position = comma_positions[(i * number_of_vertices) - 1]; | |
78 } | |
79 | |
80 std::vector<int> row = DecodeCSV(adjacency_matrix.substr(adj_position, | |
81 next_comma_position - adj_position)); | |
82 for (size_t j = 0; j < row.size(); ++j) { | |
83 if (row[j] == kInvalidValue) { | |
84 return false; | |
85 } | |
86 } | |
87 adjacency_matrix_.push_back(row); | |
88 adj_position = next_comma_position + 1; | |
89 } | |
90 comma_positions.clear(); | |
91 // Construct the graph | |
92 graph::Graph input_graph(number_of_vertices, adjacency_matrix_); | |
93 | |
94 std::string task_ID = GetNextChunk(message_, &parse_position); | |
95 if (task_ID.compare(kSentinel) == 0) { | |
96 return false; | |
97 } | |
98 if (StringToInteger(task_ID) == kInvalidValue) { | |
99 return false; | |
100 } | |
101 task_ID_ = StringToInteger(task_ID); | |
102 | |
103 std::string args = ""; | |
104 if (parse_position != static_cast<int>(message_length)) { | |
105 // There may be some arguments. | |
106 args = GetNextChunk(message_, &parse_position); | |
107 if (args.compare(kSentinel) == 0) { | |
108 return false; | |
109 } | |
110 } | |
111 if (!args.empty()) { | |
112 // args should be in CSV format; decode it. | |
113 args_ = DecodeCSV(args); | |
114 for (size_t i = 0; i < args_.size(); ++i) { | |
115 if (args_[i] == kInvalidValue) { | |
116 return false; | |
117 } | |
118 } | |
119 } | |
120 int number_of_arguments = static_cast<int>(args_.size()); | |
121 // Append enough dummy elements (zeros) to the end of args_, if necessary. | |
122 if (number_of_arguments < kMaxArgs) { | |
123 args_.insert(args_.end(), kMaxArgs - number_of_arguments, 0); | |
124 } | |
125 | |
126 // Obtain the task_map_ lookup. | |
127 TaskMap task_map_object(input_graph, args_); | |
128 task_map_ = task_map_object.task_map(); | |
129 | |
130 // Perform final validation checks. | |
131 if (task_ID_ >= static_cast<int>(task_map_.size())) { | |
132 return false; | |
133 } | |
134 if (number_of_arguments != task_map_[task_ID_].number_of_arguments) { | |
135 return false; | |
136 } | |
137 | |
138 is_valid_ = true; | |
139 return true; | |
140 } | |
141 | |
142 std::string Parser::GetResponse() const { | |
143 assert(is_valid_); | |
144 return task_map_[task_ID_].function_to_call(); | |
145 } | |
146 | |
147 std::vector<int> DecodeCSV(const std::string& message) { | |
148 std::vector<int> answer; | |
149 std::vector<int> comma_positions = GetCommaPositions(message); | |
150 int parse_position = 0; | |
151 for (size_t i = 0; i < comma_positions.size() + 1; ++i) { | |
152 int next_comma_position; | |
153 // There is no comma at the end of the list. | |
154 if (i == comma_positions.size()) { | |
155 next_comma_position = message.size(); | |
156 } else { | |
157 next_comma_position = comma_positions[i]; | |
158 } | |
159 std::string entry = message.substr(parse_position, | |
160 next_comma_position - parse_position); | |
161 answer.push_back(StringToInteger(entry)); | |
162 parse_position = next_comma_position + 1; | |
163 } | |
164 return answer; | |
165 } | |
166 | |
167 std::string GetNextChunk(const std::string& message, int* parse_position) { | |
168 assert(*parse_position < static_cast<int>(message.size())); | |
169 unsigned int next_sentinel_position = message.find(kSentinel, | |
170 *parse_position); | |
171 if (next_sentinel_position == std::string::npos) { | |
172 std::string ret_val(kSentinel); | |
173 return ret_val; // Return the sentinel string itself indicating error. | |
174 } | |
175 std::string next_chunk = message.substr(*parse_position, | |
176 next_sentinel_position - *parse_position); | |
177 // Update the position to continue parsing from (skip over the sentinel). | |
178 *parse_position = next_sentinel_position + strlen(kSentinel); | |
179 return next_chunk; | |
180 } | |
181 | |
182 std::vector<int> GetCommaPositions(const std::string& message) { | |
183 std::vector<int> comma_positions; | |
184 for (std::string temp = message; temp.find(',') != std::string::npos; | |
185 temp.replace(temp.find(','), 1, 1, '.')) { | |
186 comma_positions.push_back(temp.find(',')); | |
187 } | |
188 return comma_positions; | |
189 } | |
190 | |
191 int StringToInteger(const std::string& message) { | |
192 if (message.empty()) { | |
193 return kInvalidValue; | |
194 } | |
195 if (!isdigit(message[0])) { | |
196 return kInvalidValue; | |
197 } | |
198 return atoi(message.c_str()); | |
199 } | |
200 | |
201 } // namespace webgtt | |
OLD | NEW |