diff --git a/CoreFoundation/Parsing.subproj/CFXMLInterface.c b/CoreFoundation/Parsing.subproj/CFXMLInterface.c index 65e277fef7..0dac7dd401 100644 --- a/CoreFoundation/Parsing.subproj/CFXMLInterface.c +++ b/CoreFoundation/Parsing.subproj/CFXMLInterface.c @@ -1,6 +1,6 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -8,7 +8,7 @@ // /* CFXMLInterface.c - Copyright (c) 2015 Apple Inc. and the Swift project authors + Copyright (c) 2020 Apple Inc. and the Swift project authors */ #include @@ -210,7 +210,8 @@ void _CFXMLInterfaceCtxtUseOptions(_CFXMLInterfaceParserContext ctx, CFIndex opt } int _CFXMLInterfaceParseChunk(_CFXMLInterfaceParserContext ctxt, const char *chunk, int size, int terminate) { - return xmlParseChunk(ctxt, chunk, size, terminate); + int ret = xmlParseChunk(ctxt, chunk, size, terminate); + return ret == XML_ERR_DOCUMENT_END && terminate ? XML_ERR_OK : ret; } void _CFXMLInterfaceStopParser(_CFXMLInterfaceParserContext ctx) { diff --git a/Sources/FoundationXML/XMLParser.swift b/Sources/FoundationXML/XMLParser.swift index 7acc76b852..232f96093f 100644 --- a/Sources/FoundationXML/XMLParser.swift +++ b/Sources/FoundationXML/XMLParser.swift @@ -1,6 +1,6 @@ // This source file is part of the Swift.org open source project // -// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information @@ -486,8 +486,9 @@ open class XMLParser : NSObject { return false } - internal func parseData(_ data: Data) -> Bool { + internal func parseData(_ data: Data, lastChunkOfData: Bool = false) -> Bool { _CFXMLInterfaceSetStructuredErrorFunc(interface, _structuredErrorFunc) + defer { _CFXMLInterfaceSetStructuredErrorFunc(interface, nil) } let handler: _CFXMLInterfaceSAXHandler? = (delegate != nil ? _handler : nil) let unparsedData: Data @@ -504,7 +505,7 @@ open class XMLParser : NSObject { // If we have not received 4 bytes, save the bomChunk for next pass if bomChunk.count < 4 { _bomChunk = bomChunk - return false + return true } // Prepare options (substitute entities, recover on errors) var options = _kCFXMLInterfaceRecover | _kCFXMLInterfaceNoEnt @@ -520,6 +521,12 @@ open class XMLParser : NSObject { let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: CChar.self) _parserContext = _CFXMLInterfaceCreatePushParserCtxt(handler, interface, bytes, 4, nil) } + guard _parserContext != nil else { + if _parserError == nil { + _parserError = NSError(domain: XMLParser.errorDomain, code: ErrorCode.outOfMemoryError.rawValue) + } + return false + }; _CFXMLInterfaceCtxtUseOptions(_parserContext, options) // Prepare the remaining data for parsing let dataRange = bomChunk.indices @@ -532,57 +539,54 @@ open class XMLParser : NSObject { let parseResult = unparsedData.withUnsafeBytes { (rawBuffer: UnsafeRawBufferPointer) -> Int32 in let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: CChar.self) - return _CFXMLInterfaceParseChunk(_parserContext, bytes, Int32(unparsedData.count), 0) + return _CFXMLInterfaceParseChunk(_parserContext, bytes, Int32(unparsedData.count), lastChunkOfData ? 1 : 0) } let result = _handleParseResult(parseResult) - _CFXMLInterfaceSetStructuredErrorFunc(interface, nil) return result } - internal func parseFromStream() -> Bool { + internal func parseFrom(_ stream : InputStream) -> Bool { var result = true - XMLParser.setCurrentParser(self) - defer { XMLParser.setCurrentParser(nil) } - if let stream = _stream { - stream.open() - defer { stream.close() } - let buffer = malloc(_chunkSize)!.bindMemory(to: UInt8.self, capacity: _chunkSize) - defer { free(buffer) } - var len = stream.read(buffer, maxLength: _chunkSize) - if len != -1 { - while len > 0 { - let data = Data(bytesNoCopy: buffer, count: len, deallocator: .none) - result = parseData(data) - len = stream.read(buffer, maxLength: _chunkSize) - } - } else { + + guard let buffer = malloc(_chunkSize)?.bindMemory(to: UInt8.self, capacity: _chunkSize) else { return false } + defer { free(buffer) } + + stream.open() + defer { stream.close() } + parseLoop: while result { + switch stream.read(buffer, maxLength: _chunkSize) { + case let len where len > 0: + let data = Data(bytesNoCopy: buffer, count: len, deallocator: .none) + result = parseData(data) + case 0: + result = parseData(Data(), lastChunkOfData: true) + break parseLoop + default: // See SR-13516, should be `case ..<0:` result = false - } - } else if var data = _data { - let buffer = malloc(_chunkSize)!.bindMemory(to: UInt8.self, capacity: _chunkSize) - defer { free(buffer) } - var range = NSRange(location: 0, length: min(_chunkSize, data.count)) - while result { - let chunk = data.withUnsafeMutableBytes { (rawBuffer: UnsafeMutableRawBufferPointer) -> Data in - let ptr = rawBuffer.baseAddress!.advanced(by: range.location) - return Data(bytesNoCopy: ptr, count: range.length, deallocator: .none) - } - result = parseData(chunk) - if range.location + range.length >= data.count { - break + if _parserError == nil { + _parserError = stream.streamError } - range = NSRange(location: range.location + range.length, length: min(_chunkSize, data.count - (range.location + range.length))) + + break parseLoop } - } else { - result = false } + return result } // called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error. open func parse() -> Bool { - return parseFromStream() + XMLParser.setCurrentParser(self) + defer { XMLParser.setCurrentParser(nil) } + + if _stream != nil { + return parseFrom(_stream!) + } else if _data != nil { + return parseData(_data!, lastChunkOfData: true) + } + + return false } // called by the delegate to stop the parse. The delegate will get an error message sent to it. diff --git a/Tests/Foundation/Tests/TestXMLParser.swift b/Tests/Foundation/Tests/TestXMLParser.swift index 240dc1f9c1..e013829339 100644 --- a/Tests/Foundation/Tests/TestXMLParser.swift +++ b/Tests/Foundation/Tests/TestXMLParser.swift @@ -95,6 +95,7 @@ class TestXMLParser : XCTestCase { .foundCharacters("bar"), .didEndElement("foo", uri, namespaces ? "foo" : nil), .didEndElement("test", uri, namespaces ? "test" : nil), + .endDocument, ] }