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