import Foundation enum CostUsageJsonl { struct Line { let bytes: Data let wasTruncated: Bool } @discardableResult static func scan( fileURL: URL, offset: Int64 = 0, maxLineBytes: Int, prefixBytes: Int, onLine: (Line) -> Void) throws -> Int64 { try self.scan( fileURL: fileURL, offset: offset, maxLineBytes: maxLineBytes, prefixBytes: prefixBytes, checkCancellation: nil, onLine: onLine) } @discardableResult static func scan( fileURL: URL, offset: Int64 = 0, maxLineBytes: Int, prefixBytes: Int, checkCancellation: (() throws -> Void)? = nil, onLine: (Line) -> Void) throws -> Int64 { let handle = try FileHandle(forReadingFrom: fileURL) defer { try? handle.close() } let startOffset = max(0, offset) if startOffset > 0 { try handle.seek(toOffset: UInt64(startOffset)) } var current = Data() current.reserveCapacity(4 * 1024) var lineBytes = 0 var truncated = false var bytesRead: Int64 = 0 func appendSegment(_ bytes: UnsafePointer<UInt8>, count: Int) { guard count > 0 else { return } lineBytes += count if current.count < prefixBytes { let appendCount = min(prefixBytes - current.count, count) if appendCount > 0 { current.append(bytes, count: appendCount) } } if lineBytes > maxLineBytes || lineBytes > prefixBytes { truncated = true } } func flushLine() { guard lineBytes > 0 else { return } let line = Line(bytes: current, wasTruncated: truncated) onLine(line) current.removeAll(keepingCapacity: true) lineBytes = 0 truncated = false } while true { try checkCancellation?() let reachedEOF = try autoreleasepool { let chunk = try handle.read(upToCount: 256 * 1024) ?? Data() if chunk.isEmpty { flushLine() return true } try checkCancellation?() bytesRead += Int64(chunk.count) chunk.withUnsafeBytes { rawBuffer in guard let base = rawBuffer.bindMemory(to: UInt8.self).baseAddress else { return } var segmentStart = 0 var index = 0 while index < rawBuffer.count { if base[index] == 0x0A { appendSegment(base.advanced(by: segmentStart), count: index - segmentStart) flushLine() segmentStart = index + 1 } index += 1 } if segmentStart < rawBuffer.count { appendSegment(base.advanced(by: segmentStart), count: rawBuffer.count - segmentStart) } } return false } if reachedEOF { break } try checkCancellation?() } return startOffset + bytesRead }}
import Foundation
enum CostUsageJsonl {
struct Line {
let bytes: Data
let wasTruncated: Bool
}
@discardableResult
static func scan(
fileURL: URL,
offset: Int64 = 0,
maxLineBytes: Int,
prefixBytes: Int,
onLine: (Line) -> Void) throws
-> Int64
{
try self.scan(
fileURL: fileURL,
offset: offset,
maxLineBytes: maxLineBytes,
prefixBytes: prefixBytes,
checkCancellation: nil,
onLine: onLine)
}
@discardableResult
static func scan(
fileURL: URL,
offset: Int64 = 0,
maxLineBytes: Int,
prefixBytes: Int,
checkCancellation: (() throws -> Void)? = nil,
onLine: (Line) -> Void) throws
-> Int64
{
let handle = try FileHandle(forReadingFrom: fileURL)
defer { try? handle.close() }
let startOffset = max(0, offset)
if startOffset > 0 {
try handle.seek(toOffset: UInt64(startOffset))
}
var current = Data()
current.reserveCapacity(4 * 1024)
var lineBytes = 0
var truncated = false
var bytesRead: Int64 = 0
func appendSegment(_ bytes: UnsafePointer<UInt8>, count: Int) {
guard count > 0 else { return }
lineBytes += count
if current.count < prefixBytes {
let appendCount = min(prefixBytes - current.count, count)
if appendCount > 0 {
current.append(bytes, count: appendCount)
}
}
if lineBytes > maxLineBytes || lineBytes > prefixBytes {
truncated = true
}
}
func flushLine() {
guard lineBytes > 0 else { return }
let line = Line(bytes: current, wasTruncated: truncated)
onLine(line)
current.removeAll(keepingCapacity: true)
lineBytes = 0
truncated = false
}
while true {
try checkCancellation?()
let reachedEOF = try autoreleasepool {
let chunk = try handle.read(upToCount: 256 * 1024) ?? Data()
if chunk.isEmpty {
flushLine()
return true
}
try checkCancellation?()
bytesRead += Int64(chunk.count)
chunk.withUnsafeBytes { rawBuffer in
guard let base = rawBuffer.bindMemory(to: UInt8.self).baseAddress else { return }
var segmentStart = 0
var index = 0
while index < rawBuffer.count {
if base[index] == 0x0A {
appendSegment(base.advanced(by: segmentStart), count: index - segmentStart)
flushLine()
segmentStart = index + 1
}
index += 1
}
if segmentStart < rawBuffer.count {
appendSegment(base.advanced(by: segmentStart), count: rawBuffer.count - segmentStart)
}
}
return false
}
if reachedEOF { break }
try checkCancellation?()
}
return startOffset + bytesRead
}
}