webservices: Add a builtin dictionary.

Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
oldstable
Hans Leidekker 2017-05-30 10:09:07 +02:00 committed by Alexandre Julliard
parent 0e942e5021
commit 8214fcfa5d
5 changed files with 261 additions and 86 deletions

View File

@ -119,7 +119,7 @@ static void free_header( struct header *header )
{
heap_free( header->name.bytes );
heap_free( header->ns.bytes );
if (header->mapped) heap_free( header->u.text );
if (header->mapped) free_xml_string( header->u.text );
heap_free( header );
}

View File

@ -70,6 +70,202 @@ HRESULT prop_get( const struct prop *prop, ULONG count, ULONG id, void *buf, ULO
return S_OK;
}
static CRITICAL_SECTION dict_cs;
static CRITICAL_SECTION_DEBUG dict_cs_debug =
{
0, 0, &dict_cs,
{&dict_cs_debug.ProcessLocksList,
&dict_cs_debug.ProcessLocksList},
0, 0, {(DWORD_PTR)(__FILE__ ": dict_cs")}
};
static CRITICAL_SECTION dict_cs = {&dict_cs_debug, -1, 0, 0, 0, 0};
static ULONG dict_size, *dict_sorted;
static WS_XML_DICTIONARY dict_builtin =
{
{0x82704485,0x222a,0x4f7c,{0xb9,0x7b,0xe9,0xa4,0x62,0xa9,0x66,0x2b}}
};
static inline int cmp_string( const unsigned char *str, ULONG len, const unsigned char *str2, ULONG len2 )
{
if (len < len2) return -1;
else if (len > len2) return 1;
while (len--)
{
if (*str == *str2) { str++; str2++; }
else return *str - *str2;
}
return 0;
}
/* return -1 and string id if found, sort index if not found */
static int find_string( const unsigned char *data, ULONG len, ULONG *id )
{
int i, c, min = 0, max = dict_builtin.stringCount - 1;
while (min <= max)
{
i = (min + max) / 2;
c = cmp_string( data, len,
dict_builtin.strings[dict_sorted[i]].bytes,
dict_builtin.strings[dict_sorted[i]].length );
if (c < 0)
max = i - 1;
else if (c > 0)
min = i + 1;
else
{
*id = dict_builtin.strings[dict_sorted[i]].id;
return -1;
}
}
return max + 1;
}
#define MIN_DICTIONARY_SIZE 256
#define MAX_DICTIONARY_SIZE 2048
static BOOL grow_dict( ULONG size )
{
WS_XML_STRING *tmp;
ULONG new_size, *tmp_sorted;
if (dict_size >= dict_builtin.stringCount + size) return TRUE;
if (dict_size + size > MAX_DICTIONARY_SIZE) return FALSE;
if (!dict_builtin.strings)
{
new_size = max( MIN_DICTIONARY_SIZE, size );
if (!(dict_builtin.strings = heap_alloc( new_size * sizeof(WS_XML_STRING) ))) return FALSE;
if (!(dict_sorted = heap_alloc( new_size * sizeof(ULONG) )))
{
heap_free( dict_builtin.strings );
dict_builtin.strings = NULL;
return FALSE;
}
dict_size = new_size;
return TRUE;
}
new_size = max( dict_size * 2, size );
if (!(tmp = heap_realloc( dict_builtin.strings, new_size * sizeof(*tmp) ))) return FALSE;
dict_builtin.strings = tmp;
if (!(tmp_sorted = heap_realloc( dict_sorted, new_size * sizeof(*tmp_sorted) ))) return FALSE;
dict_sorted = tmp_sorted;
dict_size = new_size;
return TRUE;
}
static BOOL insert_string( unsigned char *data, ULONG len, int i, ULONG *ret_id )
{
ULONG id = dict_builtin.stringCount;
if (!grow_dict( 1 )) return FALSE;
memmove( &dict_sorted[i] + 1, &dict_sorted[i], (dict_builtin.stringCount - i) * sizeof(WS_XML_STRING *) );
dict_sorted[i] = id;
dict_builtin.strings[id].length = len;
dict_builtin.strings[id].bytes = data;
dict_builtin.strings[id].dictionary = &dict_builtin;
dict_builtin.strings[id].id = id;
dict_builtin.stringCount++;
*ret_id = id;
return TRUE;
}
static HRESULT add_xml_string( WS_XML_STRING *str )
{
int index;
ULONG id;
if (str->dictionary) return S_OK;
EnterCriticalSection( &dict_cs );
if ((index = find_string( str->bytes, str->length, &id )) == -1)
{
heap_free( str->bytes );
*str = dict_builtin.strings[id];
LeaveCriticalSection( &dict_cs );
return S_OK;
}
if (insert_string( str->bytes, str->length, index, &id ))
{
*str = dict_builtin.strings[id];
LeaveCriticalSection( &dict_cs );
return S_OK;
}
LeaveCriticalSection( &dict_cs );
return WS_E_QUOTA_EXCEEDED;
}
WS_XML_STRING *alloc_xml_string( const unsigned char *data, ULONG len )
{
WS_XML_STRING *ret;
if (!(ret = heap_alloc_zero( sizeof(*ret) ))) return NULL;
if ((ret->length = len) && !(ret->bytes = heap_alloc( len )))
{
heap_free( ret );
return NULL;
}
if (data)
{
memcpy( ret->bytes, data, len );
if (add_xml_string( ret ) != S_OK) WARN( "string not added to dictionary\n" );
}
return ret;
}
void free_xml_string( WS_XML_STRING *str )
{
if (!str) return;
if (!str->dictionary) heap_free( str->bytes );
heap_free( str );
}
WS_XML_STRING *dup_xml_string( const WS_XML_STRING *src )
{
WS_XML_STRING *ret;
unsigned char *data;
int index;
ULONG id;
if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
if (src->dictionary)
{
*ret = *src;
return ret;
}
EnterCriticalSection( &dict_cs );
if ((index = find_string( src->bytes, src->length, &id )) == -1)
{
*ret = dict_builtin.strings[id];
LeaveCriticalSection( &dict_cs );
return ret;
}
if (!(data = heap_alloc( src->length )))
{
heap_free( ret );
LeaveCriticalSection( &dict_cs );
return NULL;
}
memcpy( data, src->bytes, src->length );
if (insert_string( data, src->length, index, &id ))
{
*ret = dict_builtin.strings[id];
LeaveCriticalSection( &dict_cs );
return ret;
}
LeaveCriticalSection( &dict_cs );
WARN( "string not added to dictionary\n" );
ret->length = src->length;
ret->bytes = data;
ret->dictionary = NULL;
ret->id = 0;
return ret;
}
struct node *alloc_node( WS_XML_NODE_TYPE type )
{
struct node *ret;
@ -84,9 +280,9 @@ struct node *alloc_node( WS_XML_NODE_TYPE type )
void free_attribute( WS_XML_ATTRIBUTE *attr )
{
if (!attr) return;
heap_free( attr->prefix );
heap_free( attr->localName );
heap_free( attr->ns );
free_xml_string( attr->prefix );
free_xml_string( attr->localName );
free_xml_string( attr->ns );
heap_free( attr->value );
heap_free( attr );
}
@ -103,9 +299,9 @@ void free_node( struct node *node )
for (i = 0; i < elem->attributeCount; i++) free_attribute( elem->attributes[i] );
heap_free( elem->attributes );
heap_free( elem->prefix );
heap_free( elem->localName );
heap_free( elem->ns );
free_xml_string( elem->prefix );
free_xml_string( elem->localName );
free_xml_string( elem->ns );
break;
}
case WS_XML_NODE_TYPE_TEXT:
@ -351,8 +547,8 @@ enum reader_state
struct prefix
{
WS_XML_STRING str;
WS_XML_STRING ns;
WS_XML_STRING *str;
WS_XML_STRING *ns;
};
struct reader
@ -411,13 +607,10 @@ static void clear_prefixes( struct prefix *prefixes, ULONG count )
ULONG i;
for (i = 0; i < count; i++)
{
heap_free( prefixes[i].str.bytes );
prefixes[i].str.bytes = NULL;
prefixes[i].str.length = 0;
heap_free( prefixes[i].ns.bytes );
prefixes[i].ns.bytes = NULL;
prefixes[i].ns.length = 0;
free_xml_string( prefixes[i].str );
prefixes[i].str = NULL;
free_xml_string( prefixes[i].ns );
prefixes[i].ns = NULL;
}
}
@ -425,17 +618,11 @@ static HRESULT set_prefix( struct prefix *prefix, const WS_XML_STRING *str, cons
{
if (str)
{
heap_free( prefix->str.bytes );
if (!(prefix->str.bytes = heap_alloc( str->length ))) return E_OUTOFMEMORY;
memcpy( prefix->str.bytes, str->bytes, str->length );
prefix->str.length = str->length;
free_xml_string( prefix->str );
if (!(prefix->str = dup_xml_string( str ))) return E_OUTOFMEMORY;
}
heap_free( prefix->ns.bytes );
if (!(prefix->ns.bytes = heap_alloc( ns->length ))) return E_OUTOFMEMORY;
memcpy( prefix->ns.bytes, ns->bytes, ns->length );
prefix->ns.length = ns->length;
if (prefix->ns) free_xml_string( prefix->ns );
if (!(prefix->ns = dup_xml_string( ns ))) return E_OUTOFMEMORY;
return S_OK;
}
@ -446,7 +633,7 @@ static HRESULT bind_prefix( struct reader *reader, const WS_XML_STRING *prefix,
for (i = 0; i < reader->nb_prefixes; i++)
{
if (WsXmlStringEquals( prefix, &reader->prefixes[i].str, NULL ) == S_OK)
if (WsXmlStringEquals( prefix, reader->prefixes[i].str, NULL ) == S_OK)
return set_prefix( &reader->prefixes[i], NULL, ns );
}
if (i >= reader->nb_prefixes_allocated)
@ -457,7 +644,6 @@ static HRESULT bind_prefix( struct reader *reader, const WS_XML_STRING *prefix,
reader->prefixes = tmp;
reader->nb_prefixes_allocated *= 2;
}
if ((hr = set_prefix( &reader->prefixes[i], prefix, ns )) != S_OK) return hr;
reader->nb_prefixes++;
return S_OK;
@ -468,8 +654,8 @@ static const WS_XML_STRING *get_namespace( struct reader *reader, const WS_XML_S
ULONG i;
for (i = 0; i < reader->nb_prefixes; i++)
{
if (WsXmlStringEquals( prefix, &reader->prefixes[i].str, NULL ) == S_OK)
return &reader->prefixes[i].ns;
if (WsXmlStringEquals( prefix, reader->prefixes[i].str, NULL ) == S_OK)
return reader->prefixes[i].ns;
}
return NULL;
}
@ -511,7 +697,9 @@ static void free_reader( struct reader *reader )
static HRESULT init_reader( struct reader *reader )
{
static const WS_XML_STRING empty = {0, NULL};
struct node *node;
HRESULT hr;
reader->state = READER_STATE_INITIAL;
destroy_nodes( reader->root );
@ -519,10 +707,12 @@ static HRESULT init_reader( struct reader *reader )
reader->current_attr = 0;
clear_prefixes( reader->prefixes, reader->nb_prefixes );
reader->nb_prefixes = 1;
if ((hr = bind_prefix( reader, &empty, &empty )) != S_OK) return hr;
if (!(node = alloc_node( WS_XML_NODE_TYPE_EOF ))) return E_OUTOFMEMORY;
read_insert_eof( reader, node );
reader->input_enc = WS_XML_READER_ENCODING_TYPE_TEXT;
reader->dict = NULL;
reader->dict = &dict_builtin;
return S_OK;
}
@ -786,33 +976,6 @@ HRESULT WINAPI WsGetXmlAttribute( WS_XML_READER *handle, const WS_XML_STRING *at
return E_NOTIMPL;
}
WS_XML_STRING *alloc_xml_string( const unsigned char *data, ULONG len )
{
WS_XML_STRING *ret;
if (!(ret = heap_alloc( sizeof(*ret) + len ))) return NULL;
ret->length = len;
ret->bytes = len ? (BYTE *)(ret + 1) : NULL;
ret->dictionary = NULL;
ret->id = 0;
if (data) memcpy( ret->bytes, data, len );
return ret;
}
WS_XML_STRING *dup_xml_string( const WS_XML_STRING *src )
{
WS_XML_STRING *ret;
if (!src->dictionary) return alloc_xml_string( src->bytes, src->length );
if (!(ret = heap_alloc( sizeof(*ret) ))) return NULL;
ret->length = src->length;
ret->bytes = src->bytes;
ret->dictionary = src->dictionary;
ret->id = src->id;
return ret;
}
WS_XML_UTF8_TEXT *alloc_utf8_text( const unsigned char *data, ULONG len )
{
WS_XML_UTF8_TEXT *ret;
@ -1036,7 +1199,7 @@ static HRESULT parse_name( const unsigned char *str, ULONG len, WS_XML_STRING **
if (!(*prefix = alloc_xml_string( prefix_ptr, prefix_len ))) return E_OUTOFMEMORY;
if (!(*localname = alloc_xml_string( localname_ptr, localname_len )))
{
heap_free( *prefix );
free_xml_string( *prefix );
*prefix = NULL;
return E_OUTOFMEMORY;
}
@ -1274,8 +1437,12 @@ static HRESULT read_string( struct reader *reader, WS_XML_STRING **str )
HRESULT hr;
if ((hr = read_int31( reader, &len )) != S_OK) return hr;
if (!(*str = alloc_xml_string( NULL, len ))) return E_OUTOFMEMORY;
if ((hr = read_bytes( reader, (*str)->bytes, len )) == S_OK) return S_OK;
heap_free( *str );
if ((hr = read_bytes( reader, (*str)->bytes, len )) == S_OK)
{
if (add_xml_string( *str ) != S_OK) WARN( "string not added to dictionary\n" );
return S_OK;
}
free_xml_string( *str );
return hr;
}
@ -1350,11 +1517,11 @@ static HRESULT read_attribute_text( struct reader *reader, WS_XML_ATTRIBUTE **re
if ((hr = parse_name( start, len, &prefix, &localname )) != S_OK) goto error;
if (WsXmlStringEquals( prefix, &xmlns, NULL ) == S_OK)
{
heap_free( prefix );
free_xml_string( prefix );
attr->isXmlNs = 1;
if (!(attr->prefix = alloc_xml_string( localname->bytes, localname->length )))
{
heap_free( localname );
free_xml_string( localname );
hr = E_OUTOFMEMORY;
goto error;
}
@ -1524,15 +1691,15 @@ static HRESULT set_namespaces( struct reader *reader, WS_XML_ELEMENT_NODE *elem
ULONG i;
if (!(ns = get_namespace( reader, elem->prefix ))) return WS_E_INVALID_FORMAT;
if (!(elem->ns = alloc_xml_string( ns->bytes, ns->length ))) return E_OUTOFMEMORY;
if (!elem->ns->length) elem->ns->bytes = (BYTE *)(elem->ns + 1); /* quirk */
if (!(elem->ns = dup_xml_string( ns ))) return E_OUTOFMEMORY;
for (i = 0; i < elem->attributeCount; i++)
{
WS_XML_ATTRIBUTE *attr = elem->attributes[i];
if (attr->isXmlNs || WsXmlStringEquals( attr->prefix, &xml, NULL ) == S_OK) continue;
if (!(ns = get_namespace( reader, attr->prefix ))) return WS_E_INVALID_FORMAT;
if (!(attr->ns = alloc_xml_string( ns->bytes, ns->length ))) return E_OUTOFMEMORY;
if (!(attr->ns = alloc_xml_string( NULL, ns->length ))) return E_OUTOFMEMORY;
if (attr->ns->length) memcpy( attr->ns->bytes, ns->bytes, ns->length );
}
return S_OK;
}
@ -1990,8 +2157,8 @@ static HRESULT read_endelement_text( struct reader *reader )
if ((hr = parse_name( start, len, &prefix, &localname )) != S_OK) return hr;
parent = find_startelement( reader, prefix, localname );
heap_free( prefix );
heap_free( localname );
free_xml_string( prefix );
free_xml_string( localname );
if (!parent) return WS_E_INVALID_FORMAT;
reader->current = LIST_ENTRY( list_tail( &parent->children ), struct node, entry );

View File

@ -4554,7 +4554,7 @@ static void test_binary_encoding(void)
ok( elem->prefix->bytes == NULL, "bytes set\n" );
ok( elem->localName->length == 1, "got %u\n", elem->localName->length );
ok( !memcmp( elem->localName->bytes, "t", 1 ), "wrong name\n" );
todo_wine ok( elem->localName->dictionary != NULL, "dictionary not set\n" );
ok( elem->localName->dictionary != NULL, "dictionary not set\n" );
ok( !elem->ns->length, "got %u\n", elem->ns->length );
ok( elem->ns->bytes != NULL, "bytes not set\n" );
ok( !elem->attributeCount, "got %u\n", elem->attributeCount );

View File

@ -36,6 +36,7 @@ void free_xmlbuf( struct xmlbuf * ) DECLSPEC_HIDDEN;
const char *debugstr_xmlstr( const WS_XML_STRING * ) DECLSPEC_HIDDEN;
WS_XML_STRING *alloc_xml_string( const unsigned char *, ULONG ) DECLSPEC_HIDDEN;
WS_XML_STRING *dup_xml_string( const WS_XML_STRING * ) DECLSPEC_HIDDEN;
void free_xml_string( WS_XML_STRING * ) DECLSPEC_HIDDEN;
WS_XML_UTF8_TEXT *alloc_utf8_text( const unsigned char *, ULONG ) DECLSPEC_HIDDEN;
HRESULT append_attribute( WS_XML_ELEMENT_NODE *, WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN;
void free_attribute( WS_XML_ATTRIBUTE * ) DECLSPEC_HIDDEN;

View File

@ -83,6 +83,7 @@ struct writer
WS_XML_WRITER_OUTPUT_TYPE output_type;
struct xmlbuf *output_buf;
WS_HEAP *output_heap;
WS_XML_DICTIONARY *dict;
ULONG prop_count;
struct prop prop[sizeof(writer_props)/sizeof(writer_props[0])];
};
@ -109,7 +110,7 @@ static struct writer *alloc_writer(void)
static void free_writer( struct writer *writer )
{
destroy_nodes( writer->root );
heap_free( writer->current_ns );
free_xml_string( writer->current_ns );
WsFreeHeap( writer->output_heap );
writer->cs.DebugInfo->Spare[0] = 0;
@ -157,13 +158,14 @@ static HRESULT init_writer( struct writer *writer )
writer->write_bufptr = NULL;
destroy_nodes( writer->root );
writer->root = writer->current = NULL;
heap_free( writer->current_ns );
free_xml_string( writer->current_ns );
writer->current_ns = NULL;
if (!(node = alloc_node( WS_XML_NODE_TYPE_EOF ))) return E_OUTOFMEMORY;
write_insert_eof( writer, node );
writer->state = WRITER_STATE_INITIAL;
writer->output_enc = WS_XML_WRITER_ENCODING_TYPE_TEXT;
writer->dict = NULL;
return S_OK;
}
@ -379,7 +381,9 @@ HRESULT WINAPI WsSetOutput( WS_XML_WRITER *handle, const WS_XML_WRITER_ENCODING
}
case WS_XML_WRITER_ENCODING_TYPE_BINARY:
{
WS_XML_WRITER_BINARY_ENCODING *bin = (WS_XML_WRITER_BINARY_ENCODING *)encoding;
writer->output_enc = WS_XML_WRITER_ENCODING_TYPE_BINARY;
writer->dict = bin->staticDictionary;
break;
}
default:
@ -665,25 +669,26 @@ static HRESULT write_attribute_value_bin( struct writer *writer, const WS_XML_TE
}
}
static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr )
static enum record_type get_attr_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict )
{
if (!attr->prefix || !attr->prefix->length)
{
if (attr->localName->dictionary) return RECORD_SHORT_DICTIONARY_ATTRIBUTE;
if (use_dict) return RECORD_SHORT_DICTIONARY_ATTRIBUTE;
return RECORD_SHORT_ATTRIBUTE;
}
if (attr->prefix->length == 1 && attr->prefix->bytes[0] >= 'a' && attr->prefix->bytes[0] <= 'z')
{
if (attr->localName->dictionary) return RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a';
if (use_dict) return RECORD_PREFIX_DICTIONARY_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a';
return RECORD_PREFIX_ATTRIBUTE_A + attr->prefix->bytes[0] - 'a';
}
if (attr->localName->dictionary) return RECORD_DICTIONARY_ATTRIBUTE;
if (use_dict) return RECORD_DICTIONARY_ATTRIBUTE;
return RECORD_ATTRIBUTE;
};
static HRESULT write_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUTE *attr )
{
enum record_type type = get_attr_record_type( attr );
BOOL use_dict = (writer->dict && attr->localName->dictionary == writer->dict);
enum record_type type = get_attr_record_type( attr, use_dict );
HRESULT hr;
if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
@ -817,20 +822,21 @@ static HRESULT write_namespace_attribute_text( struct writer *writer, const WS_X
return S_OK;
}
static enum record_type get_xmlns_record_type( const WS_XML_ATTRIBUTE *attr )
static enum record_type get_xmlns_record_type( const WS_XML_ATTRIBUTE *attr, BOOL use_dict )
{
if (!attr->prefix || !attr->prefix->length)
{
if (attr->ns->dictionary) return RECORD_SHORT_DICTIONARY_XMLNS_ATTRIBUTE;
if (use_dict) return RECORD_SHORT_DICTIONARY_XMLNS_ATTRIBUTE;
return RECORD_SHORT_XMLNS_ATTRIBUTE;
}
if (attr->ns->dictionary) return RECORD_DICTIONARY_XMLNS_ATTRIBUTE;
if (use_dict) return RECORD_DICTIONARY_XMLNS_ATTRIBUTE;
return RECORD_XMLNS_ATTRIBUTE;
};
static HRESULT write_namespace_attribute_bin( struct writer *writer, const WS_XML_ATTRIBUTE *attr )
{
enum record_type type = get_xmlns_record_type( attr );
BOOL use_dict = (writer->dict && attr->ns->dictionary == writer->dict);
enum record_type type = get_xmlns_record_type( attr, use_dict );
HRESULT hr;
if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;
@ -932,7 +938,7 @@ static HRESULT set_current_namespace( struct writer *writer, const WS_XML_STRING
{
WS_XML_STRING *str;
if (!(str = dup_xml_string( ns ))) return E_OUTOFMEMORY;
heap_free( writer->current_ns );
free_xml_string( writer->current_ns );
writer->current_ns = str;
return S_OK;
}
@ -1029,26 +1035,27 @@ static HRESULT write_startelement_text( struct writer *writer )
return write_attributes( writer, elem );
}
static enum record_type get_elem_record_type( const WS_XML_ELEMENT_NODE *elem )
static enum record_type get_elem_record_type( const WS_XML_ELEMENT_NODE *elem, BOOL use_dict )
{
if (!elem->prefix || !elem->prefix->length)
{
if (elem->localName->dictionary) return RECORD_SHORT_DICTIONARY_ELEMENT;
if (use_dict) return RECORD_SHORT_DICTIONARY_ELEMENT;
return RECORD_SHORT_ELEMENT;
}
if (elem->prefix->length == 1 && elem->prefix->bytes[0] >= 'a' && elem->prefix->bytes[0] <= 'z')
{
if (elem->localName->dictionary) return RECORD_PREFIX_DICTIONARY_ELEMENT_A + elem->prefix->bytes[0] - 'a';
if (use_dict) return RECORD_PREFIX_DICTIONARY_ELEMENT_A + elem->prefix->bytes[0] - 'a';
return RECORD_PREFIX_ELEMENT_A + elem->prefix->bytes[0] - 'a';
}
if (elem->localName->dictionary) return RECORD_DICTIONARY_ELEMENT;
if (use_dict) return RECORD_DICTIONARY_ELEMENT;
return RECORD_ELEMENT;
};
static HRESULT write_startelement_bin( struct writer *writer )
{
const WS_XML_ELEMENT_NODE *elem = &writer->current->hdr;
enum record_type type = get_elem_record_type( elem );
BOOL use_dict = (writer->dict && elem->localName->dictionary == writer->dict);
enum record_type type = get_elem_record_type( elem, use_dict );
HRESULT hr;
if ((hr = write_grow_buffer( writer, 1 )) != S_OK) return hr;