;[c]asm-xml.asm - Asm XML Parser ;[c] ;[c]Copyright (C) 2007-10, Marc Kerbiquet ;[c]All rights reserved. ;[c] ;[c]Redistribution and use in source and binary forms, with or without ;[c]modification, are permitted provided that the following conditions are met: ;[c] ;[c]1. Redistributions of source code must retain the above copyright notice, ;[c] this list of conditions and the following disclaimer. ;[c]2. Redistributions in binary form must reproduce the above copyright notice, ;[c] this list of conditions and the following disclaimer in the documentation ;[c] and/or other materials provided with the distribution. ;[c]3. Neither the name of AsmXml nor the names of its contributors may be ;[c] used to endorse or promote products derived from this software without ;[c] specific prior written permission. ;[c] ;[c]THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ;[c]AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ;[c]IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ;[c]DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE ;[c]FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ;[c]DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ;[c]SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ;[c]CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ;[c]OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ;[c]OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ;[c] ;[c] ;[c]Open this file with Code Browser (http://code-browser.sourceforge.net/) ;[c]to view this long flat file as a structured text file. ;[c] ;[c]This is source code for fasm. ;[c] ;[c]Conventions: ;[c]Preserved registers: ebx esi edi ebp ;[c]Scratch registers: eax ecx edx ;[c](same as Windows/Linux/BSD) ;[c] ;[of]:Macros macro struct name { virtual at 0 label name } macro ends { .sizeof rd 0 end virtual } ;[cf] ;[of]:Definitions struct MemoryContext .base rd 1 ; memory buffer .limit rd 1 ; limit of memory buffer .chunks rd 1 ; list of chunks .chunkSize rd 1 ; default size of chunks ends ;[c] struct ParseContext .base rd 1 ; memory buffer .limit rd 1 ; limit of memory buffer .chunks rd 1 ; list of chunks .chunkSize rd 1 ; default size of chunks .errorCode rd 1 .source rd 1 ; address of the first char .current rd 1 ; address of the last read char .line rd 1 ; line number of the last read char .column rd 1 ; column of the last read char .root rd 1 ; root element .version rd 2 ; version info (if any) .encoding rd 2 ; encoding (if any) .strict rd 1 ; 0 if not strict .doctype rd 1 ; can read doctype .invalidElement rb Element.sizeof ends ;[c] struct CreateClassContext .base rd 1 ; memory buffer .limit rd 1 ; limit of memory buffer .chunks rd 1 ; list of chunks .chunkSize rd 1 ; default size of chunks .errorCode rd 1 .line rd 1 ; line number of the last read char .column rd 1 ; column of the last read char .classes rd 1 ; the list of global classes .rootClass rd 1 ; the root class .rootElement rd 1 ; the root element ends ;[c] struct ElementClass .offset rd 1 ; Offset of element in parent element ; (0 when it is a collection) .name rd 1 ; name of the element .nameLimit rd 1 ; limit of the name .size rd 1 ; size to store the element .id rd 1 ; id .type rd 1 ; container, text or mixed .propertyCount rd 1 ; number of attributes + text elements .childCount rd 1 ; number of child classes .attributes rd 1 ; list of attributes .elements rd 1 ; list of elements .children rd 1 ; list of child classes ; The order is the one defined in the class ; definition file. .shared rb 1 ; .status rb 1 ; 0-unsolved 1-resolving 2-resolved rb 2 ; padding .source rd 1 ; source element ends ;[c] struct TextElementClass .offset rd 1 ; Offset of attribute in element ; (the text element is stored as an attribute) .begin rd 1 ; element name (begin) .limit rd 1 ; element name (last char+1) ends ;[c] struct Element .id rd 1 ; id of the element .nextSibling rd 1 .firstChild rd 1 .lastChild rd 1 .reserved rd 2 ; store ignored attributes here ends ;[c] ;[c] struct Attribute .begin rd 1 .limit rd 1 ends ;[c] ;[c] EC_TEXT equ 1 EC_TEXT_COLL equ 2 ;[c] ;[c]Error Codes ;[c] RC_OK equ 0 ; everything is ok RC_MEMORY equ 1 ; out of memory ; Schema RC_EMPTY_NAME equ 10 ; name empty or not defined RC_ATTR_DEFINED equ 11 ; attribute already defined RC_ELEM_DEFINED equ 12 ; element already defined RC_SCHEMA_EMPTY equ 13 ; schema does not contains a document RC_DOCUMENT_DEFINED equ 14 ; schema contains more than one document RC_UNDEFINED_CLASS equ 15 ; can't find collection in reference RC_UNDEFINED_GROUP equ 16 ; can't find a group in include RC_INVALID_ID equ 17 ; id is not a valid number RC_INVALID_IGNORE equ 18 ; ignore is not 'yes' or 'no' RC_RECURSIVE_ELEMENT equ 19 ; element cannot reference itself ; Parsing RC_INVALID_ENTITY_REFERENCE equ 20 RC_UNEXPECTED_END equ 21 RC_INVALID_CHAR equ 22 RC_OVERFLOW equ 23 RC_NO_START_TAG equ 24 RC_TAG_MISMATCH equ 25 RC_INVALID_TAG equ 26 RC_INVALID_ATTRIBUTE equ 27 RC_INVALID_PI equ 28 RC_INVALID_DOCTYPE equ 29 RC_VERSION_EXPECTED equ 30 ;[c] ;[c]Various constants ;[c] TAB_SIZE equ 8 DEFAULT_CHUNK_SIZE equ 1024*1024 HEADER_SIZE equ 16 ; reserved space for chunks ;[c] TYPE_CONTAINER equ 0 TYPE_TEXT equ 1 TYPE_MIXED equ 2 ;[c] ATTR_NAME equ 0 ATTR_IGNORE equ 1 ATTR_TYPE equ 2 ATTR_ID equ 3 ;[c] ;[c]Class Ids ;[c] schemaId equ 0 documentId equ 1 attributeId equ 2 textId equ 3 collectionId equ 4 elementId equ 5 referenceId equ 6 includeId equ 7 groupId equ 8 ;[c] ;[c]Resolution ;[c] UNRESOLVED equ 0 RESOLVING equ 1 RESOLVED equ 2 ;[c] ;[c]Atoms (for DOCTYPE) ;[c] ATOM_DOCTYPE equ 0 ATOM_ELEMENT equ 1 ATOM_ATTLIST equ 2 ATOM_PCDATA equ 3 ATOM_IMPLIED equ 4 ATOM_REQUIRED equ 5 ATOM_FIXED equ 6 ATOM_NDATA equ 7 ATOM_CDATA equ 8 ATOM_ID equ 9 ATOM_IDREF equ 10 ATOM_IDREFS equ 11 ATOM_ENTITY equ 12 ATOM_ENTITIES equ 13 ATOM_NMTOKEN equ 14 ATOM_NMTOKENS equ 15 ATOM_NOTATION equ 16 ATOM_VERSION equ 17 ATOM_ENCODING equ 18 ATOM_STANDALONE equ 19 ;[cf] ;[of]:Documentation ;[c] ;[c]Element Map ;[c] ;[c] An element map is an array of 256 values each value correspond to ;[c] an action to take corresponding to the current read char: ;[c] * 1 ;[c] The character is invalid in the current state ;[c] ;[c] * Address % 4 = 0 ;[c] The address of the element map for the next char ;[c] ;[c] * Address % 4 = 1 ;[c] The address of the element class found preceded by the ;[c] remaining chars and the type of class ;[c] e.g. ;[c] "bcd", 0, t, padding, ... ;[c] * t = 0 ;[c] It is a collection, the pointer to the element class ;[c] is at the next 4 byte align. ;[c] * t = 1 ;[c] It is a text element, the text element class is stored ;[c] after the padding. ;[c] ;[c] * Address % 4 = 3 ;[c] Same as previous but all chars have already been read. ;[c] It is used when a name is the prefix of another name. ;[c] e.g.: ;[c] ;[c] ;[c] ;[c] ;[c] ;[c] ;[cf] ;[of]:C Stubs ;[of]:initialize ;[c]Initialize the library ;[c] ;[c] void ax_initialize(malloc, free) ;[c] ;[c]ARGUMENTS ;[c] malloc ;[c] the memory alllocation function ;[c] free ;[c] the free memory function ;[c] ;[c] _initialize: push ebx mov eax,[esp+8+0] mov ebx,[esp+8+4] call initialize pop ebx ret ;[cf] ;[of]:initializeParser ;[c]Initialize the parse context ;[c] ;[c] errorCode = ax_initializeParser(context, chunkSize) ;[c] ;[c]ARGUMENTS ;[c] context ;[c] the parse context. ;[c] chunkSize ;[c] the default size of chunk ;[c] ;[c]RETURN VALUE ;[c] The error code ;[c] _initializeParser: push ebp mov ebp,[esp+8+0] mov ecx,[esp+8+4] call initializeParser pop ebp ret ;[cf] ;[of]:releaseParser ;[c]Release the parse context ;[c] ;[c] ax_releaseParser(context) ;[c] ;[c]ARGUMENTS ;[c] context ;[c] the parse context. The memory base and memory limit must be ;[c] initialized. ;[c] _releaseParser: push ebp mov ebp,[esp+8+0] call releaseParser pop ebp ret ;[cf] ;[of]:parse ;[c]Parse an XML string ;[c] ;[c] Element* ax_parse(context, source, type, strict) ;[c] ;[c]ARGUMENTS ;[c] context ;[c] the parse context. The object must have been initialized. ;[c] source ;[c] the xml to parse ;[c] type ;[c] the expected type of element to parse ;[c] ;[c]RETURN VALUE ;[c] The created element or null if an error occured. ;[c] _parse: push ebx push esi push edi push ebp mov ebp,[esp+20+ 0] mov esi,[esp+20+ 4] mov edx,[esp+20+ 8] mov edi,[esp+20+12] call parse mov [ebp+ParseContext.current],esi mov [ebp+ParseContext.errorCode],eax jz .ok call computeLineColumn xor eax,eax jmp .error .ok: mov eax,[ebp+ParseContext.root] .error: pop ebp pop edi pop esi pop ebx ret ;[c] ;[cf] ;[c] ;[of]:initializeClassParser ;[c]Initialize the class parser ;[c] ;[c] errorCode = ax_initializeClassParser(context) ;[c] ;[c]ARGUMENTS ;[c] context ;[c] the class parser. ;[c] ;[c]RETURN VALUE ;[c] The error code ;[c] _initializeClassParser: push ebp mov ebp,[esp+8+0] call initializeClassParser pop ebp ret ;[cf] ;[of]:releaseClassParser ;[c]Release the class parser ;[c] ;[c] ax_releaseClassParser(context) ;[c] ;[c]ARGUMENTS ;[c] context ;[c] the class parser. ;[c] _releaseClassParser: push ebp mov ebp,[esp+8+0] call releaseClassParser pop ebp ret ;[cf] ;[of]:classFromElement ;[c]Create a class from an element ;[c] ;[c] ElementClass* ax_classFromElement(element, context) ;[c] ;[c]ARGUMENTS ;[c] element ;[c] the xml element describing the class ;[c] context ;[c] the class context. ;[c] ;[c]RETURN VALUE ;[c] The created class or null if an error occured. ;[c] _classFromElement: push esi push edi push ebp mov esi,[esp+16] mov ebp,[esp+16+4] call classFromElement mov [ebp+CreateClassContext.errorCode],eax jnz .failed mov eax,[ebp+CreateClassContext.rootClass] .error: pop ebp pop edi pop esi ret .failed: xor eax,eax jmp .error ;[c] ;[cf] ;[of]:classFromString ;[c]Create a class from a string ;[c] ;[c] ElementClass* ax_classFromString(string, context) ;[c] ;[c]ARGUMENTS ;[c] string ;[c] the xml element describing the class ;[c] context ;[c] the class context. ;[c] ;[c]RETURN VALUE ;[c] The created class or null if an error occured. ;[c] _classFromString: push ebx push esi push edi push ebp mov esi,[esp+20+0] mov ebp,[esp+20+4] call classFromString mov [ebp+CreateClassContext.errorCode],eax jnz .failed mov eax,[ebp+CreateClassContext.rootClass] .error: pop ebp pop edi pop esi pop ebx ret .failed: xor eax,eax jmp .error ;[cf] ;[cf] ;[of]:Functions ;[of]:initialize ;[c]Initialize the library ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] the allocator function ;[c] ebx ;[c] the free memory function ;[c] initialize: mov [mallocFunction],eax mov [freeFunction],ebx ret ;[cf] ;[of]:initializeParser ;[c]Initialize the parse context object ;[c] ;[c]ARGUMENTS ;[c] ebp ;[c] the parse context ;[c] ecx ;[c] chunk size ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] initializeParser: xor eax,eax mov [ebp+ParseContext.chunkSize],ecx mov [ebp+ParseContext.chunks],eax mov eax,ecx jmp newChunk ;[cf] ;[of]:releaseParser ;[c]Release the parse context object ;[c] ;[c]ARGUMENTS ;[c] ebp ;[c] the parse context ;[c] releaseParser: jmp releaseChunks ;[cf] ;[of]:parse ;[c]parse ;[c] ;[c]ARGUMENTS ;[c] edx ;[c] the type of the root element ;[c] esi ;[c] source ;[c] edi ;[c] strict flag ;[c] ebp ;[c] Parse context ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] esi ;[c] Address of the first byte after the element. ;[c] edi ;[c] ??? ;[c] parse: mov [ebp+ParseContext.source],esi mov [ebp+ParseContext.strict],edi mov [ebp+ParseContext.doctype],1 sub eax,eax mov [ebp+ParseContext.version+Attribute.begin],eax mov [ebp+ParseContext.version+Attribute.limit],eax mov [ebp+ParseContext.encoding+Attribute.begin],eax mov [ebp+ParseContext.encoding+Attribute.limit],eax ; Allocate memory for root element mov edi,[ebp+ParseContext.base] mov eax,[ebp+ParseContext.limit] mov ebx,edi add ebx,[edx+ElementClass.size] cmp ebx,eax jae parseChunkFull mov [ebp+ParseContext.base],ebx mov [ebp+ParseContext.root],edi resumeParseChunkFull: ; Clear higher bits of eax ; All the functions assume that the high bits of eax ; are always zero. xor eax,eax call readProlog jnz return call readRootElement jnz return ; forbid the doctype mov [ebp+ParseContext.doctype],0 call readMisc jnz return ; error if there is remaining chars or al,al jnz invalidChar xor eax,eax ret ;[c] ;[c]Sub-functions ;[c] ;[of]: readRootElement readRootElement: cmp al,'<' jnz notStartTag ;[of]: Initialize Element mov eax,[edx+ElementClass.id] mov [edi+Element.id],eax xor eax,eax mov [edi+Element.firstChild],eax mov [edi+Element.lastChild],eax mov [edi+Element.nextSibling],eax mov ecx,[edx+ElementClass.propertyCount] lea ebx,[edi+Element.sizeof] or ecx,ecx jz .m0 shr ecx,1 jnc .m2 mov [ebx+Attribute.begin],eax mov [ebx+Attribute.limit],eax jz .m0 add ebx,Attribute.sizeof .m2: shr ecx,1 jnc .m4 mov [ebx+Attribute.begin],eax mov [ebx+Attribute.limit],eax mov [ebx+Attribute.sizeof+Attribute.begin],eax mov [ebx+Attribute.sizeof+Attribute.limit],eax jz .m0 add ebx,Attribute.sizeof*2 .m4: mov [ebx+Attribute.sizeof*0+Attribute.begin],eax mov [ebx+Attribute.sizeof*0+Attribute.limit],eax mov [ebx+Attribute.sizeof*1+Attribute.begin],eax mov [ebx+Attribute.sizeof*1+Attribute.limit],eax mov [ebx+Attribute.sizeof*2+Attribute.begin],eax mov [ebx+Attribute.sizeof*2+Attribute.limit],eax mov [ebx+Attribute.sizeof*3+Attribute.begin],eax mov [ebx+Attribute.sizeof*3+Attribute.limit],eax add ebx,Attribute.sizeof*4 dec ecx jnz .m4 .m0: ;[cf] ;[of]: Read Element Name ;[c]Read Element Name ;[c] mov ebx,edi mov edi,[edx+ElementClass.name] mov ecx,[edx+ElementClass.nameLimit] .loop: cmpsb jnz invalidTag cmp edi,ecx jnz .loop mov edi,ebx movzx eax,byte [esi] add esi,1 cmp al,'>' jz readContent cmp al, '/' jz endOfElement test byte [S + eax], 1 jz invalidChar ;[cf] ;[of]: Read Attributes ;[c]Read Attributes ;[c] ;[c]Read the next attribute (skip blanks first) ;[c] nextAttribute: ; Skip blanks .sb: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .sb ;[c] ;[c]Read the next attribute (blanks already skipped, first char already read) ;[c] readAttributes: ; No more attributes ? cmp al, '>' jz readContentOrText cmp al, '/' jz endOfElement ;[c] ;[c]Read and Decode Attribute Name ;[c] readAttribute: mov ebx,[edx+ElementClass.attributes] mov ecx,[ebx+eax*4] ; first char already loaded test ecx,1 jnz .j2 .l2: movzx eax,byte [esi] mov ecx,[ecx+eax*4] add esi,1 test ecx,1 jz .l2 test ecx,2 jnz terminalAttribute .j2: sub ecx,1 jz invalidAttribute align 4 .l21: movzx eax,byte [esi] add ecx,1 add esi,1 cmp [ecx-1],al jz .l21 test byte [ecx-1],255 jnz invalidAttribute commonAttribute: mov ebx,edi add ebx,[ecx] ;[c] ;[c]Skip Blanks and Equal Sign ;[c] cmp al,'=' ; I'm feeling lucky jz readAttributeValue ; test byte [S + eax], 1 ; Not '=', it MUST be a blank jz invalidAttribute ; or it is an invalid attribute .skipBlank2: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .skipBlank2 cmp al,'=' jnz invalidChar readAttributeValue: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz readAttributeValue cmp al,$27 ; ' jz .value cmp al,'"' jnz invalidChar .value: mov cl,al ;[c] ;[c]Read and Store Value ;[c] cmp [ebx+Attribute.begin],0 jnz attributeDefinedOrIgnored resumeBecauseIgnored: mov [ebx+Attribute.begin],esi mov ch,'&' .l3: mov al,[esi] add esi,1 ;test byte [RAC + eax], 1 ; slower but it would allow ;jz .l3 ; detection of < in attribute ;cmp al,'<' ; makes the parser 5% slower ;jz invalidChar ; cmp al,ch jz attributeReference or al,al jz unexpectedEnd cmp al,cl jnz .l3 lea ecx,[esi-1] mov [ebx+Attribute.limit],ecx ;[c] endOfAttribute: ; Read next char movzx eax,byte [esi] add esi,1 ; Is it a blank ? test byte [S + eax], 1 jnz nextAttribute ; Is it a closing tag ? cmp al, '>' jz readContentOrText ; Is it not the end of the element ? cmp al, '/' jnz readAttribute endOfElement: movzx eax,byte [esi] add esi,1 cmp al,'>' jnz invalidChar xor eax,eax ret ;[c] terminalAttribute: and ecx,not 2 jmp commonAttribute ;[c] attributeDefinedOrIgnored: mov eax,ebx sub eax,edi cmp eax,Element.reserved jz resumeBecauseIgnored jmp attributeDefined ;[cf] ;[of]: Read Content ;[c]Read Content ;[c] readContentOrText: ; test if the content is just text cmp [edx+ElementClass.type],TYPE_TEXT jz readInnerText ;[c] readContent: cmp [edx+ElementClass.type],TYPE_MIXED jz readPCDATA mov bl,'<' .loop: mov al,[esi] or al,al jz unexpectedEnd add esi,1 cmp al,bl jnz .loop mov al,[esi] cmp al, '/' jnz referenceOrComment ;[of]:Read Close Tag readCloseTag: add esi,1 mov edi,[edx+ElementClass.name] mov ecx,[edx+ElementClass.nameLimit] .loop: cmpsb jnz closeTagMismatch cmp edi,ecx jnz .loop movzx eax,byte [esi] add esi,1 cmp al, '>' ; I'm feeling lucky jnz .skipBlanks4 ; xor eax,eax ret .skipBlanks4: test byte [S + eax], 1 jz invalidChar .skipBlank4: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .skipBlank4 cmp al, '>' jnz invalidChar xor eax,eax ; element successfully read ret ;[cf] referenceOrComment: cmp al,'!' jz skipCommentOrCDATA cmp al,'?' jz skipContentPI ;[of]:Read Child Element ;[c]Read a child element (may be a text-only element) ;[c] readChild: add esi,1 ;[c] ;[c]Decode Element Name ;[c] mov ebx,[edx+ElementClass.elements] mov ecx,[ebx+eax*4] ; first char already loaded test ecx,1 jnz .skip .loop: movzx eax,byte [esi] mov ecx,[ecx+eax*4] add esi,1 test ecx,1 jz .loop .skip: test ecx,2 jnz terminalElement dec ecx jz invalidElement .loop2: movzx eax,byte [esi] add ecx,1 add esi,1 cmp [ecx-1],al jz .loop2 test byte [ecx-1],255 jnz invalidElement mov bl,[ecx] ;[of]:Skip Blanks test byte [S + eax], 1 jz .eNotBlank1 .eSkipBlank1: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .eSkipBlank1 .eNotBlank1: ;[cf] add ecx,3+1 and ecx,$FFFFFFFC test bl,1 jz readChildElement ;[c] ;[of]:Read Text ;[c]Read a Text-Only Element ;[c] ;[c] * esi is the source ;[c] * edi is the element ;[c] ;[c] * ecx is the text element descriptor ;[c] * al is the last non blank character in the tag (must be '>') ;[c] readText: mov ebx,edi add ebx,[ecx+TextElementClass.offset] cmp al,'/' jz emptyText cmp al,'>' jnz invalidChar mov [ebx+Attribute.begin],esi movzx eax,byte [esi] add esi,1 cmp al,'<' jz emptyOrCDATAOrPI or al,al jz unexpectedEnd cmp al,'&' jz textReference .loop: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,'&' jz textReference cmp al,'<' jnz .loop movzx eax,byte [esi] cmp al,'!' jz textCommentOrCDATA cmp al,'?' jz textPI endOfEmptyText: sub esi,1 mov [ebx+Attribute.limit],esi add esi,1 endOfText: add esi,1 cmp al, '/' jnz invalidChar mov ebx,edi mov edi,[ecx+TextElementClass.begin] mov ecx,[ecx+TextElementClass.limit] .loop: cmpsb jnz tagMismatch cmp edi,ecx jnz .loop mov edi,ebx .eSkipBlank2: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .eSkipBlank2 cmp al,'>' jz readContent jmp invalidChar ;[c] emptyOrCDATAOrPI: movzx eax,byte [esi] cmp al,'?' jz textPI cmp al,'!' jnz endOfEmptyText cmp byte [esi+1],'[' jnz invalidCharOrComment cmp byte [esi+2],'C' jnz invalidChar cmp byte [esi+3],'D' jnz invalidChar cmp byte [esi+4],'A' jnz invalidChar cmp byte [esi+5],'T' jnz invalidChar cmp byte [esi+6],'A' jnz invalidChar cmp byte [esi+7],'[' jnz invalidChar add esi,8 mov [ebx+Attribute.begin],esi .loop: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,']' jnz .loop movzx eax,byte [esi] cmp al,']' jnz .loop mov al,[esi+1] cmp al,'>' jnz .loop mov al,[esi+2] cmp al,'<' jnz .mixed sub esi,1 mov [ebx+Attribute.limit],esi add esi,4 movzx eax,byte [esi] jmp endOfText ;[c] ;[c]The text starts with a CDATA section but contain something else, ;[c]let 's read it again with the slow method ;[c] .mixed: mov esi,[ebx+Attribute.begin] sub esi,9 ; size("' jnz invalidChar mov [ebx+Attribute.begin],esi ; any non null ptr is ok mov [ebx+Attribute.limit],esi jmp readContent ;[cf] ;[of]:Read a Child Element ;[c]Read a Child Element ;[c] ;[c] * esi source ;[c] * edi parent element ;[c] * ecx the element description in parent ;[c] * edx type of parent ;[c] readChildElement: push edx mov edx,[ecx] ; Allocate memory mov ecx,[ebp+ParseContext.base] mov ebx,[edx+ElementClass.size] add ebx,ecx cmp ebx,[ebp+ParseContext.limit] jae .chunkFull mov [ebp+ParseContext.base],ebx .resume: ; It is an element, not a collection: ; it must be store in an attribute slot instead of ; in the list of children. ; This case should be exceptional, it is placed ; out of the main flow to avoid breaking the pipeline cmp [edx+ElementClass.offset],0 jnz .element ; Append element cmp dword [edi+Element.firstChild],0 jnz .rce1 mov [edi+Element.firstChild],ecx .rce1: mov ebx,[edi+Element.lastChild] or ebx,ebx jz .rce2 mov [ebx+Element.nextSibling],ecx .rce2: mov [edi+Element.lastChild],ecx .resume2: push edi mov edi,ecx ;[of]: Initialize Element push eax mov eax,[edx+ElementClass.id] mov [edi+Element.id],eax xor eax,eax mov [edi+Element.firstChild],eax mov [edi+Element.lastChild],eax mov [edi+Element.nextSibling],eax mov ecx,[edx+ElementClass.propertyCount] lea ebx,[edi+Element.sizeof] or ecx,ecx jz .xm0 shr ecx,1 jnc .xm2 mov [ebx+Attribute.begin],eax mov [ebx+Attribute.limit],eax jz .xm0 add ebx,Attribute.sizeof .xm2: shr ecx,1 jnc .xm4 mov [ebx+Attribute.begin],eax mov [ebx+Attribute.limit],eax mov [ebx+Attribute.sizeof+Attribute.begin],eax mov [ebx+Attribute.sizeof+Attribute.limit],eax jz .xm0 add ebx,Attribute.sizeof*2 .xm4: mov [ebx+Attribute.sizeof*0+Attribute.begin],eax mov [ebx+Attribute.sizeof*0+Attribute.limit],eax mov [ebx+Attribute.sizeof*1+Attribute.begin],eax mov [ebx+Attribute.sizeof*1+Attribute.limit],eax mov [ebx+Attribute.sizeof*2+Attribute.begin],eax mov [ebx+Attribute.sizeof*2+Attribute.limit],eax mov [ebx+Attribute.sizeof*3+Attribute.begin],eax mov [ebx+Attribute.sizeof*3+Attribute.limit],eax add ebx,Attribute.sizeof*4 dec ecx jnz .xm4 .xm0: pop eax ;[cf] call readAttributes pop edi pop edx jz readContent ret ;[c] .element: mov ebx,[edx+ElementClass.offset] cmp [edi+ebx+Attribute.begin],0 jnz .elementDefined mov [edi+ebx+Attribute.begin],ecx jmp .resume2 .elementDefined: pop edx jmp elementDefined ;[c] .chunkFull: push eax push edx push edi mov eax,[edx+ElementClass.size] call extendMemory mov ecx,edi pop edi pop edx pop eax jz .resume pop edx jmp parseOutOfMemory ;[cf] ;[c] terminalElement: add ecx,1 mov bl,[ecx-3] ;[of]:Skip Blanks test byte [S + eax], 1 jz .eNotBlank1 .eSkipBlank1: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .eSkipBlank1 .eNotBlank1: ;[cf] test bl,1 jz readChildElement jmp readText ;[cf] ;[of]:Skip comment skipCommentOrCDATA: add esi,1 movzx eax,byte [esi] add esi,1 cmp al,'-' jz .comment ;[c] .CDATA: cmp al,'[' jnz invalidChar cmp byte [esi],'C' jnz invalidChar cmp byte [esi+1],'D' jnz invalidChar cmp byte [esi+2],'A' jnz invalidChar cmp byte [esi+3],'T' jnz invalidChar cmp byte [esi+4],'A' jnz invalidChar cmp byte [esi+5],'[' jnz invalidChar add esi,6 .cd1: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,']' jnz .cd1 movzx eax,byte [esi] cmp al,']' jnz .cd1 mov al,[esi+1] cmp al,'>' jnz .cd1 add esi,2 jmp readContent ;[c] .comment: call readComment jnz return jmp readContent ;[cf] ;[of]:Skip processing instruction skipContentPI: call readPI jnz return jmp readContent ;[cf] ;[c] ;[of]:Read Inner Text ;[c]Read a Text-Only Element ;[c] ;[c] * esi is the source ;[c] * edi is the element ;[c] * edx is the type of the element ;[c] ;[c] * al is the last non blank character in the tag (must be '>') ;[c] readInnerText: lea ebx,[edi+Element.sizeof] mov [ebx+Attribute.begin],esi mov ecx,'&' * 256 + '<' movzx eax,byte [esi] add esi,1 cmp al,cl jz innerEmptyOrCDATAOrPI or al,al jz unexpectedEnd cmp al,ch jz innerTextReference .loop: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,ch jz innerTextReference cmp al,cl jnz .loop movzx eax,byte [esi] cmp al,'!' jz innerTextCommentOrCDATA cmp al,'?' jz innerPI innerEndOfEmptyText: dec esi mov [ebx+Attribute.limit],esi inc esi innerEndOfText: cmp al, '/' jnz invalidChar jmp readCloseTag ;[c] innerEmptyOrCDATAOrPI: movzx eax,byte [esi] cmp al,'?' jz innerPI cmp al,'!' jnz innerEndOfEmptyText cmp byte [esi+1],'[' jnz innerInvalidCharOrComment cmp byte [esi+2],'C' jnz invalidChar cmp byte [esi+3],'D' jnz invalidChar cmp byte [esi+4],'A' jnz invalidChar cmp byte [esi+5],'T' jnz invalidChar cmp byte [esi+6],'A' jnz invalidChar cmp byte [esi+7],'[' jnz invalidChar add esi,8 mov [ebx+Attribute.begin],esi .loop: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,']' jnz .loop movzx eax,byte [esi] cmp al,']' jnz .loop mov al,[esi+1] cmp al,'>' jnz .loop mov al,[esi+2] cmp al,'<' jnz .mixed sub esi,1 mov [ebx+Attribute.limit],esi add esi,4 movzx eax,byte [esi] jmp innerEndOfText ;[c] ;[c]The text starts with a CDATA section but contain something else, ;[c]let 's read it again with the slow method ;[c] .mixed: mov esi,[ebx+Attribute.begin] sub esi,9 ; size("' jnz .cd1 add esi,2 jmp .decodeContent .cd3: call .realloc jmp .cd2 ;[cf] ;[of]:Skip Comments .comment: call readComment jnz .error jmp .decodeContent ;[cf] ;[of]:Skip PI .PI: call readPI jnz .error jmp .decodeContent ;[cf] ;[cf] ;[of]: decodeReference decodeReference: movzx eax,byte [esi] cmp al,'#' jz .num cmp al,'a' jz .a cmp al,'l' jz .l cmp al,'g' jz .g cmp al,'q' jnz .invalidEntityReference ; " .q: cmp byte [esi+1],'u' jnz .invalidEntityReference cmp byte [esi+2],'o' jnz .invalidEntityReference cmp byte [esi+3],'t' jnz .invalidEntityReference cmp byte [esi+4],';' jnz .invalidEntityReference add esi,5 mov al,'"' ret .num: add esi,1 xor edx,edx movzx eax,byte [esi] add esi,1 cmp al,'x' jz .hexa test byte [digit+eax],1 jz .invalidChar .nextDigit: sub al,'0' imul edx,10 jc .overflow add edx,eax jc .overflow movzx eax,byte [esi] add esi,1 test byte [digit+eax],1 jnz .nextDigit cmp al,';' jnz .invalidEntityReference mov eax,edx test edx,$FFFFFF00 jnz .invalidChar ret .hexa: movzx eax,byte [esi] add esi,1 test byte [digit+eax],2 jz .invalidChar .nextXDigit: shl edx,4 jc .overflow or dl,[xdigit+eax] movzx eax,byte [esi] add esi,1 test byte [digit+eax],2 jnz .nextXDigit cmp al,';' jnz .invalidEntityReference mov eax,edx test edx,$FFFFFF00 jnz .invalidChar ret .a: mov al,[esi+1] cmp al,'p' jz .ap cmp al,'m' jnz .invalidEntityReference ; & .am: cmp byte [esi+2],'p' jnz .invalidEntityReference cmp byte [esi+3],';' jnz .invalidEntityReference add esi,4 mov al,'&' ret ; ' .ap: cmp byte [esi+2],'o' jnz .invalidEntityReference cmp byte [esi+3],'s' jnz .invalidEntityReference cmp byte [esi+4],';' jnz .invalidEntityReference add esi,5 mov al,$27 ret ; < .l: cmp byte [esi+1],'t' jnz .invalidEntityReference cmp byte [esi+2],';' jnz .invalidEntityReference add esi,3 mov al,'<' ret ; > .g: cmp byte [esi+1],'t' jnz .invalidEntityReference cmp byte [esi+2],';' jnz .invalidEntityReference add esi,3 mov al,'>' ret ;[c] .invalidChar: add esp,4 pop edi pop edx pop ecx jmp invalidChar ;[c] .overflow: add esp,4 pop edi pop edx pop ecx jmp overflow ;[c] .invalidEntityReference: add esp,4 pop edi pop edx pop ecx jmp invalidEntityReference ;[cf] ;[of]: readProlog ;[c]Read the prolog ;[c] ;[c] The time is not critical here, so the code is less optimized: ;[c] - use of subfunctions ;[c] - return error code (instead of direct exit) ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] source ;[c] ;[c]RETURN VALUES ;[c] zf+eax ;[c] error code (zf=0) or current char (zf=1) ;[c] esi ;[c] next source char ;[c] readProlog: call readXMLDecl jnz return jmp readMisc ;[cf] ;[of]: readXMLDecl ;[c]Read the XML declaration ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] next char to read ;[c] ;[c]RETURN VALUES ;[c] zf+eax ;[c] error code ;[c] esi ;[c] next char to read ;[c] ;[c]REMARKS ;[c] If there is no xml-decl, the function just returns the same pointer ;[c] and the ok flag. ;[c] struct XDA .value rd 2 ends readXMLDecl: push edi sub esp,XDA.sizeof mov edi,esp call .goon lea esp,[esp+XDA.sizeof] pop edi ret .goon: cmp byte [esi],'<' jnz ok cmp byte [esi+1],'?' jnz ok cmp byte [esi+2],'x' jnz ok cmp byte [esi+3],'m' jnz ok cmp byte [esi+4],'l' jnz ok add esi,5 ; Read version ; ------------ call readNextChar call readBlanks jnz return call readAtom jnz return cmp ecx,ATOM_VERSION jnz noVersion call readXMLDeclValue jnz return mov ecx,[edi+XDA.value+Attribute.begin] mov [ebp+ParseContext.version+Attribute.begin],ecx mov ecx,[edi+XDA.value+Attribute.limit] mov [ebp+ParseContext.version+Attribute.limit],ecx ; Read encoding ; ------------- call isXMLDeclEnd jz ok call readBlanks jnz return call isXMLDeclEnd jz return call readAtom jnz return cmp ecx,ATOM_ENCODING jnz .notEncoding call readXMLDeclValue jnz return mov ecx,[edi+XDA.value+Attribute.begin] mov [ebp+ParseContext.encoding+Attribute.begin],ecx mov ecx,[edi+XDA.value+Attribute.limit] mov [ebp+ParseContext.encoding+Attribute.limit],ecx ; Read standalone ; --------------- call isXMLDeclEnd jz ok call readBlanks jnz return call isXMLDeclEnd jz ok call readAtom jnz return .notEncoding: cmp ecx,ATOM_STANDALONE jnz invalidChar call readXMLDeclValue jnz return ; Terminate ; --------- call isXMLDeclEnd jz ok jmp invalidChar ;[of]:readXMLDeclValue readXMLDeclValue: ; Skip blanks ; ----------- call skipBlanks ; Skip equals ; ----------- cmp al,'=' jnz invalidChar call readNextChar ; Skip blanks ; ----------- call skipBlanks ; Skip quote ; ---------- cmp al,$27 ; ' jz .value cmp al,'"' jnz invalidChar .value: mov cl,al mov [edi+XDA.value+Attribute.begin],esi .loopValue: movzx eax,byte [esi] add esi,1 or al,al jz unexpectedEnd cmp al,' ' ; Let's forbid blanks jz invalidChar ; cmp al,cl jnz .loopValue lea ecx,[esi-1] mov [edi+XDA.value+Attribute.limit],ecx jmp readNextChar ;[cf] ;[of]:isXMLDeclEnd isXMLDeclEnd: cmp al,'?' jnz return cmp byte [esi],'>' jnz return lea esi,[esi+1] ret ;[cf] ;[cf] ;[of]: readMisc ;[c]Read stuff before or after the root element ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] next char to read ;[c] ;[c]RETURN VALUES ;[c] zf+eax ;[c] error code (zf=0) or current char (zf=1) ;[c] esi ;[c] next char to read ;[c] readMisc: ; Skip blanks .sb: movzx eax,byte [esi] add esi,1 test byte [S + eax], 1 jnz .sb cmp al,'<' jnz .done cmp byte [esi],'?' jnz .notPI call readPI jmp .next .notPI: cmp byte [esi],'!' jnz .done add esi,1 movzx eax,byte [esi] add esi,1 cmp al,'-' jnz .notComment call readComment jmp .next .notComment: call readDOCTYPE .next: jnz return jmp .sb .done: test eax,0 ret ;[cf] ;[of]: readComment ;[c]Read a comment ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] points to the 2nd '-' of Node ; | |next ; |next ; | ; | succ ; Node ---------> Node ; | ; |next ; | ; | succ ; Node ---------> Node ; | ; |next ; . ; . ;[cf] ;[of]:Definitions struct Node .next rd 1 .succ rd 1 .char rb 1 .shared rb 1 rb 2 .index rd 1 .collectionClass rd 1 .nameBegin rd 1 .nameLimit rd 1 ends ;[cf] ;[c] ;[of]:findOrCreateNode ;[c]Find or create a node ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] the parent node or nil ;[c] al ;[c] the char ;[c] ;[c]RETURN VALUES ;[c] zf ;[c] 0 if error ;[c] eax ;[c] error code ;[c] esi ;[c] the new parent node ;[c] edi ;[c] the found or created node ;[c] ;[c]REMARKS ;[c] If the node already exists, the shared flag is set. ;[c] findOrCreateNode: call findNode jz .notFound mov [edi+Node.shared],1 jmp .ok ; Allocate a node .notFound: push eax call allocateNode pop edx jnz .error ; Append node call appendNode ; Initialize node xor eax,eax mov [edi+Node.next],eax mov [edi+Node.succ],eax mov [edi+Node.char],dl mov [edi+Node.shared],al mov [edi+Node.index],-1 mov [edi+Node.collectionClass],eax .ok: xor eax,eax .error: ret ;[cf] ;[of]:allocateNode ;[c]Allocates a node ;[c] ;[c]ARGUMENTS ;[c] ebp ;[c] the tree builder local variables ;[c] ;[c]RETURN VALUES ;[c] zf ;[c] 0 if not enough memory ;[c] eax ;[c] error code ;[c] edi ;[c] the node ;[c] allocateNode: mov ecx,4 mov eax,Node.sizeof mov edx,[ebp+TreeBuilder.context] jmp allocateMemory ;[cf] ;[of]:appendNode ;[c]Append a node ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] parent node ;[c] edi ;[c] node to append to parent ;[c] ;[c]RETURN VALUES ;[c] esi ;[c] parent node ;[c] edi ;[c] node to append ;[c] ;[c]REMARKS ;[c] if the parent node is nil (i.e. empty), edi becomes the parent. ;[c] appendNode: call lastNode jz .empty mov [esi+Node.next],edi ret .empty: mov esi,edi ret ;[cf] ;[of]:findNode ;[c]Find a node ;[c] ;[c]ARGUMENTS ;[c] al ;[c] char ;[c] esi ;[c] parent node ;[c] ;[c]RETURN VALUES ;[c] edi ;[c] search node or nil ;[c] zf ;[c] 1 if not found ;[c] findNode: mov edi,esi jmp .first .next: cmp al,[edi+Node.char] jz .done mov edi,[edi+Node.next] .first: or edi,edi jnz .next .done: or edi,edi ret ;[cf] ;[of]:lastNode ;[c]Returns the last node ;[c] ;[c]ARGUMENTS ;[c] esi ;[c] parent node ;[c] ;[c]RETURN VALUES ;[c] esi ;[c] the last node ;[c] zf ;[c] 1 if the list of nodes is empty ;[c] lastNode: or esi,esi jnz .first ret .next: mov esi,eax .first: mov eax,[esi+Node.next] or eax,eax jnz .next or esi,esi ret ;[cf] ;[cf] ;[cf] ;[of]: newElementClass ;[c]Create a new element class ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] source element ;[c] ebx ;[c] is element ;[c] ecx ;[c] context ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] ebx ;[c] the new element class ;[c] newElementClass: push esi push edi mov esi,eax ; ; Allocate memory for element class + name ; mov eax,[esi+Element.sizeof+Attribute.limit] sub eax,[esi+Element.sizeof+Attribute.begin] add eax,ElementClass.sizeof+1 mov edx,ecx mov ecx,4 call allocateMemory jnz .error ; ; Reset offset ; ; the offset is set to 0 if the element is a collection, ; and it is temporary set to 1 if it is an element, it ; is just a flag now, but it will replaced by an offset ; later. ; mov [edi+ElementClass.offset],ebx mov [edi+ElementClass.shared],0 mov [edi+ElementClass.status],UNRESOLVED ; ; Setup type ; lea eax,[esi+Element.sizeof+ATTR_TYPE*Attribute.sizeof] mov ebx,TYPE_MIXED lea ecx,[mixedValue] call compareAttributeValue jz .found mov ebx,TYPE_CONTAINER lea ecx,[textValue] call compareAttributeValue jnz .found mov ebx,TYPE_TEXT .found: mov [edi+ElementClass.type],ebx ; ; Copy and Set Name ; push esi lea eax,[edi+ElementClass.sizeof] mov [edi+ElementClass.name],eax push edi mov ecx,[esi+Element.sizeof+Attribute.limit] mov esi,[esi+Element.sizeof+Attribute.begin] sub ecx,esi mov edi,eax rep movsb mov esi,edi xor eax,eax stosb pop edi mov [edi+ElementClass.nameLimit],esi pop esi ; ; Setup id ; mov eax,[esi+Element.sizeof+ATTR_ID*Attribute.sizeof+Attribute.begin] mov ebx,[esi+Element.sizeof+ATTR_ID*Attribute.sizeof+Attribute.limit] call toInteger jnz .error mov [edi+ElementClass.id],ebx xor eax,eax mov ebx,edi .error: pop edi pop esi ret ;[cf] ;[of]: copyElementClass ;[c]Copy an element class ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] source class ;[c] ebx ;[c] context ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] ebx ;[c] the new element class ;[c] copyElementClass: push esi push edi mov esi,eax ; ; Allocate memory for element class ; mov eax,ElementClass.sizeof mov edx,ebx mov ecx,4 call allocateMemory jnz .error mov ebx,edi mov ecx,ElementClass.sizeof / 4 rep movsd xor eax,eax .error: pop edi pop esi ret ;[cf] ;[cf] ;[cf] ;[of]:Utility Functions ;[of]:compareAttributeValue ;[c]Compare the value of an attribute ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] the attribute ;[c] ecx ;[c] the value (zero terminated) ;[c] ;[c]RETURN VALUES ;[c] zf ;[c] 1 if equal ;[c] compareAttributeValue: push esi push edi mov edi,ecx mov esi,[eax+Attribute.begin] mov ecx,[eax+Attribute.limit] sub ecx,esi jz .done .next: cmpsb jnz .diff sub ecx,1 jnz .next .done: cmp byte [edi],0 .diff: pop edi pop esi ret ;[cf] ;[of]:compareAttributes ;[c]Compare two attributes ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] the first attribute ;[c] ebx ;[c] the second attribute ;[c] ;[c]RETURN VALUES ;[c] zf ;[c] 1 if equal ;[c] compareAttributes: push esi push edi mov esi,[eax+Attribute.begin] mov edi,[ebx+Attribute.begin] ; Compare length mov ecx,[eax+Attribute.limit] mov edx,[ebx+Attribute.limit] sub ecx,esi sub edx,edi cmp ecx,edx jnz .diff repz cmpsb .diff: pop edi pop esi ret ;[cf] ;[of]:toInteger ;[c]Converts a string to an integer ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] the string to parse ;[c] ebx ;[c] the end of string ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] ebx ;[c] integer value ;[c] toInteger: push esi push edi mov esi,eax mov edi,ebx xor eax,eax jmp .first .loop: movzx ecx,byte [esi] cmp cl,'0' jc .error cmp cl,'9'+1 jnc .error inc esi mov edx,10 imul eax,edx sub cl,'0' add eax,ecx .first: cmp esi,edi jnz .loop mov ebx,eax xor eax,eax .done: pop edi pop esi ret ;[c] .error: mov eax,RC_INVALID_ID or eax,eax jmp .done ;[cf] ;[cf] ;[of]:Memory ;[of]:allocateMemory ;[c]Allocates a memory block ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] size ;[c] ecx ;[c] alignment ;[c] edx ;[c] context ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] edi ;[c] the object ;[c] allocateMemory: mov edi,[edx+MemoryContext.base] sub ecx,1 add edi,ecx not ecx and edi,ecx add eax,edi cmp eax,[edx+MemoryContext.limit] jnc .chunkFull mov [edx+MemoryContext.base],eax xor eax,eax ret ;[c] .chunkFull: push edx push ebp mov ebp,edx sub eax,edi call extendMemory ; let's assume that alignment is ok pop ebp pop edx ret ;[cf] ;[of]:extendMemory ;[c]Extend memory ;[c] ;[c]When there is not enough memory available, a new chunk is created ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] size ;[c] ebp ;[c] the parse context ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] edi ;[c] the allocated memory buffer ;[c] extendMemory: push eax mov ecx,[ebp+MemoryContext.chunkSize] add eax,ecx call newChunk pop ecx jnz .error mov edi,[ebp+MemoryContext.base] add ecx,edi mov [ebp+MemoryContext.base],ecx xor eax,eax .error: ret ;[cf] ;[of]:releaseChunks ;[c]Release the memory handled by the memory context ;[c] ;[c]ARGUMENTS ;[c] ebp ;[c] the context ;[c] releaseChunks: push ebx mov eax,[ebp+MemoryContext.chunks] or eax,eax jz .done .loop: mov ebx,[eax] call freeChunk mov eax,ebx or eax,eax jnz .loop .done: pop ebx ret ;[cf] ;[of]:newChunk ;[c]Create a new chunk of memory ;[c] ;[c]ARGUMENTS ;[c] ebp ;[c] the parse context ;[c] eax ;[c] the size of chunk ;[c] ;[c]RETURN VALUES ;[c] eax+zf ;[c] error code ;[c] newChunk: mov ecx,eax add eax,HEADER_SIZE ; size for link push ecx push eax call [mallocFunction] add esp,4 pop ecx or eax,eax jz .outOfMemory mov edx,[ebp+MemoryContext.chunks] mov [eax],edx mov [ebp+MemoryContext.chunks],eax add eax,HEADER_SIZE add ecx,eax mov [ebp+MemoryContext.base],eax mov [ebp+MemoryContext.limit],ecx xor eax,eax ret ;[c] .outOfMemory: mov eax,RC_MEMORY or eax,eax ret ;[cf] ;[of]:freeChunk ;[c]Free a chunk ;[c] ;[c]ARGUMENTS ;[c] eax ;[c] the chunk ;[c] freeChunk: push eax call [freeFunction] add esp,4 ret ;[cf] ;[cf] ;[of]:Data align 4 ;[of]:S ;[c]The blank map ;[c] ;[c] Flag for each character: ;[c] - 1 for blank characters (9, 10, 13, 32) ;[c] - 0 for other characters ;[c] S: db 0,0,0,0,0,0,0,0,0,1,1,0,0,1,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;[cf] ;[of]:RAC ;[c]Regular Attribute Chars ;[c] ;[c] Flag for each character: ;[c] - 1 for special chars (" ' & \0) ;[c] - 0 for other characters ;[c] RAC: db 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;[cf] ;[of]:terminalChar ;[c]Any zero char is a terminator for an attribute name or an element name ;[c] terminalChar: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0 ; -. db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 ; : db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1 ; _ db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ;[cf] ;[of]:digit ;[c]Two flags for each chars: ;[c] - bit 0 for decimal chars ;[c] - bit 1 for hexadecimal chars ;[c] digit: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0 db 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,2,2,2,2,2,2,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;[cf] ;[of]:xdigit ;[c]Hexadecimal value of a char, assuming that the char is a valid ;[c]hexadecimal digit. ;[c] xdigit: db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0 db 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;[cf] ;[of]:noElement / noAttribute ;[c]The default map for element without child elements (attribute only elements). ;[c] noAttribute: noElement: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ;[cf] ;[of]:PubidChar ;[c]Valid characters for PubidLiteral ;[c] PubidChar: db 0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1 db 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1 db 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 db 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;[cf] ;[of]:invalidClass ;[c]This special class is used to ignore unknown elements when ;[c]parsing in non strict mode. ;[c] invalidClass: dd Element.reserved ; store as ignored dd invalidName dd invalidName+1 dd Element.sizeof + 0 * Attribute.sizeof dd 0 ; null id dd TYPE_CONTAINER dd 0 ; no child dd 0 ; no element dd noAttribute dd noElement ;[cf] ;[of]:Schema Class ;[c]Schema Class ;[c] SchemaClass: dd 0 dd schemaName dd schemaName+6 dd Element.sizeof dd schemaId dd TYPE_CONTAINER dd 0 dd 0 dd noAttribute dd schemaMap ;[of]:Attribute Class ;[c]Attribute Class ;[c] attributeClass: dd 0 dd attributeName dd attributeName+9 dd Element.sizeof + 2 * Attribute.sizeof dd attributeId dd TYPE_CONTAINER dd 2 dd 0 dd nameIgnoreAttr dd noElement ;[cf] ;[of]:Text Class ;[c]Text Class ;[c] textClass: dd 0 dd textName dd textName+4 dd Element.sizeof + 2 * Attribute.sizeof dd textId dd TYPE_CONTAINER dd 2 dd 0 dd nameIgnoreAttr dd noElement ;[cf] ;[of]:Document Class documentClass: dd 0 dd documentName dd documentName+8 dd Element.sizeof + 4 * Attribute.sizeof dd documentId dd TYPE_CONTAINER dd 4 dd 0 dd nameAndTypeAttr dd elementMap ;[cf] ;[of]:Collection Class collectionClass: dd 0 dd collectionName dd collectionName+10 dd Element.sizeof + 4 * Attribute.sizeof dd collectionId dd TYPE_CONTAINER dd 4 dd 0 dd nameAndTypeAttr dd elementMap ;[cf] ;[of]:Group Class groupClass: dd 0 dd groupName dd groupName+5 dd Element.sizeof + 4 * Attribute.sizeof dd groupId dd TYPE_CONTAINER dd 4 dd 0 dd nameAndTypeAttr dd elementMap ;[cf] ;[of]:Element Class elementClass: dd 0 dd elementName dd elementName+7 dd Element.sizeof + 4 * Attribute.sizeof dd elementId dd TYPE_CONTAINER dd 4 dd 0 dd nameAndTypeAttr dd elementMap ;[cf] ;[of]:Reference Class referenceClass: dd 0 dd referenceName dd referenceName+9 dd Element.sizeof + 2 * Attribute.sizeof dd referenceId dd TYPE_CONTAINER dd 2 dd 0 dd nameIgnoreAttr dd noElement ;[cf] ;[of]:Include Class includeClass: dd 0 dd includeName dd includeName+7 dd Element.sizeof + 1 * Attribute.sizeof dd includeId dd TYPE_CONTAINER dd 1 dd 0 dd nameAttr dd noElement ;[cf] ;[of]:Element maps align 4 schemaMap: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,.c+1,.d+1,.e+1,1,.g+1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 .c: align 4 db "ollection",0,0 align 4 dd collectionClass .d: align 4 db "ocument",0,0 align 4 dd documentClass .e: align 4 db "lement",0,0 align 4 dd elementClass .g: align 4 db "roup",0,0 align 4 dd groupClass align 4 elementMap: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,.a+1,1,.c+1,1,.e+1,1,1,1,.i+1,1,1,1,1,1,1 dd 1,1,.r+1,1,.t+1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 align 4 .a: db "ttribute",0,0 align 4 dd attributeClass align 4 .t: db "ext",0,0 align 4 dd textClass .c: align 4 db "ollection",0,0 align 4 dd collectionClass .e: align 4 db "lement",0,0 align 4 dd elementClass .i: align 4 db "nclude",0,0 align 4 dd includeClass .r: align 4 db "eference",0,0 align 4 dd referenceClass ;[cf] ;[of]:name= align 4 nameAttr: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,.name+1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 align 4 .name: db "ame",0 dd Element.sizeof + 0 * Attribute.sizeof ;[cf] ;[of]:name= ignore= align 4 nameIgnoreAttr: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,.ignore+1,1,1,1,1,.name+1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 align 4 .name: db "ame",0 dd Element.sizeof + 0 * Attribute.sizeof align 4 .ignore: db "gnore",0 dd Element.sizeof + 1 * Attribute.sizeof ;[cf] ;[of]:name= ignore= id= type= align 4 nameAndTypeAttr: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,.i,1,1,1,1,.name+1,1 dd 1,1,1,1,.type+1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 .i: dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,.id+1,1,1,.ignore+1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 dd 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 align 4 .name: db "ame",0 dd Element.sizeof + 0 * Attribute.sizeof align 4 .ignore db "nore",0 dd Element.sizeof + 1 * Attribute.sizeof align 4 .type: db "ype",0 dd Element.sizeof + 2 * Attribute.sizeof align 4 .id: db 0 dd Element.sizeof + 3 * Attribute.sizeof ;[cf] ;[of]:Element Names schemaName db "schema",0 documentName db "document",0 attributeName db "attribute",0 textName db "text",0 collectionName db "collection",0 elementName db "element",0 referenceName db "reference",0 includeName db "include",0 groupName db "group",0 invalidName db "?",0 textValue db "text",0 mixedValue db "mixed",0 yes db "yes",0 no db "no",0 ;[cf] ;[cf] ;[of]:Atoms atomTable dd DOCTYPEName dd ELEMENTName dd ATTLISTName dd PCDATAName dd IMPLIEDName dd REQUIREDName dd FIXEDName dd NDATAName dd CDATAName dd IDName dd IDREFName dd IDREFSName dd ENTITYName dd ENTITIESName dd NMTOKENName dd NMTOKENSName dd NOTATIONName dd versionName dd encodingName dd standaloneName dd 0 DOCTYPEName db "DOCTYPE",0 ELEMENTName db "ELEMENT",0 ATTLISTName db "ATTLIST",0 PCDATAName db "PCDATA",0 IMPLIEDName db "IMPLIED",0 REQUIREDName db "REQUIRED",0 FIXEDName db "FIXED",0 NDATAName db "NDATA",0 CDATAName db "CDATA",0 IDName db "ID",0 IDREFName db "IDREF",0 IDREFSName db "IDREFS",0 ENTITYName db "ENTITY",0 ENTITIESName db "ENTITIES",0 NMTOKENName db "NMTOKEN",0 NMTOKENSName db "NMTOKENS",0 NOTATIONName db "NOTATION",0 versionName db "version",0 encodingName db "encoding",0 standaloneName db "standalone",0 ;[cf] ;[cf] ;[of]:BSS section '.asmdata' writeable mallocFunction rd 1 freeFunction rd 1 ;[cf]