You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
336 lines
8.5 KiB
336 lines
8.5 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Helper Library
|
|
* -------------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief XML Writer.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "qpXmlWriter.h"
|
|
|
|
#include "deMemory.h"
|
|
#include "deInt32.h"
|
|
|
|
/*------------------------------------------------------------------------
|
|
* qpXmlWriter stand-alone implementation.
|
|
*----------------------------------------------------------------------*/
|
|
|
|
#include "deMemPool.h"
|
|
#include "dePoolArray.h"
|
|
|
|
struct qpXmlWriter_s
|
|
{
|
|
FILE* outputFile;
|
|
deBool flushAfterWrite;
|
|
|
|
deBool xmlPrevIsStartElement;
|
|
deBool xmlIsWriting;
|
|
int xmlElementDepth;
|
|
};
|
|
|
|
static deBool writeEscaped (qpXmlWriter* writer, const char* str)
|
|
{
|
|
char buf[256 + 10];
|
|
char* d = &buf[0];
|
|
const char* s = str;
|
|
deBool isEOS = DE_FALSE;
|
|
|
|
do
|
|
{
|
|
/* Check for characters that need to be escaped. */
|
|
const char* repl = DE_NULL;
|
|
switch (*s)
|
|
{
|
|
case 0: isEOS = DE_TRUE; break;
|
|
case '<': repl = "<"; break;
|
|
case '>': repl = ">"; break;
|
|
case '&': repl = "&"; break;
|
|
case '\'': repl = "'"; break;
|
|
case '"': repl = """; break;
|
|
|
|
/* Non-printable characters. */
|
|
case 1: repl = "<SOH>"; break;
|
|
case 2: repl = "<STX>"; break;
|
|
case 3: repl = "<ETX>"; break;
|
|
case 4: repl = "<EOT>"; break;
|
|
case 5: repl = "<ENQ>"; break;
|
|
case 6: repl = "<ACK>"; break;
|
|
case 7: repl = "<BEL>"; break;
|
|
case 8: repl = "<BS>"; break;
|
|
case 11: repl = "<VT>"; break;
|
|
case 12: repl = "<FF>"; break;
|
|
case 14: repl = "<SO>"; break;
|
|
case 15: repl = "<SI>"; break;
|
|
case 16: repl = "<DLE>"; break;
|
|
case 17: repl = "<DC1>"; break;
|
|
case 18: repl = "<DC2>"; break;
|
|
case 19: repl = "<DC3>"; break;
|
|
case 20: repl = "<DC4>"; break;
|
|
case 21: repl = "<NAK>"; break;
|
|
case 22: repl = "<SYN>"; break;
|
|
case 23: repl = "<ETB>"; break;
|
|
case 24: repl = "<CAN>"; break;
|
|
case 25: repl = "<EM>"; break;
|
|
case 26: repl = "<SUB>"; break;
|
|
case 27: repl = "<ESC>"; break;
|
|
case 28: repl = "<FS>"; break;
|
|
case 29: repl = "<GS>"; break;
|
|
case 30: repl = "<RS>"; break;
|
|
case 31: repl = "<US>"; break;
|
|
|
|
default: /* nada */ break;
|
|
}
|
|
|
|
/* Write out char or escape sequence. */
|
|
if (repl)
|
|
{
|
|
s++;
|
|
strcpy(d, repl);
|
|
d += strlen(repl);
|
|
}
|
|
else
|
|
*d++ = *s++;
|
|
|
|
/* Write buffer if EOS or buffer full. */
|
|
if (isEOS || ((d - &buf[0]) >= 4))
|
|
{
|
|
*d = 0;
|
|
fprintf(writer->outputFile, "%s", buf);
|
|
d = &buf[0];
|
|
}
|
|
} while (!isEOS);
|
|
|
|
if (writer->flushAfterWrite)
|
|
fflush(writer->outputFile);
|
|
DE_ASSERT(d == &buf[0]); /* buffer must be empty */
|
|
return DE_TRUE;
|
|
}
|
|
|
|
qpXmlWriter* qpXmlWriter_createFileWriter (FILE* outputFile, deBool useCompression, deBool flushAfterWrite)
|
|
{
|
|
qpXmlWriter* writer = (qpXmlWriter*)deCalloc(sizeof(qpXmlWriter));
|
|
if (!writer)
|
|
return DE_NULL;
|
|
|
|
DE_UNREF(useCompression); /* no compression supported. */
|
|
|
|
writer->outputFile = outputFile;
|
|
writer->flushAfterWrite = flushAfterWrite;
|
|
|
|
return writer;
|
|
}
|
|
|
|
void qpXmlWriter_destroy (qpXmlWriter* writer)
|
|
{
|
|
DE_ASSERT(writer);
|
|
|
|
deFree(writer);
|
|
}
|
|
|
|
static deBool closePending (qpXmlWriter* writer)
|
|
{
|
|
if (writer->xmlPrevIsStartElement)
|
|
{
|
|
fprintf(writer->outputFile, ">\n");
|
|
writer->xmlPrevIsStartElement = DE_FALSE;
|
|
}
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
void qpXmlWriter_flush (qpXmlWriter* writer)
|
|
{
|
|
closePending(writer);
|
|
}
|
|
|
|
deBool qpXmlWriter_startDocument (qpXmlWriter* writer)
|
|
{
|
|
DE_ASSERT(writer && !writer->xmlIsWriting);
|
|
writer->xmlIsWriting = DE_TRUE;
|
|
writer->xmlElementDepth = 0;
|
|
writer->xmlPrevIsStartElement = DE_FALSE;
|
|
fprintf(writer->outputFile, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
|
return DE_TRUE;
|
|
}
|
|
|
|
static const char* getIndentStr (int indentLevel)
|
|
{
|
|
static const char s_indentStr[33] = " ";
|
|
static const int s_indentStrLen = 32;
|
|
return &s_indentStr[s_indentStrLen - deMin32(s_indentStrLen, indentLevel)];
|
|
}
|
|
|
|
deBool qpXmlWriter_endDocument (qpXmlWriter* writer)
|
|
{
|
|
DE_ASSERT(writer);
|
|
DE_ASSERT(writer->xmlIsWriting);
|
|
DE_ASSERT(writer->xmlElementDepth == 0);
|
|
closePending(writer);
|
|
writer->xmlIsWriting = DE_FALSE;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool qpXmlWriter_writeString (qpXmlWriter* writer, const char* str)
|
|
{
|
|
if (writer->xmlPrevIsStartElement)
|
|
{
|
|
fprintf(writer->outputFile, ">");
|
|
writer->xmlPrevIsStartElement = DE_FALSE;
|
|
}
|
|
|
|
return writeEscaped(writer, str);
|
|
}
|
|
|
|
deBool qpXmlWriter_startElement(qpXmlWriter* writer, const char* elementName, int numAttribs, const qpXmlAttribute* attribs)
|
|
{
|
|
int ndx;
|
|
|
|
closePending(writer);
|
|
|
|
fprintf(writer->outputFile, "%s<%s", getIndentStr(writer->xmlElementDepth), elementName);
|
|
|
|
for (ndx = 0; ndx < numAttribs; ndx++)
|
|
{
|
|
const qpXmlAttribute* attrib = &attribs[ndx];
|
|
fprintf(writer->outputFile, " %s=\"", attrib->name);
|
|
switch (attrib->type)
|
|
{
|
|
case QP_XML_ATTRIBUTE_STRING:
|
|
writeEscaped(writer, attrib->stringValue);
|
|
break;
|
|
|
|
case QP_XML_ATTRIBUTE_INT:
|
|
{
|
|
char buf[64];
|
|
sprintf(buf, "%d", attrib->intValue);
|
|
writeEscaped(writer, buf);
|
|
break;
|
|
}
|
|
|
|
case QP_XML_ATTRIBUTE_BOOL:
|
|
writeEscaped(writer, attrib->boolValue ? "True" : "False");
|
|
break;
|
|
|
|
default:
|
|
DE_ASSERT(DE_FALSE);
|
|
}
|
|
fprintf(writer->outputFile, "\"");
|
|
}
|
|
|
|
writer->xmlElementDepth++;
|
|
writer->xmlPrevIsStartElement = DE_TRUE;
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool qpXmlWriter_endElement (qpXmlWriter* writer, const char* elementName)
|
|
{
|
|
DE_ASSERT(writer && writer->xmlElementDepth > 0);
|
|
writer->xmlElementDepth--;
|
|
|
|
if (writer->xmlPrevIsStartElement) /* leave flag as-is */
|
|
{
|
|
fprintf(writer->outputFile, " />\n");
|
|
writer->xmlPrevIsStartElement = DE_FALSE;
|
|
}
|
|
else
|
|
fprintf(writer->outputFile, "</%s>\n", /*getIndentStr(writer->xmlElementDepth),*/ elementName);
|
|
|
|
return DE_TRUE;
|
|
}
|
|
|
|
deBool qpXmlWriter_writeBase64 (qpXmlWriter* writer, const deUint8* data, size_t numBytes)
|
|
{
|
|
static const char s_base64Table[64] =
|
|
{
|
|
'A','B','C','D','E','F','G','H','I','J','K','L','M',
|
|
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
|
|
'a','b','c','d','e','f','g','h','i','j','k','l','m',
|
|
'n','o','p','q','r','s','t','u','v','w','x','y','z',
|
|
'0','1','2','3','4','5','6','7','8','9','+','/'
|
|
};
|
|
|
|
int numWritten = 0;
|
|
size_t srcNdx = 0;
|
|
deBool writeIndent = DE_TRUE;
|
|
const char* indentStr = getIndentStr(writer->xmlElementDepth);
|
|
|
|
DE_ASSERT(writer && data && (numBytes > 0));
|
|
|
|
/* Close and pending writes. */
|
|
closePending(writer);
|
|
|
|
/* Loop all input chars. */
|
|
while (srcNdx < numBytes)
|
|
{
|
|
size_t numRead = (size_t)deMin32(3, (int)(numBytes - srcNdx));
|
|
deUint8 s0 = data[srcNdx];
|
|
deUint8 s1 = (numRead >= 2) ? data[srcNdx+1] : 0;
|
|
deUint8 s2 = (numRead >= 3) ? data[srcNdx+2] : 0;
|
|
char d[5];
|
|
|
|
srcNdx += numRead;
|
|
|
|
d[0] = s_base64Table[s0 >> 2];
|
|
d[1] = s_base64Table[((s0&0x3)<<4) | (s1>>4)];
|
|
d[2] = s_base64Table[((s1&0xF)<<2) | (s2>>6)];
|
|
d[3] = s_base64Table[s2&0x3F];
|
|
d[4] = 0;
|
|
|
|
if (numRead < 3) d[3] = '=';
|
|
if (numRead < 2) d[2] = '=';
|
|
|
|
/* Write indent (if needed). */
|
|
if (writeIndent)
|
|
{
|
|
fprintf(writer->outputFile, "%s", indentStr);
|
|
writeIndent = DE_FALSE;
|
|
}
|
|
|
|
/* Write data. */
|
|
fprintf(writer->outputFile, "%s", &d[0]);
|
|
|
|
/* EOL every now and then. */
|
|
numWritten += 4;
|
|
if (numWritten >= 64)
|
|
{
|
|
fprintf(writer->outputFile, "\n");
|
|
numWritten = 0;
|
|
writeIndent = DE_TRUE;
|
|
}
|
|
}
|
|
|
|
/* Last EOL. */
|
|
if (numWritten > 0)
|
|
fprintf(writer->outputFile, "\n");
|
|
|
|
DE_ASSERT(srcNdx == numBytes);
|
|
return DE_TRUE;
|
|
}
|
|
|
|
/* Common helper functions. */
|
|
|
|
deBool qpXmlWriter_writeStringElement (qpXmlWriter* writer, const char* elementName, const char* elementContent)
|
|
{
|
|
if (!qpXmlWriter_startElement(writer, elementName, 0, DE_NULL) ||
|
|
(elementContent && !qpXmlWriter_writeString(writer, elementContent)) ||
|
|
!qpXmlWriter_endElement(writer, elementName))
|
|
return DE_FALSE;
|
|
|
|
return DE_TRUE;
|
|
}
|