public string GetAttribute(string aName) { byte[] name = Encoding.ASCII.GetBytes(aName); for (int i = 0; i < attributes.Count; i++) { TBXMLAttribute attribute = attributes [i]; if (tbxml.strlen(attribute.nameIdx) == name.Length && tbxml.strncmp(attribute.nameIdx, name, name.Length) == 0) { return(System.Text.UTF8Encoding.Default.GetString(tbxml.bytes, (int)attribute.valueIdx, (int)tbxml.strlen(attribute.valueIdx))); } } return(null); }
private void DecodeBytes(Action <TBXMLElement> onStartElement, Action <TBXMLElement> onEndElement) { byte[] localBytes = bytes; long localBytesLength = bytesLength; Stack <TBXMLElement> elementStack = new Stack <TBXMLElement> (); Stack <TBXMLElement> freeElementList = new Stack <TBXMLElement> (); TBXMLAttribute xmlAttribute = new TBXMLAttribute(); // set elementStart pointer to the start of our xml long elementStartIdx = 0; // find next element start while ((elementStartIdx = strstr1(elementStartIdx, (byte)'<')) < localBytesLength) { // detect comment section //if (strncmp (elementStartIdx, commentStartArray, commentStartArray.Length) == 0) { if (localBytes [elementStartIdx] == (byte)'<' && localBytes [elementStartIdx + 1] == (byte)'!' && localBytes [elementStartIdx + 2] == (byte)'-') { elementStartIdx = strstr3(elementStartIdx, (byte)'-', (byte)'-', (byte)'>') + 3; continue; } // detect cdata section within element text //long isCDATA = strncmp (elementStartIdx, cdataStartArray, cdataStartArray.Length); // NOTE: logic is switched here to match the output of the strncmp() method, in which 0 == true long isCDATA = 1; if (elementStartIdx < localBytesLength - 9 && localBytes [elementStartIdx] == (byte)'<' && localBytes [elementStartIdx + 8] == (byte)'[' && localBytes [elementStartIdx + 1] == (byte)'!' && localBytes [elementStartIdx + 2] == (byte)'[' && localBytes [elementStartIdx + 3] == (byte)'C' && localBytes [elementStartIdx + 4] == (byte)'D' && localBytes [elementStartIdx + 5] == (byte)'A' && localBytes [elementStartIdx + 6] == (byte)'T' && localBytes [elementStartIdx + 7] == (byte)'A') { isCDATA = 0; } long elementEndIdx; // if cdata section found, skip data within cdata section and remove cdata tags if (isCDATA == 0) { // find end of cdata section long CDATAEndIdx = strstr3(elementStartIdx, (byte)']', (byte)']', (byte)'>'); // find start of next element skipping any cdata sections within text elementEndIdx = CDATAEndIdx; // find next open tag elementEndIdx = strstr1(elementEndIdx, (byte)'<'); // if open tag is a cdata section while (localBytes [elementEndIdx] == (byte)'<' && localBytes [elementEndIdx + 8] == (byte)'[' && localBytes [elementEndIdx + 1] == (byte)'!' && localBytes [elementEndIdx + 2] == (byte)'[' && localBytes [elementEndIdx + 3] == (byte)'C' && localBytes [elementEndIdx + 4] == (byte)'D' && localBytes [elementEndIdx + 5] == (byte)'A' && localBytes [elementEndIdx + 6] == (byte)'T' && localBytes [elementEndIdx + 7] == (byte)'A') { // find end of cdata section elementEndIdx = strstr3(elementEndIdx, (byte)']', (byte)']', (byte)'>'); // find next open tag elementEndIdx = strstr1(elementEndIdx, (byte)'<'); } // calculate length of cdata content long CDATALength = elementEndIdx - elementStartIdx; // calculate total length of text long textLength = elementEndIdx - elementStartIdx; // remove begining cdata section tag memcpy(elementStartIdx, elementStartIdx + 9, CDATAEndIdx - elementStartIdx - 9); // remove ending cdata section tag memcpy(CDATAEndIdx - 9, CDATAEndIdx + 3, textLength - CDATALength - 3); // blank out end of text memset(elementStartIdx + textLength - 12, (byte)' ', 12); // set new search start position elementStartIdx = CDATAEndIdx - 9; continue; } // find element end, skipping any cdata sections within attributes elementEndIdx = elementStartIdx + 1; while ((elementEndIdx = strpbrk2(elementEndIdx, (byte)'<', (byte)'>')) < localBytesLength) { //if (strncmp (elementEndIdx, cdataStartArray, cdataStartArray.Length) == 0) { if (localBytes [elementEndIdx] == (byte)'<' && localBytes [elementEndIdx + 8] == (byte)'[' && localBytes [elementEndIdx + 1] == (byte)'!' && localBytes [elementEndIdx + 2] == (byte)'[' && localBytes [elementEndIdx + 3] == (byte)'C' && localBytes [elementEndIdx + 4] == (byte)'D' && localBytes [elementEndIdx + 5] == (byte)'A' && localBytes [elementEndIdx + 6] == (byte)'T' && localBytes [elementEndIdx + 7] == (byte)'A') { elementEndIdx = strstr3(elementEndIdx, (byte)']', (byte)']', (byte)'>') + 3; } else { break; } } // check for end of everything if (elementEndIdx >= localBytesLength) { elementEndIdx = localBytesLength - 1; } // null terminate element end localBytes [elementEndIdx] = 0; // null terminate element start so previous element text doesnt overrun localBytes [elementStartIdx] = 0; // get element name start long elementNameStartIdx = elementStartIdx + 1; // ignore tags that start with ? or ! unless cdata "<![CDATA" if (localBytes [elementNameStartIdx] == (byte)'?' || (localBytes [elementNameStartIdx] == (byte)'!' && isCDATA != 0)) { elementStartIdx = elementEndIdx + 1; continue; } // ignore attributes/text if this is a closing element if (localBytes [elementNameStartIdx] == (byte)'/') { elementStartIdx = elementEndIdx + 1; // end of an element TBXMLElement closeElement = elementStack.Pop(); onEndElement(closeElement); closeElement.attributes.Clear(); freeElementList.Push(closeElement); if (elementStack.Count > 0) { TBXMLElement parentElement = elementStack.Peek(); if (parentElement.textIdx > 0) { // trim whitespace from start of text while (isspace(localBytes [parentElement.textIdx])) { parentElement.textIdx++; } // trim whitespace from end of text long endIdx = parentElement.textIdx + strlen(parentElement.textIdx) - 1; while (endIdx > parentElement.textIdx && isspace(localBytes [endIdx])) { localBytes [endIdx] = 0; endIdx--; } } /* * parentElement = parentXMLElement.parentElement; * * // if parent element has children clear text * if (parentXMLElement != null && parentXMLElement.firstChild != null) * parentXMLElement.textIdx = 0; */ } continue; } // is this element opening and closing bool selfClosingElement = (localBytes [elementEndIdx - 1] == '/'); // create new xmlElement struct TBXMLElement xmlElement = null; if (freeElementList.Count > 0) { xmlElement = freeElementList.Pop(); } else { xmlElement = new TBXMLElement(); xmlElement.tbxml = this; } elementStack.Push(xmlElement); // set element name xmlElement.nameIdx = elementNameStartIdx; // in the following xml the ">" is replaced with \0 by elementEnd. // element may contain no atributes and would return null while looking for element name end // <tile> // find end of element name long elementNameEndIdx = strpbrk3(xmlElement.nameIdx, (byte)' ', (byte)'/', (byte)'\n'); // if end was found check for attributes if (elementNameEndIdx < localBytesLength) { // null terminate end of elemenet name localBytes [elementNameEndIdx] = 0; long chrIdx = elementNameEndIdx; long nameIdx = 0; long valueIdx = 0; long CDATAStartIdx = 0; long CDATAEndIdx = 0; bool singleQuote = false; int mode = TBXML_ATTRIBUTE_NAME_START; // loop through all characters within element byte localChrByte; while (chrIdx++ < elementEndIdx) { localChrByte = localBytes [chrIdx]; switch (mode) { // look for start of attribute name case TBXML_ATTRIBUTE_NAME_START: if (localChrByte == ' ' || localChrByte == '\t' || localChrByte == '\n' || localChrByte == '\r' || localChrByte == '\v' || localChrByte == '\f') { continue; } nameIdx = chrIdx; mode = TBXML_ATTRIBUTE_NAME_END; break; // look for end of attribute name case TBXML_ATTRIBUTE_NAME_END: if (localChrByte == ' ' || localChrByte == '\t' || localChrByte == '\n' || localChrByte == '\r' || localChrByte == '\v' || localChrByte == '\f' || localChrByte == '=') { localBytes [chrIdx] = 0; mode = TBXML_ATTRIBUTE_VALUE_START; } break; // look for start of attribute value case TBXML_ATTRIBUTE_VALUE_START: if (localChrByte == ' ' || localChrByte == '\t' || localChrByte == '\n' || localChrByte == '\r' || localChrByte == '\v' || localChrByte == '\f') { continue; } if (localChrByte == '"' || localChrByte == '\'') { valueIdx = chrIdx + 1; mode = TBXML_ATTRIBUTE_VALUE_END; if (localChrByte == '\'') { singleQuote = true; } else { singleQuote = false; } } break; // look for end of attribute value case TBXML_ATTRIBUTE_VALUE_END: if (localChrByte == (byte)'<' && localBytes [chrIdx + 8] == (byte)'[' && localBytes [chrIdx + 1] == (byte)'!' && localBytes [chrIdx + 2] == (byte)'[' && localBytes [chrIdx + 3] == (byte)'C' && localBytes [chrIdx + 4] == (byte)'D' && localBytes [chrIdx + 5] == (byte)'A' && localBytes [chrIdx + 6] == (byte)'T' && localBytes [chrIdx + 7] == (byte)'A') { mode = TBXML_ATTRIBUTE_CDATA_END; } else if ((localChrByte == '"' && singleQuote == false) || (localChrByte == '\'' && singleQuote == true)) { localBytes [chrIdx] = 0; // remove cdata section tags while ((CDATAStartIdx = strstr9(valueIdx, (byte)'<', (byte)'!', (byte)'[', (byte)'C', (byte)'D', (byte)'A', (byte)'T', (byte)'A', (byte)'[')) < localBytesLength) { // remove begin cdata tag memcpy(CDATAStartIdx, CDATAStartIdx + 9, strlen(CDATAStartIdx) - 8); // search for end cdata CDATAEndIdx = strstr3(CDATAStartIdx, (byte)']', (byte)']', (byte)'>'); // remove end cdata tag memcpy(CDATAEndIdx, CDATAEndIdx + 3, strlen(CDATAEndIdx) - 2); } // create new attribute xmlAttribute.nameIdx = nameIdx; xmlAttribute.valueIdx = valueIdx; xmlElement.attributes.Add(xmlAttribute); // clear name and value pointers nameIdx = 0; valueIdx = 0; // start looking for next attribute mode = TBXML_ATTRIBUTE_NAME_START; } break; // look for end of cdata case TBXML_ATTRIBUTE_CDATA_END: if (localChrByte == ']') { if (localChrByte == (byte)']' && localBytes [chrIdx + 1] == (byte)']' && localBytes [chrIdx + 2] == (byte)'>') { mode = TBXML_ATTRIBUTE_VALUE_END; } } break; default: break; } } } // if tag is not self closing, set parent to current element if (!selfClosingElement) { // set text on element to element end+1 if (localBytes [elementEndIdx + 1] != '>') { xmlElement.textIdx = elementEndIdx + 1; } } onStartElement(elementStack.Peek()); if (selfClosingElement) { TBXMLElement closeElement = elementStack.Pop(); onEndElement(closeElement); closeElement.attributes.Clear(); freeElementList.Push(closeElement); } // start looking for next element after end of current element elementStartIdx = elementEndIdx + 1; } while (elementStack.Count > 0) { onEndElement(elementStack.Pop()); } }