diff --git a/Sources/apk/Index/ApkIndexUpdate.swift b/Sources/apk/Index/ApkIndexUpdate.swift index 3593833..924fafe 100644 --- a/Sources/apk/Index/ApkIndexUpdate.swift +++ b/Sources/apk/Index/ApkIndexUpdate.swift @@ -104,7 +104,7 @@ public struct ApkIndexUpdater { } return try ApkIndex(raw: - try ApkRawIndex(lines: TextInputStream(binaryStream: MemoryInputStream(buffer: apkIndexFile)).lines)) + try ApkRawIndex(lines: MemoryInputStream(buffer: apkIndexFile).lines)) } } diff --git a/Sources/apk/Utility/TextInputStream.swift b/Sources/apk/Utility/TextInputStream.swift deleted file mode 100644 index 0389542..0000000 --- a/Sources/apk/Utility/TextInputStream.swift +++ /dev/null @@ -1,93 +0,0 @@ -/* - * darwin-apk © 2024 Gay Pizza Specifications - * SPDX-License-Identifier: Apache-2.0 - */ - -import System - -struct TextInputStream where InStream.Element == UInt8 { - private var _stream: InStream - - public init(binaryStream: InStream) { - _stream = binaryStream - } - - public var lines: LineSequence { - LineSequence(_stream: self._stream) - } - - public struct LineSequence: Sequence { - public typealias Element = String - - fileprivate var _stream: InStream - - public struct Iterator: IteratorProtocol { - public typealias Element = String - - fileprivate init(stream: InStream) { - self._stream = stream - } - - private var _stream: InStream - private var _bytes = [UInt8]() - private var _lastChar: UInt8? = nil - private var _eof = false - - @inline(__always) private mutating func readRawLine() { - if let first = self._lastChar { - // Add any holdovers from reading the previous line to the start of this one - self._bytes.append(first) - self._lastChar = nil - } - - while true { - guard let nextChar = self._stream.next() else { - self._eof = true - break - } - if nextChar == 0x0A { // "\n" - break - } else if nextChar == 0x0D { // "\r" - // Match CRLF to avoid double newlines when dealing with DOS-based text - let lookAhead = self._stream.next() - if _slowPath(lookAhead != 0x0A) { - // If it wasn't an LF then queue it for the next line - self._lastChar = nextChar - } - break - } - self._bytes.append(nextChar) - } - } - - public mutating func next() -> String? { - // Return early if we already hit the end of the stream - guard !self._eof else { - return nil - } - - // Read raw bytes until newline - self.readRawLine() - defer { - self._bytes.removeAll(keepingCapacity: true) - } - - if _fastPath(!self._bytes.isEmpty) { - // Convert and return line - return String(decoding: self._bytes, as: UTF8.self) - } else { - if _fastPath(!self._eof) { - // Don't bother decoding empty lines and just return an empty string - return "" - } - // Ignore the final empty newline - return nil - } - } - } - - public func makeIterator() -> Iterator { - Iterator(stream: self._stream) - } - } -} diff --git a/Sources/apk/Utility/TextLineSequence.swift b/Sources/apk/Utility/TextLineSequence.swift new file mode 100644 index 0000000..d8c6dee --- /dev/null +++ b/Sources/apk/Utility/TextLineSequence.swift @@ -0,0 +1,91 @@ +/* + * darwin-apk © 2024 Gay Pizza Specifications + * SPDX-License-Identifier: Apache-2.0 + */ + +import System + +internal struct TextLineSequence: Sequence where Base.Element == UInt8 { + public typealias Element = String + + private var _stream: Base + + fileprivate init(_ stream: Base) { + self._stream = stream + } + + public struct Iterator: IteratorProtocol { + public typealias Element = String + + fileprivate init(base stream: Base) { + self._stream = stream + } + + private var _stream: Base + private var _bytes = [UInt8]() + private var _lastChar: UInt8? = nil + private var _eof = false + + @inline(__always) private mutating func readRawLine() { + if let first = self._lastChar { + // Add any holdovers from reading the previous line to the start of this one + self._bytes.append(first) + self._lastChar = nil + } + + while true { + guard let nextChar = self._stream.next() else { + self._eof = true + break + } + if nextChar == 0x0A { // "\n" + break + } else if nextChar == 0x0D { // "\r" + // Match CRLF to avoid double newlines when dealing with DOS-based text + let lookAhead = self._stream.next() + if _slowPath(lookAhead != 0x0A) { + // If it wasn't an LF then queue it for the next line + self._lastChar = nextChar + } + break + } + self._bytes.append(nextChar) + } + } + + public mutating func next() -> String? { + // Return early if we already hit the end of the stream + guard !self._eof else { + return nil + } + + // Read raw bytes until newline + self.readRawLine() + defer { + self._bytes.removeAll(keepingCapacity: true) + } + + if _fastPath(!self._bytes.isEmpty) { + // Convert and return line + return String(decoding: self._bytes, as: UTF8.self) + } else { + if _fastPath(!self._eof) { + // Don't bother decoding empty lines and just return an empty string + return "" + } + // Ignore the final empty newline + return nil + } + } + } + + public func makeIterator() -> Iterator { + Iterator(base: self._stream) + } +} + +internal extension InputStream { + var lines: TextLineSequence { + .init(self) + } +}