Skip to content

Commit af8f0c2

Browse files
committed
Improved code clearity: parseFromStream also parsed from Data.
Fixed: Malloc errors were unchecked. Fixed: Out of memory when creating context was ignored. Improved: Parsing from data allocated memory that was never used. Fixed: Parsing from InputStream ignored the parser return value. Fixed: Reading the BOM returned an error if the BOM is not returned in one chunck from InputStream, e.g. when InputStreams returns one byte after the other on read.
1 parent 807e415 commit af8f0c2

File tree

1 file changed

+38
-35
lines changed

1 file changed

+38
-35
lines changed

Sources/FoundationXML/XMLParser.swift

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// This source file is part of the Swift.org open source project
22
//
3-
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
3+
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
44
// Licensed under Apache License v2.0 with Runtime Library Exception
55
//
66
// See http://swift.org/LICENSE.txt for license information
@@ -504,7 +504,7 @@ open class XMLParser : NSObject {
504504
// If we have not received 4 bytes, save the bomChunk for next pass
505505
if bomChunk.count < 4 {
506506
_bomChunk = bomChunk
507-
return false
507+
return true
508508
}
509509
// Prepare options (substitute entities, recover on errors)
510510
var options = _kCFXMLInterfaceRecover | _kCFXMLInterfaceNoEnt
@@ -520,6 +520,12 @@ open class XMLParser : NSObject {
520520
let bytes = rawBuffer.baseAddress!.assumingMemoryBound(to: CChar.self)
521521
_parserContext = _CFXMLInterfaceCreatePushParserCtxt(handler, interface, bytes, 4, nil)
522522
}
523+
guard _parserContext != nil else {
524+
if _parserError == nil {
525+
_parserError = NSError(domain: XMLParser.errorDomain, code: ErrorCode.outOfMemoryError.rawValue)
526+
}
527+
return false
528+
};
523529
_CFXMLInterfaceCtxtUseOptions(_parserContext, options)
524530
// Prepare the remaining data for parsing
525531
let dataRange = bomChunk.indices
@@ -540,49 +546,46 @@ open class XMLParser : NSObject {
540546
return result
541547
}
542548

543-
internal func parseFromStream() -> Bool {
549+
internal func parseFrom(_ stream : InputStream) -> Bool {
544550
var result = true
545-
XMLParser.setCurrentParser(self)
546-
defer { XMLParser.setCurrentParser(nil) }
547-
if let stream = _stream {
548-
stream.open()
549-
defer { stream.close() }
550-
let buffer = malloc(_chunkSize)!.bindMemory(to: UInt8.self, capacity: _chunkSize)
551-
defer { free(buffer) }
552-
var len = stream.read(buffer, maxLength: _chunkSize)
553-
if len != -1 {
554-
while len > 0 {
555-
let data = Data(bytesNoCopy: buffer, count: len, deallocator: .none)
556-
result = parseData(data)
557-
len = stream.read(buffer, maxLength: _chunkSize)
558-
}
559-
} else {
551+
552+
guard let buffer = malloc(_chunkSize)?.bindMemory(to: UInt8.self, capacity: _chunkSize) else { return false }
553+
defer { free(buffer) }
554+
555+
stream.open()
556+
defer { stream.close() }
557+
parseLoop: while result {
558+
switch stream.read(buffer, maxLength: _chunkSize) {
559+
case let len where len > 0:
560+
let data = Data(bytesNoCopy: buffer, count: len, deallocator: .none)
561+
result = parseData(data)
562+
case 0:
563+
break parseLoop
564+
default: // See SR-13516, should be `case ..<0:`
560565
result = false
561-
}
562-
} else if var data = _data {
563-
let buffer = malloc(_chunkSize)!.bindMemory(to: UInt8.self, capacity: _chunkSize)
564-
defer { free(buffer) }
565-
var range = NSRange(location: 0, length: min(_chunkSize, data.count))
566-
while result {
567-
let chunk = data.withUnsafeMutableBytes { (rawBuffer: UnsafeMutableRawBufferPointer) -> Data in
568-
let ptr = rawBuffer.baseAddress!.advanced(by: range.location)
569-
return Data(bytesNoCopy: ptr, count: range.length, deallocator: .none)
570-
}
571-
result = parseData(chunk)
572-
if range.location + range.length >= data.count {
573-
break
566+
if _parserError == nil {
567+
_parserError = stream.streamError
574568
}
575-
range = NSRange(location: range.location + range.length, length: min(_chunkSize, data.count - (range.location + range.length)))
569+
570+
break parseLoop
576571
}
577-
} else {
578-
result = false
579572
}
573+
580574
return result
581575
}
582576

583577
// called to start the event-driven parse. Returns YES in the event of a successful parse, and NO in case of error.
584578
open func parse() -> Bool {
585-
return parseFromStream()
579+
XMLParser.setCurrentParser(self)
580+
defer { XMLParser.setCurrentParser(nil) }
581+
582+
if _stream != nil {
583+
return parseFrom(_stream!)
584+
} else if _data != nil {
585+
return parseData(_data!)
586+
}
587+
588+
return false
586589
}
587590

588591
// called by the delegate to stop the parse. The delegate will get an error message sent to it.

0 commit comments

Comments
 (0)