blob: 77a56e55045b62fbffa466d0a206830db40f1167 [file] [log] [blame]
Yi Jinb44f7d42017-07-21 12:12:59 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "incident_helper"
18
19#include "ih_util.h"
20
Yi Jin810b14f2017-09-11 19:01:08 -070021#include <algorithm>
Yi Jinb44f7d42017-07-21 12:12:59 -070022#include <sstream>
23#include <unistd.h>
24
Yi Jine2f7f792017-11-01 17:08:27 -070025bool isValidChar(char c) {
26 uint8_t v = (uint8_t)c;
27 return (v >= (uint8_t)'a' && v <= (uint8_t)'z')
28 || (v >= (uint8_t)'A' && v <= (uint8_t)'Z')
29 || (v >= (uint8_t)'0' && v <= (uint8_t)'9')
30 || (v == (uint8_t)'_');
31}
Yi Jinb44f7d42017-07-21 12:12:59 -070032
Yi Jin0dfa7522017-11-06 17:43:47 -080033std::string trim(const std::string& s, const std::string& charset) {
34 const auto head = s.find_first_not_of(charset);
Yi Jinb44f7d42017-07-21 12:12:59 -070035 if (head == std::string::npos) return "";
36
Yi Jin0dfa7522017-11-06 17:43:47 -080037 const auto tail = s.find_last_not_of(charset);
Yi Jinb44f7d42017-07-21 12:12:59 -070038 return s.substr(head, tail - head + 1);
39}
40
Yi Jin0dfa7522017-11-06 17:43:47 -080041static inline std::string toLowerStr(const std::string& s) {
42 std::string res(s);
43 std::transform(res.begin(), res.end(), res.begin(), ::tolower);
44 return res;
45}
46
47static inline std::string trimDefault(const std::string& s) {
Yi Jine2f7f792017-11-01 17:08:27 -070048 return trim(s, DEFAULT_WHITESPACE);
49}
50
Yi Jin0dfa7522017-11-06 17:43:47 -080051static inline std::string trimHeader(const std::string& s) {
52 return toLowerStr(trimDefault(s));
Yi Jin4ef28b72017-08-14 14:45:28 -070053}
54
Kweku Adamsf5cc5752017-12-20 17:59:17 -080055static inline bool isNumber(const std::string& s) {
56 std::string::const_iterator it = s.begin();
57 while (it != s.end() && std::isdigit(*it)) ++it;
58 return !s.empty() && it == s.end();
59}
60
Yi Jinb44f7d42017-07-21 12:12:59 -070061// This is similiar to Split in android-base/file.h, but it won't add empty string
Yi Jin4ef28b72017-08-14 14:45:28 -070062static void split(const std::string& line, std::vector<std::string>& words,
63 const trans_func& func, const std::string& delimiters) {
Yi Jinb44f7d42017-07-21 12:12:59 -070064 words.clear(); // clear the buffer before split
65
66 size_t base = 0;
67 size_t found;
68 while (true) {
69 found = line.find_first_of(delimiters, base);
70 if (found != base) {
Yi Jin4ef28b72017-08-14 14:45:28 -070071 std::string word = (*func) (line.substr(base, found - base));
Yi Jinb44f7d42017-07-21 12:12:59 -070072 if (!word.empty()) {
73 words.push_back(word);
74 }
75 }
76 if (found == line.npos) break;
77 base = found + 1;
78 }
79}
80
Yi Jin4ef28b72017-08-14 14:45:28 -070081header_t parseHeader(const std::string& line, const std::string& delimiters) {
82 header_t header;
83 trans_func f = &trimHeader;
84 split(line, header, f, delimiters);
85 return header;
86}
87
88record_t parseRecord(const std::string& line, const std::string& delimiters) {
89 record_t record;
Yi Jine2f7f792017-11-01 17:08:27 -070090 trans_func f = &trimDefault;
Yi Jin4ef28b72017-08-14 14:45:28 -070091 split(line, record, f, delimiters);
92 return record;
Yi Jinb44f7d42017-07-21 12:12:59 -070093}
94
Kweku Adamsf5cc5752017-12-20 17:59:17 -080095bool getColumnIndices(std::vector<int>& indices, const char** headerNames, const std::string& line) {
96 indices.clear();
97
98 size_t lastIndex = 0;
99 int i = 0;
Yi Kong08a8d722018-08-06 14:48:58 -0700100 while (headerNames[i] != nullptr) {
Yi Jin6cacbcb2018-03-30 14:04:52 -0700101 std::string s = headerNames[i];
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800102 lastIndex = line.find(s, lastIndex);
Yi Jin6cacbcb2018-03-30 14:04:52 -0700103 if (lastIndex == std::string::npos) {
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800104 fprintf(stderr, "Bad Task Header: %s\n", line.c_str());
105 return false;
106 }
107 lastIndex += s.length();
108 indices.push_back(lastIndex);
109 i++;
110 }
111
112 return true;
113}
114
Yi Jine2f7f792017-11-01 17:08:27 -0700115record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
116 record_t record;
117 int lastIndex = 0;
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800118 int lastBeginning = 0;
Yi Jine2f7f792017-11-01 17:08:27 -0700119 int lineSize = (int)line.size();
120 for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
121 int idx = *it;
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800122 if (idx <= lastIndex) {
123 // We saved up until lastIndex last time, so we should start at
124 // lastIndex + 1 this time.
125 idx = lastIndex + 1;
126 }
127 if (idx > lineSize) {
128 if (lastIndex < idx && lastIndex < lineSize) {
129 // There's a little bit more for us to save, which we'll do
130 // outside of the loop.
131 break;
132 }
133 // If we're past the end of the line AND we've already saved everything up to the end.
134 fprintf(stderr, "index wrong: lastIndex: %d, idx: %d, lineSize: %d\n", lastIndex, idx, lineSize);
135 record.clear(); // The indices are wrong, return empty.
Yi Jine2f7f792017-11-01 17:08:27 -0700136 return record;
137 }
138 while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
139 record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800140 lastBeginning = lastIndex;
Yi Jine2f7f792017-11-01 17:08:27 -0700141 lastIndex = idx;
142 }
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800143 if (lineSize - lastIndex > 0) {
144 int beginning = lastIndex;
Yao Chenb5908ae2019-03-01 10:04:47 -0800145 if (record.size() == indices.size() && !record.empty()) {
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800146 // We've already encountered all of the columns...put whatever is
147 // left in the last column.
148 record.pop_back();
149 beginning = lastBeginning;
150 }
151 record.push_back(trimDefault(line.substr(beginning, lineSize - beginning)));
152 }
Yi Jine2f7f792017-11-01 17:08:27 -0700153 return record;
154}
155
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800156void printRecord(const record_t& record) {
157 fprintf(stderr, "Record: { ");
158 if (record.size() == 0) {
159 fprintf(stderr, "}\n");
160 return;
161 }
162 for(size_t i = 0; i < record.size(); ++i) {
163 if(i != 0) fprintf(stderr, "\", ");
164 fprintf(stderr, "\"%s", record[i].c_str());
165 }
166 fprintf(stderr, "\" }\n");
167}
168
Yi Jine2f7f792017-11-01 17:08:27 -0700169bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
Yi Jin810b14f2017-09-11 19:01:08 -0700170 const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
171 if (head == std::string::npos) return false;
Yi Jine2f7f792017-11-01 17:08:27 -0700172 int len = (int)line->length();
173 int i = 0;
174 int j = head;
Yi Jin810b14f2017-09-11 19:01:08 -0700175 while (key[i] != '\0') {
Yi Jine2f7f792017-11-01 17:08:27 -0700176 if (j >= len || key[i++] != line->at(j++)) {
Yi Jin810b14f2017-09-11 19:01:08 -0700177 return false;
178 }
179 }
Yi Jine2f7f792017-11-01 17:08:27 -0700180
181 if (endAtDelimiter) {
182 // this means if the line only have prefix or no delimiter, we still return false.
183 if (j == len || isValidChar(line->at(j))) return false;
184 }
185
186 line->assign(trimDefault(line->substr(j)));
187 return true;
188}
189
190bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
191 const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
192 if (tail == std::string::npos) return false;
193 int i = 0;
194 while (key[++i] != '\0'); // compute the size of the key
195 int j = tail;
196 while (i > 0) {
197 if (j < 0 || key[--i] != line->at(j--)) {
198 return false;
199 }
200 }
201
202 if (endAtDelimiter) {
203 // this means if the line only have suffix or no delimiter, we still return false.
204 if (j < 0 || isValidChar(line->at(j))) return false;
205 }
206
207 line->assign(trimDefault(line->substr(0, j+1)));
Yi Jin810b14f2017-09-11 19:01:08 -0700208 return true;
209}
210
Yi Jin3c034c92017-12-22 17:36:47 -0800211std::string behead(std::string* line, const char cut) {
212 auto found = line->find_first_of(cut);
213 if (found == std::string::npos) {
214 std::string head = line->substr(0);
215 line->assign("");
216 return head;
217 }
218 std::string head = line->substr(0, found);
219 while(line->at(found) == cut) found++; // trim more cut of the rest
220 line->assign(line->substr(found));
221 return head;
222}
223
Yi Jin04625ad2017-10-17 18:29:33 -0700224int toInt(const std::string& s) {
225 return atoi(s.c_str());
226}
227
228long long toLongLong(const std::string& s) {
229 return atoll(s.c_str());
230}
231
Yi Jine2f7f792017-11-01 17:08:27 -0700232double toDouble(const std::string& s) {
233 return atof(s.c_str());
234}
Yi Jinb44f7d42017-07-21 12:12:59 -0700235
Yi Jine2f7f792017-11-01 17:08:27 -0700236// ==============================================================================
237Reader::Reader(const int fd)
Yi Jinb44f7d42017-07-21 12:12:59 -0700238{
Yi Jine2f7f792017-11-01 17:08:27 -0700239 mFile = fdopen(fd, "r");
Yi Kong08a8d722018-08-06 14:48:58 -0700240 mStatus = mFile == nullptr ? "Invalid fd " + std::to_string(fd) : "";
Yi Jinb44f7d42017-07-21 12:12:59 -0700241}
242
243Reader::~Reader()
244{
Yi Kong08a8d722018-08-06 14:48:58 -0700245 if (mFile != nullptr) fclose(mFile);
Yi Jinb44f7d42017-07-21 12:12:59 -0700246}
247
Yi Jine2f7f792017-11-01 17:08:27 -0700248bool Reader::readLine(std::string* line) {
Yi Kong08a8d722018-08-06 14:48:58 -0700249 if (mFile == nullptr) return false;
Yi Jinb44f7d42017-07-21 12:12:59 -0700250
Yi Kong08a8d722018-08-06 14:48:58 -0700251 char* buf = nullptr;
Yi Jine2f7f792017-11-01 17:08:27 -0700252 size_t len = 0;
253 ssize_t read = getline(&buf, &len, mFile);
254 if (read != -1) {
255 std::string s(buf);
256 line->assign(trim(s, DEFAULT_NEWLINE));
257 } else if (errno == EINVAL) {
258 mStatus = "Bad Argument";
Yi Jinb44f7d42017-07-21 12:12:59 -0700259 }
Yi Jine2f7f792017-11-01 17:08:27 -0700260 free(buf);
261 return read != -1;
Yi Jinb44f7d42017-07-21 12:12:59 -0700262}
263
Yi Jin810b14f2017-09-11 19:01:08 -0700264bool Reader::ok(std::string* error) {
265 error->assign(mStatus);
Yi Jinb44f7d42017-07-21 12:12:59 -0700266 return mStatus.empty();
267}
Yi Jin04625ad2017-10-17 18:29:33 -0700268
269// ==============================================================================
270Table::Table(const char* names[], const uint64_t ids[], const int count)
Yi Jin0dfa7522017-11-06 17:43:47 -0800271 :mEnums(),
272 mEnumValuesByName()
Yi Jin04625ad2017-10-17 18:29:33 -0700273{
Yi Jin6cacbcb2018-03-30 14:04:52 -0700274 std::map<std::string, uint64_t> fields;
Yi Jin0dfa7522017-11-06 17:43:47 -0800275 for (int i = 0; i < count; i++) {
276 fields[names[i]] = ids[i];
277 }
278 mFields = fields;
Yi Jin04625ad2017-10-17 18:29:33 -0700279}
280
281Table::~Table()
282{
283}
284
Yi Jine2f7f792017-11-01 17:08:27 -0700285void
Yi Jin0dfa7522017-11-06 17:43:47 -0800286Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
Yi Jin04625ad2017-10-17 18:29:33 -0700287{
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800288 if (mFields.find(field) == mFields.end()) {
Yi Jin6cacbcb2018-03-30 14:04:52 -0700289 fprintf(stderr, "Field '%s' not found", field);
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800290 return;
291 }
Yi Jin04625ad2017-10-17 18:29:33 -0700292
Yi Jin6cacbcb2018-03-30 14:04:52 -0700293 std::map<std::string, int> enu;
Yi Jin0dfa7522017-11-06 17:43:47 -0800294 for (int i = 0; i < enumSize; i++) {
295 enu[enumNames[i]] = enumValues[i];
296 }
297 mEnums[field] = enu;
298}
299
300void
301Table::addEnumNameToValue(const char* enumName, const int enumValue)
302{
303 mEnumValuesByName[enumName] = enumValue;
Yi Jine2f7f792017-11-01 17:08:27 -0700304}
305
306bool
307Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
308{
Yi Jin0dfa7522017-11-06 17:43:47 -0800309 if (mFields.find(name) == mFields.end()) return false;
Yi Jine2f7f792017-11-01 17:08:27 -0700310
Yi Jin0dfa7522017-11-06 17:43:47 -0800311 uint64_t found = mFields[name];
312 record_t repeats; // used for repeated fields
313 switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
314 case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
315 case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
Yi Jine2f7f792017-11-01 17:08:27 -0700316 proto->write(found, toDouble(value));
317 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800318 case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
319 case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
Yi Jine2f7f792017-11-01 17:08:27 -0700320 proto->write(found, value);
Yi Jin04625ad2017-10-17 18:29:33 -0700321 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800322 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
323 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
324 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
325 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
326 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
Yi Jine2f7f792017-11-01 17:08:27 -0700327 proto->write(found, toLongLong(value));
Yi Jin04625ad2017-10-17 18:29:33 -0700328 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800329 case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
330 if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
331 proto->write(found, true);
332 break;
333 }
334 if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
335 proto->write(found, false);
336 break;
337 }
Yi Jine2f7f792017-11-01 17:08:27 -0700338 return false;
Yi Jin0dfa7522017-11-06 17:43:47 -0800339 case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
340 // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
341 if (mEnums.find(name) != mEnums.end()) {
342 if (mEnums[name].find(value) != mEnums[name].end()) {
343 proto->write(found, mEnums[name][value]);
344 } else {
345 proto->write(found, 0); // TODO: should get the default enum value (Unknown)
346 }
347 } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
348 proto->write(found, mEnumValuesByName[value]);
Kweku Adamsf5cc5752017-12-20 17:59:17 -0800349 } else if (isNumber(value)) {
350 proto->write(found, toInt(value));
Yi Jin0dfa7522017-11-06 17:43:47 -0800351 } else {
Yi Jine2f7f792017-11-01 17:08:27 -0700352 return false;
353 }
Yi Jine2f7f792017-11-01 17:08:27 -0700354 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800355 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
356 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
357 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
358 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
359 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
Yi Jine2f7f792017-11-01 17:08:27 -0700360 proto->write(found, toInt(value));
Yi Jin04625ad2017-10-17 18:29:33 -0700361 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800362 // REPEATED TYPE below:
363 case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
364 repeats = parseRecord(value, COMMA_DELIMITER);
365 for (size_t i=0; i<repeats.size(); i++) {
366 proto->write(found, toInt(repeats[i]));
367 }
368 break;
369 case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
370 repeats = parseRecord(value, COMMA_DELIMITER);
371 for (size_t i=0; i<repeats.size(); i++) {
372 proto->write(found, repeats[i]);
373 }
374 break;
Yi Jin04625ad2017-10-17 18:29:33 -0700375 default:
376 return false;
377 }
378 return true;
379}
Yi Jin9299af932017-12-05 17:44:48 -0800380
381// ================================================================================
382Message::Message(Table* table)
383 :mTable(table),
384 mPreviousField(""),
385 mTokens(),
386 mSubMessages()
387{
388}
389
390Message::~Message()
391{
392}
393
394void
395Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
396{
397 for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
398 if (iter->second == fieldId) {
399 mSubMessages[iter->first] = fieldMsg;
400 return;
401 }
402 }
403}
404
405bool
406Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
407{
408 // If the field name can be found, it means the name is a primitive field.
409 if (mTable->mFields.find(name) != mTable->mFields.end()) {
410 endSession(proto);
411 // The only edge case is for example ro.hardware itself is a message, so a field called "value"
412 // would be defined in proto Ro::Hardware and it must be the first field.
413 if (mSubMessages.find(name) != mSubMessages.end()) {
414 startSession(proto, name);
415 return mSubMessages[name]->insertField(proto, "value", value);
416 } else {
417 return mTable->insertField(proto, name, value);
418 }
419 }
420
421 // Try to find the message field which is the prefix of name, so the value would be inserted
422 // recursively into the submessage.
Yi Jin6cacbcb2018-03-30 14:04:52 -0700423 std::string mutableName = name;
Yi Jin9299af932017-12-05 17:44:48 -0800424 for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
Yi Jin6cacbcb2018-03-30 14:04:52 -0700425 std::string fieldName = iter->first;
426 std::string prefix = fieldName + "_"; // underscore is the delimiter in the name
Yi Jin9299af932017-12-05 17:44:48 -0800427 if (stripPrefix(&mutableName, prefix.c_str())) {
428 if (mPreviousField != fieldName) {
429 endSession(proto);
430 startSession(proto, fieldName);
431 }
432 return mSubMessages[fieldName]->insertField(proto, mutableName, value);
433 }
434 }
435 // Can't find the name in proto definition, handle it separately.
436 return false;
437}
438
439void
Yi Jin6cacbcb2018-03-30 14:04:52 -0700440Message::startSession(ProtoOutputStream* proto, const std::string& name)
Yi Jin9299af932017-12-05 17:44:48 -0800441{
442 uint64_t fieldId = mTable->mFields[name];
Yi Jin5ee07872018-03-05 18:18:27 -0800443 uint64_t token = proto->start(fieldId);
Yi Jin9299af932017-12-05 17:44:48 -0800444 mPreviousField = name;
445 mTokens.push(token);
446}
447
448void
449Message::endSession(ProtoOutputStream* proto)
450{
451 if (mPreviousField == "") return;
452 if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
453 mSubMessages[mPreviousField]->endSession(proto);
454 }
455 proto->end(mTokens.top());
456 mTokens.pop();
457 mPreviousField = "";
458}