xref: /aosp_15_r20/external/deqp/executor/xeContainerFormatParser.cpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Test Executor
3  * ------------------------------------------
4  *
5  * Copyright 2014 The Android Open Source Project
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief Test log container format parser.
22  *//*--------------------------------------------------------------------*/
23 
24 #include "xeContainerFormatParser.hpp"
25 #include "deInt32.h"
26 
27 namespace xe
28 {
29 
30 enum
31 {
32     CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE = 1024
33 };
34 
getNextBufferSize(int curSize,int minNewSize)35 static int getNextBufferSize(int curSize, int minNewSize)
36 {
37     return de::max(curSize * 2, 1 << deLog2Ceil32(minNewSize));
38 }
39 
ContainerFormatParser(void)40 ContainerFormatParser::ContainerFormatParser(void)
41     : m_element(CONTAINERELEMENT_INCOMPLETE)
42     , m_elementLen(0)
43     , m_state(STATE_AT_LINE_START)
44     , m_buf(CONTAINERFORMATPARSER_INITIAL_BUFFER_SIZE)
45 {
46 }
47 
~ContainerFormatParser(void)48 ContainerFormatParser::~ContainerFormatParser(void)
49 {
50 }
51 
clear(void)52 void ContainerFormatParser::clear(void)
53 {
54     m_element    = CONTAINERELEMENT_INCOMPLETE;
55     m_elementLen = 0;
56     m_state      = STATE_AT_LINE_START;
57     m_buf.clear();
58 }
59 
error(const std::string & what)60 void ContainerFormatParser::error(const std::string &what)
61 {
62     throw ContainerParseError(what);
63 }
64 
feed(const uint8_t * bytes,size_t numBytes)65 void ContainerFormatParser::feed(const uint8_t *bytes, size_t numBytes)
66 {
67     // Grow buffer if necessary.
68     if (m_buf.getNumFree() < (int)numBytes)
69         m_buf.resize(getNextBufferSize(m_buf.getSize(), m_buf.getNumElements() + (int)numBytes));
70 
71     // Append to front.
72     m_buf.pushFront(bytes, (int)numBytes);
73 
74     // If we haven't parsed complete element, re-try after data feed.
75     if (m_element == CONTAINERELEMENT_INCOMPLETE)
76         advance();
77 }
78 
getSessionInfoAttribute(void) const79 const char *ContainerFormatParser::getSessionInfoAttribute(void) const
80 {
81     DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
82     return m_attribute.c_str();
83 }
84 
getSessionInfoValue(void) const85 const char *ContainerFormatParser::getSessionInfoValue(void) const
86 {
87     DE_ASSERT(m_element == CONTAINERELEMENT_SESSION_INFO);
88     return m_value.c_str();
89 }
90 
getTestCasePath(void) const91 const char *ContainerFormatParser::getTestCasePath(void) const
92 {
93     DE_ASSERT(m_element == CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT);
94     return m_value.c_str();
95 }
96 
getTerminateReason(void) const97 const char *ContainerFormatParser::getTerminateReason(void) const
98 {
99     DE_ASSERT(m_element == CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT);
100     return m_value.c_str();
101 }
102 
getDataSize(void) const103 int ContainerFormatParser::getDataSize(void) const
104 {
105     DE_ASSERT(m_element == CONTAINERELEMENT_TEST_LOG_DATA);
106     return m_elementLen;
107 }
108 
getData(uint8_t * dst,int numBytes,int offset)109 void ContainerFormatParser::getData(uint8_t *dst, int numBytes, int offset)
110 {
111     DE_ASSERT(de::inBounds(offset, 0, m_elementLen) && numBytes > 0 && de::inRange(numBytes + offset, 0, m_elementLen));
112 
113     for (int ndx = 0; ndx < numBytes; ndx++)
114         dst[ndx] = m_buf.peekBack(offset + ndx);
115 }
116 
getChar(int offset) const117 int ContainerFormatParser::getChar(int offset) const
118 {
119     DE_ASSERT(de::inRange(offset, 0, m_buf.getNumElements()));
120 
121     if (offset < m_buf.getNumElements())
122         return m_buf.peekBack(offset);
123     else
124         return END_OF_BUFFER;
125 }
126 
getTestRunsParams(void) const127 const char *ContainerFormatParser::getTestRunsParams(void) const
128 {
129     DE_ASSERT(m_element == CONTAINERELEMENT_TEST_RUN_PARAM_BEGIN);
130     return m_value.c_str();
131 }
132 
advance(void)133 void ContainerFormatParser::advance(void)
134 {
135     if (m_element != CONTAINERELEMENT_INCOMPLETE)
136     {
137         m_buf.popBack(m_elementLen);
138 
139         m_element    = CONTAINERELEMENT_INCOMPLETE;
140         m_elementLen = 0;
141         m_attribute.clear();
142         m_value.clear();
143     }
144 
145     for (;;)
146     {
147         int curChar = getChar(m_elementLen);
148 
149         if (curChar != (int)END_OF_BUFFER)
150             m_elementLen += 1;
151 
152         if (curChar == END_OF_STRING)
153         {
154             if (m_elementLen == 1)
155                 m_element = CONTAINERELEMENT_END_OF_STRING;
156             else if (m_state == STATE_CONTAINER_LINE)
157                 parseContainerLine();
158             else
159                 m_element = CONTAINERELEMENT_TEST_LOG_DATA;
160 
161             break;
162         }
163         else if (curChar == (int)END_OF_BUFFER)
164         {
165             if (m_elementLen > 0 && m_state == STATE_DATA)
166                 m_element = CONTAINERELEMENT_TEST_LOG_DATA;
167 
168             break;
169         }
170         else if (curChar == '\r' || curChar == '\n')
171         {
172             // Check for \r\n
173             int nextChar = getChar(m_elementLen);
174             if (curChar == '\n' || (nextChar != (int)END_OF_BUFFER && nextChar != '\n'))
175             {
176                 if (m_state == STATE_CONTAINER_LINE)
177                     parseContainerLine();
178                 else
179                     m_element = CONTAINERELEMENT_TEST_LOG_DATA;
180 
181                 m_state = STATE_AT_LINE_START;
182                 break;
183             }
184             // else handle following end or \n in next iteration.
185         }
186         else if (m_state == STATE_AT_LINE_START)
187         {
188             DE_ASSERT(m_elementLen == 1);
189             m_state = (curChar == '#') ? STATE_CONTAINER_LINE : STATE_DATA;
190         }
191     }
192 }
193 
parseContainerLine(void)194 void ContainerFormatParser::parseContainerLine(void)
195 {
196     static const struct
197     {
198         const char *name;
199         ContainerElement element;
200     } s_elements[] = {
201         {"beginTestCaseResult", CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT},
202         {"endTestCaseResult", CONTAINERELEMENT_END_TEST_CASE_RESULT},
203         {"terminateTestCaseResult", CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT},
204         {"sessionInfo", CONTAINERELEMENT_SESSION_INFO},
205         {"beginSession", CONTAINERELEMENT_BEGIN_SESSION},
206         {"endSession", CONTAINERELEMENT_END_SESSION},
207         {"beginTestRunParamsCollection", CONTAINERELEMENT_TEST_RUN_PARAM_SESSION_BEGIN},
208         {"endTestRunParamsCollection", CONTAINERELEMENT_TEST_RUN_PARAM_SESSION_END},
209         {"beginTestRunParams", CONTAINERELEMENT_TEST_RUN_PARAM_BEGIN},
210         {"endTestRunParams", CONTAINERELEMENT_TEST_RUN_PARAM_END},
211     };
212 
213     DE_ASSERT(m_elementLen >= 1);
214     DE_ASSERT(getChar(0) == '#');
215 
216     int offset = 1;
217 
218     for (int elemNdx = 0; elemNdx < DE_LENGTH_OF_ARRAY(s_elements); elemNdx++)
219     {
220         bool isMatch = false;
221         int ndx      = 0;
222 
223         for (;;)
224         {
225             int bufChar = (offset + ndx < m_elementLen) ? getChar(offset + ndx) : 0;
226             bool bufEnd =
227                 bufChar == 0 || bufChar == ' ' || bufChar == '\r' || bufChar == '\n' || bufChar == (int)END_OF_BUFFER;
228             int elemChar = s_elements[elemNdx].name[ndx];
229             bool elemEnd = elemChar == 0;
230 
231             if (bufEnd || elemEnd)
232             {
233                 isMatch = bufEnd == elemEnd;
234                 break;
235             }
236             else if (bufChar != elemChar)
237                 break;
238 
239             ndx += 1;
240         }
241 
242         if (isMatch)
243         {
244             m_element = s_elements[elemNdx].element;
245             offset += ndx;
246             break;
247         }
248     }
249 
250     switch (m_element)
251     {
252     case CONTAINERELEMENT_BEGIN_SESSION:
253     case CONTAINERELEMENT_END_SESSION:
254     case CONTAINERELEMENT_END_TEST_CASE_RESULT:
255     case CONTAINERELEMENT_TEST_RUN_PARAM_SESSION_BEGIN:
256     case CONTAINERELEMENT_TEST_RUN_PARAM_SESSION_END:
257     case CONTAINERELEMENT_TEST_RUN_PARAM_END:
258         break; // No attribute or value.
259 
260     case CONTAINERELEMENT_BEGIN_TEST_CASE_RESULT:
261     case CONTAINERELEMENT_TERMINATE_TEST_CASE_RESULT:
262     case CONTAINERELEMENT_TEST_RUN_PARAM_BEGIN:
263         if (getChar(offset) != ' ')
264             error("Expected value after instruction");
265         offset += 1;
266         parseContainerValue(m_value, offset);
267         break;
268 
269     case CONTAINERELEMENT_SESSION_INFO:
270         if (getChar(offset) != ' ')
271             error("Expected attribute name after #sessionInfo");
272         offset += 1;
273         parseContainerValue(m_attribute, offset);
274         if (getChar(offset) != ' ')
275             error("No value for #sessionInfo attribute");
276         offset += 1;
277 
278         if (m_attribute == "timestamp")
279         {
280             m_value.clear();
281 
282             // \note Candy produces unescaped timestamps.
283             for (;;)
284             {
285                 const int curChar = offset < m_elementLen ? getChar(offset) : 0;
286                 const bool isEnd  = curChar == 0 || curChar == (int)END_OF_BUFFER || curChar == '\n' || curChar == '\t';
287 
288                 if (isEnd)
289                     break;
290                 else
291                     m_value.push_back((char)curChar);
292 
293                 offset += 1;
294             }
295         }
296         else
297             parseContainerValue(m_value, offset);
298         break;
299 
300     default:
301         // \todo [2012-06-09 pyry] Implement better way to handle # at the beginning of log lines.
302         m_element = CONTAINERELEMENT_TEST_LOG_DATA;
303         break;
304     }
305 }
306 
parseContainerValue(std::string & dst,int & offset) const307 void ContainerFormatParser::parseContainerValue(std::string &dst, int &offset) const
308 {
309     DE_ASSERT(offset < m_elementLen);
310 
311     bool isString = getChar(offset) == '"' || getChar(offset) == '\'';
312     int quotChar  = isString ? getChar(offset) : 0;
313 
314     if (isString)
315         offset += 1;
316 
317     dst.clear();
318 
319     for (;;)
320     {
321         int curChar = offset < m_elementLen ? getChar(offset) : 0;
322         bool isEnd  = curChar == 0 || curChar == (int)END_OF_BUFFER ||
323                      (isString ? (curChar == quotChar) : (curChar == ' ' || curChar == '\n' || curChar == '\r'));
324 
325         if (isEnd)
326             break;
327         else
328         {
329             // \todo [2012-06-09 pyry] Escapes.
330             dst.push_back((char)curChar);
331         }
332 
333         offset += 1;
334     }
335 
336     if (isString && getChar(offset) == quotChar)
337         offset += 1;
338 }
339 
340 } // namespace xe
341