Skip to content

Instantly share code, notes, and snippets.

@mingsai
Last active February 12, 2016 14:53
Show Gist options
  • Save mingsai/57e3d998780f57697088 to your computer and use it in GitHub Desktop.
Save mingsai/57e3d998780f57697088 to your computer and use it in GitHub Desktop.
A reader to quickly process data using a stream
extension NSData {
func mng_enumerateComponentsSeparatedBy(delimiter:NSData, block:(data:NSData,finalBlock:Bool) -> () ){
var loc:Int = 0
while true {
let rangeOfNewline = self.rangeOfData(delimiter, options: NSDataSearchOptions.Anchored, range:NSMakeRange(loc, self.length - loc))
if rangeOfNewline.location == NSNotFound {
break
}
let rangeWithDelimiter = NSMakeRange(loc, rangeOfNewline.length - (loc + delimiter.length))
let chunkWithDelimiter = self.subdataWithRange(rangeWithDelimiter)
block(data: chunkWithDelimiter, finalBlock: false)
loc = NSMaxRange(rangeWithDelimiter)
}
let remainder = self.subdataWithRange(NSMakeRange(loc, self.length - loc))
block(data: remainder, finalBlock: true)
}
}
//
// Created by Tommie N. Carter, Jr., MBA on 2/12/16.
// Copyright © 2016 MING Technology. All rights reserved.
//
import Foundation
class StreamReader: NSObject, NSStreamDelegate {
var callback: ((UInt , String) -> ())?
var completion: ((Int) -> Void)?
var fileURL:NSURL? = nil
var inputStream: NSInputStream? = nil
var lineNumber:UInt = 0
var queue:NSOperationQueue? = nil
var remainder:NSMutableData? = nil
var delimiter:NSData? = nil
func enumerateLinesWithBlock(block: (UInt, String)->() , completionHandler completion:(numberOfLines:Int) -> Void ) {
if self.queue == nil {
self.queue = NSOperationQueue()
self.queue!.maxConcurrentOperationCount = 1
}
assert(self.queue!.maxConcurrentOperationCount == 1, "Queue can't be concurrent.")
assert(self.inputStream == nil, "Cannot process multiple input streams in parallel")
self.callback = block
self.completion = completion
self.inputStream = NSInputStream(URL: self.fileURL!)
self.inputStream!.delegate = self
self.inputStream!.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode)
self.inputStream!.open()
}
convenience init? (withFileAtURL fileURL: NSURL) {
guard !fileURL.fileURL else { return nil }
self.init()
self.fileURL = fileURL
self.delimiter = "\n".dataUsingEncoding(NSUTF8StringEncoding)
}
@objc func stream(aStream: NSStream, handleEvent eventCode: NSStreamEvent){
switch eventCode {
case NSStreamEvent.OpenCompleted:
fallthrough
case NSStreamEvent.EndEncountered:
self.emitLineWithData(self.remainder!)
self.remainder = nil
self.inputStream!.close()
self.inputStream = nil
self.queue!.addOperationWithBlock({ () -> Void in
self.completion!(Int(self.lineNumber) + 1)
})
break
case NSStreamEvent.ErrorOccurred:
NSLog("error")
break
case NSStreamEvent.HasSpaceAvailable:
NSLog("HasSpaceAvailable")
break
case NSStreamEvent.HasBytesAvailable:
NSLog("HasBytesAvaible")
let buffer = NSMutableData(capacity: 4096)
let length = self.inputStream!.read(UnsafeMutablePointer<UInt8>(buffer!.mutableBytes), maxLength: buffer!.length)
if 0 < length {
buffer!.length = length
self.queue!.addOperationWithBlock({ [weak self] () -> Void in
self!.processDataChunk(buffer!)
})
}
break
default:
break
}
}
func processDataChunk(buffer: NSMutableData) {
if self.remainder != nil {
self.remainder!.appendData(buffer)
}
else {
self.remainder = buffer
}
self.remainder!.mng_enumerateComponentsSeparatedBy(self.delimiter!, block: {( component: NSData, last: Bool) in
if !last {
self.emitLineWithData(component)
}
else {
if 0 < component.length {
self.remainder = (component.mutableCopy() as! NSMutableData)
}
else {
self.remainder = nil
}
}
})
}
func emitLineWithData(data: NSData) {
let lineNumber = self.lineNumber
self.lineNumber = lineNumber + 1
if 0 < data.length {
if let line = NSString(data: data, encoding: NSUTF8StringEncoding) {
callback!(lineNumber, line as String)
}
}
}
}
@DivineDominion
Copy link

The blocks' signatures are missing somehow

@mingsai
Copy link
Author

mingsai commented Feb 12, 2016

I've updated this routine to be compliant with Swift 2.2 and added the NSData extension. This is my interpretation of the original objective-c source code for the article on advanced file i/o

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment