blob: 4bf956a9a03d7edac58757c53e12fa9f51edc4b3 [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
Yi Jinb44f7d42017-07-21 12:12:59 -070055// This is similiar to Split in android-base/file.h, but it won't add empty string
Yi Jin4ef28b72017-08-14 14:45:28 -070056static void split(const std::string& line, std::vector<std::string>& words,
57 const trans_func& func, const std::string& delimiters) {
Yi Jinb44f7d42017-07-21 12:12:59 -070058 words.clear(); // clear the buffer before split
59
60 size_t base = 0;
61 size_t found;
62 while (true) {
63 found = line.find_first_of(delimiters, base);
64 if (found != base) {
Yi Jin4ef28b72017-08-14 14:45:28 -070065 std::string word = (*func) (line.substr(base, found - base));
Yi Jinb44f7d42017-07-21 12:12:59 -070066 if (!word.empty()) {
67 words.push_back(word);
68 }
69 }
70 if (found == line.npos) break;
71 base = found + 1;
72 }
73}
74
Yi Jin4ef28b72017-08-14 14:45:28 -070075header_t parseHeader(const std::string& line, const std::string& delimiters) {
76 header_t header;
77 trans_func f = &trimHeader;
78 split(line, header, f, delimiters);
79 return header;
80}
81
82record_t parseRecord(const std::string& line, const std::string& delimiters) {
83 record_t record;
Yi Jine2f7f792017-11-01 17:08:27 -070084 trans_func f = &trimDefault;
Yi Jin4ef28b72017-08-14 14:45:28 -070085 split(line, record, f, delimiters);
86 return record;
Yi Jinb44f7d42017-07-21 12:12:59 -070087}
88
Yi Jine2f7f792017-11-01 17:08:27 -070089record_t parseRecordByColumns(const std::string& line, const std::vector<int>& indices, const std::string& delimiters) {
90 record_t record;
91 int lastIndex = 0;
92 int lineSize = (int)line.size();
93 for (std::vector<int>::const_iterator it = indices.begin(); it != indices.end(); ++it) {
94 int idx = *it;
95 if (lastIndex > idx || idx > lineSize) {
96 record.clear(); // The indices is wrong, return empty;
97 return record;
98 }
99 while (idx < lineSize && delimiters.find(line[idx++]) == std::string::npos);
100 record.push_back(trimDefault(line.substr(lastIndex, idx - lastIndex)));
101 lastIndex = idx;
102 }
103 record.push_back(trimDefault(line.substr(lastIndex, lineSize - lastIndex)));
104 return record;
105}
106
107bool stripPrefix(std::string* line, const char* key, bool endAtDelimiter) {
Yi Jin810b14f2017-09-11 19:01:08 -0700108 const auto head = line->find_first_not_of(DEFAULT_WHITESPACE);
109 if (head == std::string::npos) return false;
Yi Jine2f7f792017-11-01 17:08:27 -0700110 int len = (int)line->length();
111 int i = 0;
112 int j = head;
Yi Jin810b14f2017-09-11 19:01:08 -0700113 while (key[i] != '\0') {
Yi Jine2f7f792017-11-01 17:08:27 -0700114 if (j >= len || key[i++] != line->at(j++)) {
Yi Jin810b14f2017-09-11 19:01:08 -0700115 return false;
116 }
117 }
Yi Jine2f7f792017-11-01 17:08:27 -0700118
119 if (endAtDelimiter) {
120 // this means if the line only have prefix or no delimiter, we still return false.
121 if (j == len || isValidChar(line->at(j))) return false;
122 }
123
124 line->assign(trimDefault(line->substr(j)));
125 return true;
126}
127
128bool stripSuffix(std::string* line, const char* key, bool endAtDelimiter) {
129 const auto tail = line->find_last_not_of(DEFAULT_WHITESPACE);
130 if (tail == std::string::npos) return false;
131 int i = 0;
132 while (key[++i] != '\0'); // compute the size of the key
133 int j = tail;
134 while (i > 0) {
135 if (j < 0 || key[--i] != line->at(j--)) {
136 return false;
137 }
138 }
139
140 if (endAtDelimiter) {
141 // this means if the line only have suffix or no delimiter, we still return false.
142 if (j < 0 || isValidChar(line->at(j))) return false;
143 }
144
145 line->assign(trimDefault(line->substr(0, j+1)));
Yi Jin810b14f2017-09-11 19:01:08 -0700146 return true;
147}
148
Yi Jin04625ad2017-10-17 18:29:33 -0700149int toInt(const std::string& s) {
150 return atoi(s.c_str());
151}
152
153long long toLongLong(const std::string& s) {
154 return atoll(s.c_str());
155}
156
Yi Jine2f7f792017-11-01 17:08:27 -0700157double toDouble(const std::string& s) {
158 return atof(s.c_str());
159}
Yi Jinb44f7d42017-07-21 12:12:59 -0700160
Yi Jine2f7f792017-11-01 17:08:27 -0700161// ==============================================================================
162Reader::Reader(const int fd)
Yi Jinb44f7d42017-07-21 12:12:59 -0700163{
Yi Jine2f7f792017-11-01 17:08:27 -0700164 mFile = fdopen(fd, "r");
165 mStatus = mFile == NULL ? "Invalid fd " + std::to_string(fd) : "";
Yi Jinb44f7d42017-07-21 12:12:59 -0700166}
167
168Reader::~Reader()
169{
Yi Jine2f7f792017-11-01 17:08:27 -0700170 if (mFile != NULL) fclose(mFile);
Yi Jinb44f7d42017-07-21 12:12:59 -0700171}
172
Yi Jine2f7f792017-11-01 17:08:27 -0700173bool Reader::readLine(std::string* line) {
174 if (mFile == NULL) return false;
Yi Jinb44f7d42017-07-21 12:12:59 -0700175
Yi Jine2f7f792017-11-01 17:08:27 -0700176 char* buf = NULL;
177 size_t len = 0;
178 ssize_t read = getline(&buf, &len, mFile);
179 if (read != -1) {
180 std::string s(buf);
181 line->assign(trim(s, DEFAULT_NEWLINE));
182 } else if (errno == EINVAL) {
183 mStatus = "Bad Argument";
Yi Jinb44f7d42017-07-21 12:12:59 -0700184 }
Yi Jine2f7f792017-11-01 17:08:27 -0700185 free(buf);
186 return read != -1;
Yi Jinb44f7d42017-07-21 12:12:59 -0700187}
188
Yi Jin810b14f2017-09-11 19:01:08 -0700189bool Reader::ok(std::string* error) {
190 error->assign(mStatus);
Yi Jinb44f7d42017-07-21 12:12:59 -0700191 return mStatus.empty();
192}
Yi Jin04625ad2017-10-17 18:29:33 -0700193
194// ==============================================================================
195Table::Table(const char* names[], const uint64_t ids[], const int count)
Yi Jin0dfa7522017-11-06 17:43:47 -0800196 :mEnums(),
197 mEnumValuesByName()
Yi Jin04625ad2017-10-17 18:29:33 -0700198{
Yi Jin0dfa7522017-11-06 17:43:47 -0800199 map<std::string, uint64_t> fields;
200 for (int i = 0; i < count; i++) {
201 fields[names[i]] = ids[i];
202 }
203 mFields = fields;
Yi Jin04625ad2017-10-17 18:29:33 -0700204}
205
206Table::~Table()
207{
208}
209
Yi Jine2f7f792017-11-01 17:08:27 -0700210void
Yi Jin0dfa7522017-11-06 17:43:47 -0800211Table::addEnumTypeMap(const char* field, const char* enumNames[], const int enumValues[], const int enumSize)
Yi Jin04625ad2017-10-17 18:29:33 -0700212{
Yi Jin0dfa7522017-11-06 17:43:47 -0800213 if (mFields.find(field) == mFields.end()) return;
Yi Jin04625ad2017-10-17 18:29:33 -0700214
Yi Jin0dfa7522017-11-06 17:43:47 -0800215 map<std::string, int> enu;
216 for (int i = 0; i < enumSize; i++) {
217 enu[enumNames[i]] = enumValues[i];
218 }
219 mEnums[field] = enu;
220}
221
222void
223Table::addEnumNameToValue(const char* enumName, const int enumValue)
224{
225 mEnumValuesByName[enumName] = enumValue;
Yi Jine2f7f792017-11-01 17:08:27 -0700226}
227
228bool
229Table::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
230{
Yi Jin0dfa7522017-11-06 17:43:47 -0800231 if (mFields.find(name) == mFields.end()) return false;
Yi Jine2f7f792017-11-01 17:08:27 -0700232
Yi Jin0dfa7522017-11-06 17:43:47 -0800233 uint64_t found = mFields[name];
234 record_t repeats; // used for repeated fields
235 switch ((found & FIELD_COUNT_MASK) | (found & FIELD_TYPE_MASK)) {
236 case FIELD_COUNT_SINGLE | FIELD_TYPE_DOUBLE:
237 case FIELD_COUNT_SINGLE | FIELD_TYPE_FLOAT:
Yi Jine2f7f792017-11-01 17:08:27 -0700238 proto->write(found, toDouble(value));
239 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800240 case FIELD_COUNT_SINGLE | FIELD_TYPE_STRING:
241 case FIELD_COUNT_SINGLE | FIELD_TYPE_BYTES:
Yi Jine2f7f792017-11-01 17:08:27 -0700242 proto->write(found, value);
Yi Jin04625ad2017-10-17 18:29:33 -0700243 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800244 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT64:
245 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT64:
246 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT64:
247 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED64:
248 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED64:
Yi Jine2f7f792017-11-01 17:08:27 -0700249 proto->write(found, toLongLong(value));
Yi Jin04625ad2017-10-17 18:29:33 -0700250 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800251 case FIELD_COUNT_SINGLE | FIELD_TYPE_BOOL:
252 if (strcmp(toLowerStr(value).c_str(), "true") == 0 || strcmp(value.c_str(), "1") == 0) {
253 proto->write(found, true);
254 break;
255 }
256 if (strcmp(toLowerStr(value).c_str(), "false") == 0 || strcmp(value.c_str(), "0") == 0) {
257 proto->write(found, false);
258 break;
259 }
Yi Jine2f7f792017-11-01 17:08:27 -0700260 return false;
Yi Jin0dfa7522017-11-06 17:43:47 -0800261 case FIELD_COUNT_SINGLE | FIELD_TYPE_ENUM:
262 // if the field has its own enum mapping, use this, otherwise use general name to value mapping.
263 if (mEnums.find(name) != mEnums.end()) {
264 if (mEnums[name].find(value) != mEnums[name].end()) {
265 proto->write(found, mEnums[name][value]);
266 } else {
267 proto->write(found, 0); // TODO: should get the default enum value (Unknown)
268 }
269 } else if (mEnumValuesByName.find(value) != mEnumValuesByName.end()) {
270 proto->write(found, mEnumValuesByName[value]);
271 } else {
Yi Jine2f7f792017-11-01 17:08:27 -0700272 return false;
273 }
Yi Jine2f7f792017-11-01 17:08:27 -0700274 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800275 case FIELD_COUNT_SINGLE | FIELD_TYPE_INT32:
276 case FIELD_COUNT_SINGLE | FIELD_TYPE_SINT32:
277 case FIELD_COUNT_SINGLE | FIELD_TYPE_UINT32:
278 case FIELD_COUNT_SINGLE | FIELD_TYPE_FIXED32:
279 case FIELD_COUNT_SINGLE | FIELD_TYPE_SFIXED32:
Yi Jine2f7f792017-11-01 17:08:27 -0700280 proto->write(found, toInt(value));
Yi Jin04625ad2017-10-17 18:29:33 -0700281 break;
Yi Jin0dfa7522017-11-06 17:43:47 -0800282 // REPEATED TYPE below:
283 case FIELD_COUNT_REPEATED | FIELD_TYPE_INT32:
284 repeats = parseRecord(value, COMMA_DELIMITER);
285 for (size_t i=0; i<repeats.size(); i++) {
286 proto->write(found, toInt(repeats[i]));
287 }
288 break;
289 case FIELD_COUNT_REPEATED | FIELD_TYPE_STRING:
290 repeats = parseRecord(value, COMMA_DELIMITER);
291 for (size_t i=0; i<repeats.size(); i++) {
292 proto->write(found, repeats[i]);
293 }
294 break;
Yi Jin04625ad2017-10-17 18:29:33 -0700295 default:
296 return false;
297 }
298 return true;
299}
Yi Jin9299af932017-12-05 17:44:48 -0800300
301// ================================================================================
302Message::Message(Table* table)
303 :mTable(table),
304 mPreviousField(""),
305 mTokens(),
306 mSubMessages()
307{
308}
309
310Message::~Message()
311{
312}
313
314void
315Message::addSubMessage(uint64_t fieldId, Message* fieldMsg)
316{
317 for (auto iter = mTable->mFields.begin(); iter != mTable->mFields.end(); iter++) {
318 if (iter->second == fieldId) {
319 mSubMessages[iter->first] = fieldMsg;
320 return;
321 }
322 }
323}
324
325bool
326Message::insertField(ProtoOutputStream* proto, const std::string& name, const std::string& value)
327{
328 // If the field name can be found, it means the name is a primitive field.
329 if (mTable->mFields.find(name) != mTable->mFields.end()) {
330 endSession(proto);
331 // The only edge case is for example ro.hardware itself is a message, so a field called "value"
332 // would be defined in proto Ro::Hardware and it must be the first field.
333 if (mSubMessages.find(name) != mSubMessages.end()) {
334 startSession(proto, name);
335 return mSubMessages[name]->insertField(proto, "value", value);
336 } else {
337 return mTable->insertField(proto, name, value);
338 }
339 }
340
341 // Try to find the message field which is the prefix of name, so the value would be inserted
342 // recursively into the submessage.
343 string mutableName = name;
344 for (auto iter = mSubMessages.begin(); iter != mSubMessages.end(); iter++) {
345 string fieldName = iter->first;
346 string prefix = fieldName + "_"; // underscore is the delimiter in the name
347 if (stripPrefix(&mutableName, prefix.c_str())) {
348 if (mPreviousField != fieldName) {
349 endSession(proto);
350 startSession(proto, fieldName);
351 }
352 return mSubMessages[fieldName]->insertField(proto, mutableName, value);
353 }
354 }
355 // Can't find the name in proto definition, handle it separately.
356 return false;
357}
358
359void
360Message::startSession(ProtoOutputStream* proto, const string& name)
361{
362 uint64_t fieldId = mTable->mFields[name];
363 long long token = proto->start(fieldId);
364 mPreviousField = name;
365 mTokens.push(token);
366}
367
368void
369Message::endSession(ProtoOutputStream* proto)
370{
371 if (mPreviousField == "") return;
372 if (mSubMessages.find(mPreviousField) != mSubMessages.end()) {
373 mSubMessages[mPreviousField]->endSession(proto);
374 }
375 proto->end(mTokens.top());
376 mTokens.pop();
377 mPreviousField = "";
378}