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());
            }
        }