1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 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
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
public import Foundation
#if canImport(System)
import System
#else
import SystemPackage
#endif
public final class LineReader {
private let delimiter: Data
private let bufferSize: Int
private var fileHandle: FileDescriptor
private var buffer: Data
private var eof: Bool = false
public init(forReadingFrom url: URL, delimiter: String = "\n", bufferSize: Int = 1024) throws {
self.delimiter = Data(delimiter.utf8)
self.bufferSize = bufferSize
fileHandle = try FileDescriptor.open(FilePath(url.filePath.str), .readOnly)
buffer = Data(capacity: bufferSize)
}
deinit {
try? fileHandle.close()
}
public func rewind() throws {
try fileHandle.seek(offset: 0, from: .start)
buffer.removeAll(keepingCapacity: true)
eof = false
}
public func readLine() throws -> String? {
guard !eof else {
return nil
}
let tmpBuffer = UnsafeMutableRawBufferPointer.allocate(byteCount: bufferSize, alignment: 1)
defer { tmpBuffer.deallocate() }
repeat {
if let range = buffer.range(of: delimiter) {
let line = String(decoding: buffer.subdata(in: buffer.startIndex..<range.lowerBound), as: UTF8.self)
buffer.removeSubrange(buffer.startIndex..<range.upperBound)
return line
} else {
let count = try fileHandle.read(into: tmpBuffer)
if count == 0 {
eof = true
return !buffer.isEmpty ? String(decoding: buffer, as: UTF8.self) : nil
}
buffer.append(Data(tmpBuffer[0..<count]))
}
} while true
}
}
@available(*, unavailable)
extension LineReader: Sendable { }
|