2 * IXmlReader implementation
4 * Copyright 2010 Nikolay Sivov
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "xmllite_private.h"
31 #include "wine/debug.h"
32 #include "wine/unicode.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(xmllite
);
36 /* not defined in public headers */
37 DEFINE_GUID(IID_IXmlReaderInput
, 0x0b3ccc9b, 0x9214, 0x428b, 0xa2, 0xae, 0xef, 0x3a, 0xa8, 0x71, 0xaf, 0xda);
46 static const WCHAR utf16W
[] = {'U','T','F','-','1','6',0};
47 static const WCHAR utf8W
[] = {'U','T','F','-','8',0};
49 struct xml_encoding_data
51 const WCHAR
*encoding
;
56 static const struct xml_encoding_data xml_encoding_map
[] = {
57 { utf16W
, XmlEncoding_UTF16
, ~0 },
58 { utf8W
, XmlEncoding_UTF8
, CP_UTF8
}
64 unsigned int allocated
;
68 typedef struct input_buffer input_buffer
;
70 typedef struct _xmlreaderinput
72 IXmlReaderInput IXmlReaderInput_iface
;
74 /* reference passed on IXmlReaderInput creation, is kept when input is created */
77 xml_encoding encoding
;
80 /* stream reference set after SetInput() call from reader,
81 stored as sequential stream, cause currently
82 optimizations possible with IStream aren't implemented */
83 ISequentialStream
*stream
;
87 typedef struct _xmlreader
89 IXmlReader IXmlReader_iface
;
91 xmlreaderinput
*input
;
95 DtdProcessing dtdmode
;
96 UINT line
, pos
; /* reader position in XML stream */
101 encoded_buffer utf16
;
102 encoded_buffer encoded
;
104 xmlreaderinput
*input
;
107 static inline xmlreader
*impl_from_IXmlReader(IXmlReader
*iface
)
109 return CONTAINING_RECORD(iface
, xmlreader
, IXmlReader_iface
);
112 static inline xmlreaderinput
*impl_from_IXmlReaderInput(IXmlReaderInput
*iface
)
114 return CONTAINING_RECORD(iface
, xmlreaderinput
, IXmlReaderInput_iface
);
117 static inline void *m_alloc(IMalloc
*imalloc
, size_t len
)
120 return IMalloc_Alloc(imalloc
, len
);
122 return heap_alloc(len
);
125 static inline void *m_realloc(IMalloc
*imalloc
, void *mem
, size_t len
)
128 return IMalloc_Realloc(imalloc
, mem
, len
);
130 return heap_realloc(mem
, len
);
133 static inline void m_free(IMalloc
*imalloc
, void *mem
)
136 IMalloc_Free(imalloc
, mem
);
141 /* reader memory allocation functions */
142 static inline void *reader_alloc(xmlreader
*reader
, size_t len
)
144 return m_alloc(reader
->imalloc
, len
);
147 static inline void reader_free(xmlreader
*reader
, void *mem
)
149 return m_free(reader
->imalloc
, mem
);
152 /* reader input memory allocation functions */
153 static inline void *readerinput_alloc(xmlreaderinput
*input
, size_t len
)
155 return m_alloc(input
->imalloc
, len
);
158 static inline void *readerinput_realloc(xmlreaderinput
*input
, void *mem
, size_t len
)
160 return m_realloc(input
->imalloc
, mem
, len
);
163 static inline void readerinput_free(xmlreaderinput
*input
, void *mem
)
165 return m_free(input
->imalloc
, mem
);
168 static inline WCHAR
*readerinput_strdupW(xmlreaderinput
*input
, const WCHAR
*str
)
175 size
= (strlenW(str
)+1)*sizeof(WCHAR
);
176 ret
= readerinput_alloc(input
, size
);
177 if (ret
) memcpy(ret
, str
, size
);
183 static HRESULT
init_encoded_buffer(xmlreaderinput
*input
, encoded_buffer
*buffer
)
185 const int initial_len
= 0x2000;
186 buffer
->data
= readerinput_alloc(input
, initial_len
);
187 if (!buffer
->data
) return E_OUTOFMEMORY
;
189 memset(buffer
->data
, 0, 4);
190 buffer
->allocated
= initial_len
;
196 static void free_encoded_buffer(xmlreaderinput
*input
, encoded_buffer
*buffer
)
198 readerinput_free(input
, buffer
->data
);
201 static HRESULT
get_code_page(xml_encoding encoding
, xmlreaderinput
*input
)
203 const struct xml_encoding_data
*data
;
205 if (encoding
== XmlEncoding_Unknown
)
207 FIXME("unsupported encoding %d\n", encoding
);
211 data
= &xml_encoding_map
[encoding
];
212 input
->buffer
->code_page
= data
->cp
;
217 static xml_encoding
parse_encoding_name(const WCHAR
*encoding
)
221 if (!encoding
) return XmlEncoding_Unknown
;
224 max
= sizeof(xml_encoding_map
)/sizeof(struct xml_encoding_data
) - 1;
230 c
= strcmpiW(xml_encoding_map
[n
].encoding
, encoding
);
232 return xml_encoding_map
[n
].enc
;
240 return XmlEncoding_Unknown
;
243 static HRESULT
alloc_input_buffer(xmlreaderinput
*input
)
245 input_buffer
*buffer
;
248 input
->buffer
= NULL
;
250 buffer
= readerinput_alloc(input
, sizeof(*buffer
));
251 if (!buffer
) return E_OUTOFMEMORY
;
253 buffer
->input
= input
;
254 buffer
->code_page
= ~0; /* code page is unknown at this point */
255 hr
= init_encoded_buffer(input
, &buffer
->utf16
);
257 readerinput_free(input
, buffer
);
261 hr
= init_encoded_buffer(input
, &buffer
->encoded
);
263 free_encoded_buffer(input
, &buffer
->utf16
);
264 readerinput_free(input
, buffer
);
268 input
->buffer
= buffer
;
272 static void free_input_buffer(input_buffer
*buffer
)
274 free_encoded_buffer(buffer
->input
, &buffer
->encoded
);
275 free_encoded_buffer(buffer
->input
, &buffer
->utf16
);
276 readerinput_free(buffer
->input
, buffer
);
279 static void readerinput_release_stream(xmlreaderinput
*readerinput
)
281 if (readerinput
->stream
) {
282 ISequentialStream_Release(readerinput
->stream
);
283 readerinput
->stream
= NULL
;
287 /* Queries already stored interface for IStream/ISequentialStream.
288 Interface supplied on creation will be overwritten */
289 static HRESULT
readerinput_query_for_stream(xmlreaderinput
*readerinput
)
293 readerinput_release_stream(readerinput
);
294 hr
= IUnknown_QueryInterface(readerinput
->input
, &IID_IStream
, (void**)&readerinput
->stream
);
296 hr
= IUnknown_QueryInterface(readerinput
->input
, &IID_ISequentialStream
, (void**)&readerinput
->stream
);
301 /* reads a chunk to raw buffer */
302 static HRESULT
readerinput_growraw(xmlreaderinput
*readerinput
)
304 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
305 ULONG len
= buffer
->allocated
- buffer
->written
, read
;
308 /* always try to get aligned to 4 bytes, so the only case we can get partialy read characters is
309 variable width encodings like UTF-8 */
310 len
= (len
+ 3) & ~3;
311 /* try to use allocated space or grow */
312 if (buffer
->allocated
- buffer
->written
< len
)
314 buffer
->allocated
*= 2;
315 buffer
->data
= readerinput_realloc(readerinput
, buffer
->data
, buffer
->allocated
);
316 len
= buffer
->allocated
- buffer
->written
;
319 hr
= ISequentialStream_Read(readerinput
->stream
, buffer
->data
+ buffer
->written
, len
, &read
);
320 if (FAILED(hr
)) return hr
;
321 TRACE("requested %d, read %d, ret 0x%08x\n", len
, read
, hr
);
322 buffer
->written
+= read
;
327 static xml_encoding
readerinput_detectencoding(xmlreaderinput
*readerinput
)
329 encoded_buffer
*buffer
= &readerinput
->buffer
->encoded
;
331 /* try start symbols if we have enough data to do that, input buffer should contain
332 first chunk already */
333 if (buffer
->written
>= 4)
335 static char startA
[] = {'<','?','x','m'};
336 static WCHAR startW
[] = {'<','?'};
338 if (!memcmp(buffer
->data
, startA
, sizeof(startA
))) return XmlEncoding_UTF8
;
339 if (!memcmp(buffer
->data
, startW
, sizeof(startW
))) return XmlEncoding_UTF16
;
342 /* try with BOM now */
343 if (buffer
->written
>= 3)
345 static char utf8bom
[] = {0xef,0xbb,0xbf};
346 static char utf16lebom
[] = {0xff,0xfe};
347 if (!memcmp(buffer
->data
, utf8bom
, sizeof(utf8bom
))) return XmlEncoding_UTF8
;
348 if (!memcmp(buffer
->data
, utf16lebom
, sizeof(utf16lebom
))) return XmlEncoding_UTF16
;
351 return XmlEncoding_Unknown
;
354 static HRESULT WINAPI
xmlreader_QueryInterface(IXmlReader
*iface
, REFIID riid
, void** ppvObject
)
356 xmlreader
*This
= impl_from_IXmlReader(iface
);
358 TRACE("%p %s %p\n", This
, debugstr_guid(riid
), ppvObject
);
360 if (IsEqualGUID(riid
, &IID_IUnknown
) ||
361 IsEqualGUID(riid
, &IID_IXmlReader
))
367 FIXME("interface %s not implemented\n", debugstr_guid(riid
));
368 return E_NOINTERFACE
;
371 IXmlReader_AddRef(iface
);
376 static ULONG WINAPI
xmlreader_AddRef(IXmlReader
*iface
)
378 xmlreader
*This
= impl_from_IXmlReader(iface
);
379 ULONG ref
= InterlockedIncrement(&This
->ref
);
380 TRACE("(%p)->(%d)\n", This
, ref
);
384 static ULONG WINAPI
xmlreader_Release(IXmlReader
*iface
)
386 xmlreader
*This
= impl_from_IXmlReader(iface
);
387 LONG ref
= InterlockedDecrement(&This
->ref
);
389 TRACE("(%p)->(%d)\n", This
, ref
);
393 IMalloc
*imalloc
= This
->imalloc
;
394 if (This
->input
) IUnknown_Release(&This
->input
->IXmlReaderInput_iface
);
395 reader_free(This
, This
);
396 if (imalloc
) IMalloc_Release(imalloc
);
402 static HRESULT WINAPI
xmlreader_SetInput(IXmlReader
* iface
, IUnknown
*input
)
404 xmlreader
*This
= impl_from_IXmlReader(iface
);
407 TRACE("(%p %p)\n", This
, input
);
411 readerinput_release_stream(This
->input
);
412 IUnknown_Release(&This
->input
->IXmlReaderInput_iface
);
416 This
->line
= This
->pos
= 0;
418 /* just reset current input */
421 This
->state
= XmlReadState_Initial
;
425 /* now try IXmlReaderInput, ISequentialStream, IStream */
426 hr
= IUnknown_QueryInterface(input
, &IID_IXmlReaderInput
, (void**)&This
->input
);
429 IXmlReaderInput
*readerinput
;
431 /* create IXmlReaderInput basing on supplied interface */
432 hr
= CreateXmlReaderInputWithEncodingName(input
,
433 NULL
, NULL
, FALSE
, NULL
, &readerinput
);
434 if (hr
!= S_OK
) return hr
;
435 This
->input
= impl_from_IXmlReaderInput(readerinput
);
438 /* set stream for supplied IXmlReaderInput */
439 hr
= readerinput_query_for_stream(This
->input
);
441 This
->state
= XmlReadState_Initial
;
446 static HRESULT WINAPI
xmlreader_GetProperty(IXmlReader
* iface
, UINT property
, LONG_PTR
*value
)
448 xmlreader
*This
= impl_from_IXmlReader(iface
);
450 TRACE("(%p %u %p)\n", This
, property
, value
);
452 if (!value
) return E_INVALIDARG
;
456 case XmlReaderProperty_DtdProcessing
:
457 *value
= This
->dtdmode
;
459 case XmlReaderProperty_ReadState
:
460 *value
= This
->state
;
463 FIXME("Unimplemented property (%u)\n", property
);
470 static HRESULT WINAPI
xmlreader_SetProperty(IXmlReader
* iface
, UINT property
, LONG_PTR value
)
472 xmlreader
*This
= impl_from_IXmlReader(iface
);
474 TRACE("(%p %u %lu)\n", iface
, property
, value
);
478 case XmlReaderProperty_DtdProcessing
:
479 if (value
< 0 || value
> _DtdProcessing_Last
) return E_INVALIDARG
;
480 This
->dtdmode
= value
;
483 FIXME("Unimplemented property (%u)\n", property
);
490 static HRESULT WINAPI
xmlreader_Read(IXmlReader
* iface
, XmlNodeType
*node_type
)
492 xmlreader
*This
= impl_from_IXmlReader(iface
);
494 FIXME("(%p)->(%p): stub\n", This
, node_type
);
496 if (This
->state
== XmlReadState_Closed
) return S_FALSE
;
498 /* if it's a first call for a new input we need to detect stream encoding */
499 if (This
->state
== XmlReadState_Initial
)
504 hr
= readerinput_growraw(This
->input
);
505 if (FAILED(hr
)) return hr
;
507 /* try to detect encoding by BOM or data and set input code page */
508 enc
= readerinput_detectencoding(This
->input
);
509 TRACE("detected encoding %d\n", enc
);
510 get_code_page(enc
, This
->input
);
516 static HRESULT WINAPI
xmlreader_GetNodeType(IXmlReader
* iface
, XmlNodeType
*node_type
)
518 xmlreader
*This
= impl_from_IXmlReader(iface
);
519 TRACE("(%p)->(%p)\n", This
, node_type
);
520 *node_type
= This
->nodetype
;
521 return This
->state
== XmlReadState_Closed
? S_FALSE
: S_OK
;
524 static HRESULT WINAPI
xmlreader_MoveToFirstAttribute(IXmlReader
* iface
)
526 FIXME("(%p): stub\n", iface
);
530 static HRESULT WINAPI
xmlreader_MoveToNextAttribute(IXmlReader
* iface
)
532 FIXME("(%p): stub\n", iface
);
536 static HRESULT WINAPI
xmlreader_MoveToAttributeByName(IXmlReader
* iface
,
538 LPCWSTR namespaceUri
)
540 FIXME("(%p %p %p): stub\n", iface
, local_name
, namespaceUri
);
544 static HRESULT WINAPI
xmlreader_MoveToElement(IXmlReader
* iface
)
546 FIXME("(%p): stub\n", iface
);
550 static HRESULT WINAPI
xmlreader_GetQualifiedName(IXmlReader
* iface
, LPCWSTR
*qualifiedName
,
551 UINT
*qualifiedName_length
)
553 FIXME("(%p %p %p): stub\n", iface
, qualifiedName
, qualifiedName_length
);
557 static HRESULT WINAPI
xmlreader_GetNamespaceUri(IXmlReader
* iface
,
558 LPCWSTR
*namespaceUri
,
559 UINT
*namespaceUri_length
)
561 FIXME("(%p %p %p): stub\n", iface
, namespaceUri
, namespaceUri_length
);
565 static HRESULT WINAPI
xmlreader_GetLocalName(IXmlReader
* iface
,
567 UINT
*local_name_length
)
569 FIXME("(%p %p %p): stub\n", iface
, local_name
, local_name_length
);
573 static HRESULT WINAPI
xmlreader_GetPrefix(IXmlReader
* iface
,
577 FIXME("(%p %p %p): stub\n", iface
, prefix
, prefix_length
);
581 static HRESULT WINAPI
xmlreader_GetValue(IXmlReader
* iface
,
585 FIXME("(%p %p %p): stub\n", iface
, value
, value_length
);
589 static HRESULT WINAPI
xmlreader_ReadValueChunk(IXmlReader
* iface
,
594 FIXME("(%p %p %u %p): stub\n", iface
, buffer
, chunk_size
, read
);
598 static HRESULT WINAPI
xmlreader_GetBaseUri(IXmlReader
* iface
,
600 UINT
*baseUri_length
)
602 FIXME("(%p %p %p): stub\n", iface
, baseUri
, baseUri_length
);
606 static BOOL WINAPI
xmlreader_IsDefault(IXmlReader
* iface
)
608 FIXME("(%p): stub\n", iface
);
612 static BOOL WINAPI
xmlreader_IsEmptyElement(IXmlReader
* iface
)
614 FIXME("(%p): stub\n", iface
);
618 static HRESULT WINAPI
xmlreader_GetLineNumber(IXmlReader
* iface
, UINT
*lineNumber
)
620 xmlreader
*This
= impl_from_IXmlReader(iface
);
622 TRACE("(%p %p)\n", This
, lineNumber
);
624 if (!lineNumber
) return E_INVALIDARG
;
626 *lineNumber
= This
->line
;
631 static HRESULT WINAPI
xmlreader_GetLinePosition(IXmlReader
* iface
, UINT
*linePosition
)
633 xmlreader
*This
= impl_from_IXmlReader(iface
);
635 TRACE("(%p %p)\n", This
, linePosition
);
637 if (!linePosition
) return E_INVALIDARG
;
639 *linePosition
= This
->pos
;
644 static HRESULT WINAPI
xmlreader_GetAttributeCount(IXmlReader
* iface
, UINT
*attributeCount
)
646 FIXME("(%p %p): stub\n", iface
, attributeCount
);
650 static HRESULT WINAPI
xmlreader_GetDepth(IXmlReader
* iface
, UINT
*depth
)
652 FIXME("(%p %p): stub\n", iface
, depth
);
656 static BOOL WINAPI
xmlreader_IsEOF(IXmlReader
* iface
)
658 FIXME("(%p): stub\n", iface
);
662 static const struct IXmlReaderVtbl xmlreader_vtbl
=
664 xmlreader_QueryInterface
,
668 xmlreader_GetProperty
,
669 xmlreader_SetProperty
,
671 xmlreader_GetNodeType
,
672 xmlreader_MoveToFirstAttribute
,
673 xmlreader_MoveToNextAttribute
,
674 xmlreader_MoveToAttributeByName
,
675 xmlreader_MoveToElement
,
676 xmlreader_GetQualifiedName
,
677 xmlreader_GetNamespaceUri
,
678 xmlreader_GetLocalName
,
681 xmlreader_ReadValueChunk
,
682 xmlreader_GetBaseUri
,
684 xmlreader_IsEmptyElement
,
685 xmlreader_GetLineNumber
,
686 xmlreader_GetLinePosition
,
687 xmlreader_GetAttributeCount
,
692 /** IXmlReaderInput **/
693 static HRESULT WINAPI
xmlreaderinput_QueryInterface(IXmlReaderInput
*iface
, REFIID riid
, void** ppvObject
)
695 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
697 TRACE("%p %s %p\n", This
, debugstr_guid(riid
), ppvObject
);
699 if (IsEqualGUID(riid
, &IID_IXmlReaderInput
) ||
700 IsEqualGUID(riid
, &IID_IUnknown
))
706 WARN("interface %s not implemented\n", debugstr_guid(riid
));
707 return E_NOINTERFACE
;
710 IUnknown_AddRef(iface
);
715 static ULONG WINAPI
xmlreaderinput_AddRef(IXmlReaderInput
*iface
)
717 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
718 ULONG ref
= InterlockedIncrement(&This
->ref
);
719 TRACE("(%p)->(%d)\n", This
, ref
);
723 static ULONG WINAPI
xmlreaderinput_Release(IXmlReaderInput
*iface
)
725 xmlreaderinput
*This
= impl_from_IXmlReaderInput(iface
);
726 LONG ref
= InterlockedDecrement(&This
->ref
);
728 TRACE("(%p)->(%d)\n", This
, ref
);
732 IMalloc
*imalloc
= This
->imalloc
;
733 if (This
->input
) IUnknown_Release(This
->input
);
734 if (This
->stream
) ISequentialStream_Release(This
->stream
);
735 if (This
->buffer
) free_input_buffer(This
->buffer
);
736 readerinput_free(This
, This
->baseuri
);
737 readerinput_free(This
, This
);
738 if (imalloc
) IMalloc_Release(imalloc
);
744 static const struct IUnknownVtbl xmlreaderinput_vtbl
=
746 xmlreaderinput_QueryInterface
,
747 xmlreaderinput_AddRef
,
748 xmlreaderinput_Release
751 HRESULT WINAPI
CreateXmlReader(REFIID riid
, void **obj
, IMalloc
*imalloc
)
755 TRACE("(%s, %p, %p)\n", wine_dbgstr_guid(riid
), obj
, imalloc
);
757 if (!IsEqualGUID(riid
, &IID_IXmlReader
))
759 ERR("Unexpected IID requested -> (%s)\n", wine_dbgstr_guid(riid
));
764 reader
= IMalloc_Alloc(imalloc
, sizeof(*reader
));
766 reader
= heap_alloc(sizeof(*reader
));
767 if(!reader
) return E_OUTOFMEMORY
;
769 reader
->IXmlReader_iface
.lpVtbl
= &xmlreader_vtbl
;
771 reader
->input
= NULL
;
772 reader
->state
= XmlReadState_Closed
;
773 reader
->dtdmode
= DtdProcessing_Prohibit
;
774 reader
->line
= reader
->pos
= 0;
775 reader
->imalloc
= imalloc
;
776 if (imalloc
) IMalloc_AddRef(imalloc
);
777 reader
->nodetype
= XmlNodeType_None
;
779 *obj
= &reader
->IXmlReader_iface
;
781 TRACE("returning iface %p\n", *obj
);
786 HRESULT WINAPI
CreateXmlReaderInputWithEncodingName(IUnknown
*stream
,
791 IXmlReaderInput
**ppInput
)
793 xmlreaderinput
*readerinput
;
796 TRACE("%p %p %s %d %s %p\n", stream
, imalloc
, wine_dbgstr_w(encoding
),
797 hint
, wine_dbgstr_w(base_uri
), ppInput
);
799 if (!stream
|| !ppInput
) return E_INVALIDARG
;
802 readerinput
= IMalloc_Alloc(imalloc
, sizeof(*readerinput
));
804 readerinput
= heap_alloc(sizeof(*readerinput
));
805 if(!readerinput
) return E_OUTOFMEMORY
;
807 readerinput
->IXmlReaderInput_iface
.lpVtbl
= &xmlreaderinput_vtbl
;
808 readerinput
->ref
= 1;
809 readerinput
->imalloc
= imalloc
;
810 readerinput
->stream
= NULL
;
811 if (imalloc
) IMalloc_AddRef(imalloc
);
812 readerinput
->encoding
= parse_encoding_name(encoding
);
813 readerinput
->hint
= hint
;
814 readerinput
->baseuri
= readerinput_strdupW(readerinput
, base_uri
);
816 hr
= alloc_input_buffer(readerinput
);
819 readerinput_free(readerinput
, readerinput
);
822 IUnknown_QueryInterface(stream
, &IID_IUnknown
, (void**)&readerinput
->input
);
824 *ppInput
= &readerinput
->IXmlReaderInput_iface
;
826 TRACE("returning iface %p\n", *ppInput
);