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