eglGetFrameTimestamps: Allow reads done to equal rendering complete.
[platform/upstream/VK-GL-CTS.git] / framework / qphelper / qpXmlWriter.c
1 /*-------------------------------------------------------------------------
2  * drawElements Quality Program Helper Library
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 XML Writer.
22  *//*--------------------------------------------------------------------*/
23
24 #include "qpXmlWriter.h"
25
26 #include "deMemory.h"
27 #include "deInt32.h"
28
29 /*------------------------------------------------------------------------
30  * qpXmlWriter stand-alone implementation.
31  *----------------------------------------------------------------------*/
32
33 #include "deMemPool.h"
34 #include "dePoolArray.h"
35
36 struct qpXmlWriter_s
37 {
38         FILE*                           outputFile;
39         deBool                          flushAfterWrite;
40
41         deBool                          xmlPrevIsStartElement;
42         deBool                          xmlIsWriting;
43         int                                     xmlElementDepth;
44 };
45
46 static deBool writeEscaped (qpXmlWriter* writer, const char* str)
47 {
48         char            buf[256 + 10];
49         char*           d               = &buf[0];
50         const char*     s               = str;
51         deBool          isEOS   = DE_FALSE;
52
53         do
54         {
55                 /* Check for characters that need to be escaped. */
56                 const char* repl = DE_NULL;
57                 switch (*s)
58                 {
59                         case 0:         isEOS = DE_TRUE;                break;
60                         case '<':       repl = "&lt;";                  break;
61                         case '>':       repl = "&gt;";                  break;
62                         case '&':       repl = "&amp;";                 break;
63                         case '\'':      repl = "&apos;";                break;
64                         case '"':       repl = "&quot;";                break;
65
66                         /* Non-printable characters. */
67                         case 1:         repl = "&lt;SOH&gt;";   break;
68                         case 2:         repl = "&lt;STX&gt;";   break;
69                         case 3:         repl = "&lt;ETX&gt;";   break;
70                         case 4:         repl = "&lt;EOT&gt;";   break;
71                         case 5:         repl = "&lt;ENQ&gt;";   break;
72                         case 6:         repl = "&lt;ACK&gt;";   break;
73                         case 7:         repl = "&lt;BEL&gt;";   break;
74                         case 8:         repl = "&lt;BS&gt;";    break;
75                         case 11:        repl = "&lt;VT&gt;";    break;
76                         case 12:        repl = "&lt;FF&gt;";    break;
77                         case 14:        repl = "&lt;SO&gt;";    break;
78                         case 15:        repl = "&lt;SI&gt;";    break;
79                         case 16:        repl = "&lt;DLE&gt;";   break;
80                         case 17:        repl = "&lt;DC1&gt;";   break;
81                         case 18:        repl = "&lt;DC2&gt;";   break;
82                         case 19:        repl = "&lt;DC3&gt;";   break;
83                         case 20:        repl = "&lt;DC4&gt;";   break;
84                         case 21:        repl = "&lt;NAK&gt;";   break;
85                         case 22:        repl = "&lt;SYN&gt;";   break;
86                         case 23:        repl = "&lt;ETB&gt;";   break;
87                         case 24:        repl = "&lt;CAN&gt;";   break;
88                         case 25:        repl = "&lt;EM&gt;";    break;
89                         case 26:        repl = "&lt;SUB&gt;";   break;
90                         case 27:        repl = "&lt;ESC&gt;";   break;
91                         case 28:        repl = "&lt;FS&gt;";    break;
92                         case 29:        repl = "&lt;GS&gt;";    break;
93                         case 30:        repl = "&lt;RS&gt;";    break;
94                         case 31:        repl = "&lt;US&gt;";    break;
95
96                         default:        /* nada */                              break;
97                 }
98
99                 /* Write out char or escape sequence. */
100                 if (repl)
101                 {
102                         s++;
103                         strcpy(d, repl);
104                         d += strlen(repl);
105                 }
106                 else
107                         *d++ = *s++;
108
109                 /* Write buffer if EOS or buffer full. */
110                 if (isEOS || ((d - &buf[0]) >= 4))
111                 {
112                         *d = 0;
113                         fprintf(writer->outputFile, "%s", buf);
114                         d = &buf[0];
115                 }
116         } while (!isEOS);
117
118         if (writer->flushAfterWrite)
119                 fflush(writer->outputFile);
120         DE_ASSERT(d == &buf[0]); /* buffer must be empty */
121         return DE_TRUE;
122 }
123
124 qpXmlWriter* qpXmlWriter_createFileWriter (FILE* outputFile, deBool useCompression, deBool flushAfterWrite)
125 {
126         qpXmlWriter* writer = (qpXmlWriter*)deCalloc(sizeof(qpXmlWriter));
127         if (!writer)
128                 return DE_NULL;
129
130         DE_UNREF(useCompression); /* no compression supported. */
131
132         writer->outputFile = outputFile;
133         writer->flushAfterWrite = flushAfterWrite;
134
135         return writer;
136 }
137
138 void qpXmlWriter_destroy (qpXmlWriter* writer)
139 {
140         DE_ASSERT(writer);
141
142         deFree(writer);
143 }
144
145 static deBool closePending (qpXmlWriter* writer)
146 {
147         if (writer->xmlPrevIsStartElement)
148         {
149                 fprintf(writer->outputFile, ">\n");
150                 writer->xmlPrevIsStartElement = DE_FALSE;
151         }
152
153         return DE_TRUE;
154 }
155
156 void qpXmlWriter_flush (qpXmlWriter* writer)
157 {
158         closePending(writer);
159 }
160
161 deBool qpXmlWriter_startDocument (qpXmlWriter* writer)
162 {
163         DE_ASSERT(writer && !writer->xmlIsWriting);
164         writer->xmlIsWriting                    = DE_TRUE;
165         writer->xmlElementDepth                 = 0;
166         writer->xmlPrevIsStartElement   = DE_FALSE;
167         fprintf(writer->outputFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
168         return DE_TRUE;
169 }
170
171 static const char* getIndentStr (int indentLevel)
172 {
173         static const char       s_indentStr[33] = "                                ";
174         static const int        s_indentStrLen  = 32;
175         return &s_indentStr[s_indentStrLen - deMin32(s_indentStrLen, indentLevel)];
176 }
177
178 deBool qpXmlWriter_endDocument (qpXmlWriter* writer)
179 {
180         DE_ASSERT(writer);
181         DE_ASSERT(writer->xmlIsWriting);
182         DE_ASSERT(writer->xmlElementDepth == 0);
183         closePending(writer);
184         writer->xmlIsWriting = DE_FALSE;
185         return DE_TRUE;
186 }
187
188 deBool qpXmlWriter_writeString (qpXmlWriter* writer, const char* str)
189 {
190         if (writer->xmlPrevIsStartElement)
191         {
192                 fprintf(writer->outputFile, ">");
193                 writer->xmlPrevIsStartElement = DE_FALSE;
194         }
195
196         return writeEscaped(writer, str);
197 }
198
199 deBool qpXmlWriter_startElement(qpXmlWriter* writer, const char* elementName, int numAttribs, const qpXmlAttribute* attribs)
200 {
201         int ndx;
202
203         closePending(writer);
204
205         fprintf(writer->outputFile, "%s<%s", getIndentStr(writer->xmlElementDepth), elementName);
206
207         for (ndx = 0; ndx < numAttribs; ndx++)
208         {
209                 const qpXmlAttribute* attrib = &attribs[ndx];
210                 fprintf(writer->outputFile, " %s=\"", attrib->name);
211                 switch (attrib->type)
212                 {
213                         case QP_XML_ATTRIBUTE_STRING:
214                                 writeEscaped(writer, attrib->stringValue);
215                                 break;
216
217                         case QP_XML_ATTRIBUTE_INT:
218                         {
219                                 char buf[64];
220                                 sprintf(buf, "%d", attrib->intValue);
221                                 writeEscaped(writer, buf);
222                                 break;
223                         }
224
225                         case QP_XML_ATTRIBUTE_BOOL:
226                                 writeEscaped(writer, attrib->boolValue ? "True" : "False");
227                                 break;
228
229                         default:
230                                 DE_ASSERT(DE_FALSE);
231                 }
232                 fprintf(writer->outputFile, "\"");
233         }
234
235         writer->xmlElementDepth++;
236         writer->xmlPrevIsStartElement = DE_TRUE;
237         return DE_TRUE;
238 }
239
240 deBool qpXmlWriter_endElement (qpXmlWriter* writer, const char* elementName)
241 {
242         DE_ASSERT(writer && writer->xmlElementDepth > 0);
243         writer->xmlElementDepth--;
244
245         if (writer->xmlPrevIsStartElement) /* leave flag as-is */
246         {
247                 fprintf(writer->outputFile, " />\n");
248                 writer->xmlPrevIsStartElement = DE_FALSE;
249         }
250         else
251                 fprintf(writer->outputFile, "</%s>\n", /*getIndentStr(writer->xmlElementDepth),*/ elementName);
252
253         return DE_TRUE;
254 }
255
256 deBool qpXmlWriter_writeBase64 (qpXmlWriter* writer, const deUint8* data, size_t numBytes)
257 {
258         static const char s_base64Table[64] =
259         {
260                 'A','B','C','D','E','F','G','H','I','J','K','L','M',
261                 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
262                 'a','b','c','d','e','f','g','h','i','j','k','l','m',
263                 'n','o','p','q','r','s','t','u','v','w','x','y','z',
264                 '0','1','2','3','4','5','6','7','8','9','+','/'
265         };
266
267         int                     numWritten      = 0;
268         size_t          srcNdx          = 0;
269         deBool          writeIndent     = DE_TRUE;
270         const char*     indentStr       = getIndentStr(writer->xmlElementDepth);
271
272         DE_ASSERT(writer && data && (numBytes > 0));
273
274         /* Close and pending writes. */
275         closePending(writer);
276
277         /* Loop all input chars. */
278         while (srcNdx < numBytes)
279         {
280                 size_t  numRead = (size_t)deMin32(3, (int)(numBytes - srcNdx));
281                 deUint8 s0 = data[srcNdx];
282                 deUint8 s1 = (numRead >= 2) ? data[srcNdx+1] : 0;
283                 deUint8 s2 = (numRead >= 3) ? data[srcNdx+2] : 0;
284                 char    d[5];
285
286                 srcNdx += numRead;
287
288                 d[0] = s_base64Table[s0 >> 2];
289                 d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
290                 d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
291                 d[3] = s_base64Table[s2&0x3F];
292                 d[4] = 0;
293
294                 if (numRead < 3) d[3] = '=';
295                 if (numRead < 2) d[2] = '=';
296
297                 /* Write indent (if needed). */
298                 if (writeIndent)
299                 {
300                         fprintf(writer->outputFile, "%s", indentStr);
301                         writeIndent = DE_FALSE;
302                 }
303
304                 /* Write data. */
305                 fprintf(writer->outputFile, "%s", &d[0]);
306
307                 /* EOL every now and then. */
308                 numWritten += 4;
309                 if (numWritten >= 64)
310                 {
311                         fprintf(writer->outputFile, "\n");
312                         numWritten = 0;
313                         writeIndent = DE_TRUE;
314                 }
315         }
316
317         /* Last EOL. */
318         if (numWritten > 0)
319                 fprintf(writer->outputFile, "\n");
320
321         DE_ASSERT(srcNdx == numBytes);
322         return DE_TRUE;
323 }
324
325 /* Common helper functions. */
326
327 deBool qpXmlWriter_writeStringElement (qpXmlWriter* writer, const char* elementName, const char* elementContent)
328 {
329         if (!qpXmlWriter_startElement(writer, elementName, 0, DE_NULL) ||
330                 (elementContent && !qpXmlWriter_writeString(writer, elementContent)) ||
331                 !qpXmlWriter_endElement(writer, elementName))
332                 return DE_FALSE;
333
334         return DE_TRUE;
335 }