Commit c8b0aad8 authored by Ignacio Corderi's avatar Ignacio Corderi
Browse files

Added KineticSession.traverse()

parent a9d2ab03
Loading
Loading
Loading
Loading
+116 −8
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ public enum Range<K : KeyType> : CustomStringConvertible{
    case From(K, Bool)
    case To(K, Bool)
    case Prefix(K)
    case Empty

    internal func build(builder: Builder, device: KineticDevice) {
        switch self {
@@ -54,6 +55,11 @@ public enum Range<K : KeyType> : CustomStringConvertible{
                builder.range.endKey = NSData(bytes: bs, length: p.length)
                builder.range.endKeyInclusive = false
            }
        case Empty:
            builder.range.startKey = "".toData()
            builder.range.startKeyInclusive = false
            builder.range.endKey = "".toData()
            builder.range.endKeyInclusive = false
        }
    }
    
@@ -72,11 +78,11 @@ public enum Range<K : KeyType> : CustomStringConvertible{
        case To(let key, true):  return "Range [..'\(key)']"
        case To(let key, false): return "Range [..'\(key)')"
        case Prefix(let prefix): return "Prefix '\(prefix)'"
        case Empty: return "Empty"
        }
    }
}


public class GetKeyRangeCommand<K : KeyType> : ChannelCommand {
    
    public typealias ResponseType = KeyRangeResponse
@@ -91,7 +97,7 @@ public class GetKeyRangeCommand<K : KeyType> : ChannelCommand {
        self.maxReturned = maxReturned
    }
    
    public func build(builder: Builder, device: KineticDevice) -> Int {
    public func build(builder: Builder, device: KineticDevice) -> KeyRangeContext {
        builder.header.messageType = .Getkeyrange
        self.range.build(builder, device: device)
        builder.range.reverse = self.reverse
@@ -105,20 +111,47 @@ public class GetKeyRangeCommand<K : KeyType> : ChannelCommand {
        }
         builder.range.maxReturned = max
        
        return Int(max)
        return KeyRangeContext(maxReturned: Int(max),
                               reverse: self.reverse,
                               endKey: builder.range.endKey,
                               endInclusive: builder.range.endKeyInclusive)
    }
    
}

public struct KeyRangeContext {
    internal let maxReturned: Int
    internal let reverse: Bool
    internal let endKey: NSData
    internal let endInclusive: Bool
}

public struct KeyRangeResponse: ChannelResponse {
    public typealias ContextType = Int
    public typealias ContextType = KeyRangeContext
    
    public let success: Bool
    public let error: KineticRemoteError?
    public let keys: [NSData]
    public let hasMore: Bool
    private let context: KeyRangeContext
    
    public var next: GetKeyRangeCommand<NSData> {
        guard self.hasMore else {
            return GetKeyRangeCommand(.Empty) }
        guard self.keys.count > 0 else {
            return GetKeyRangeCommand(.Empty) }
        // Even if there might be more, we know there isnt because we already got the last key
        guard self.keys[self.keys.count - 1] != self.context.endKey else {
            return GetKeyRangeCommand(.Empty) }

    public static func parse(raw: RawResponse, context: Int) -> KeyRangeResponse {
        return GetKeyRangeCommand(.FromTo(self.keys[self.keys.count-1],
                                              self.context.endKey,
                                              false, self.context.endInclusive),
                                      reverse: self.context.reverse,
                                      maxReturned: self.context.maxReturned)
    }
    
    public static func parse(raw: RawResponse, context: KeyRangeContext) -> KeyRangeResponse {
        switch raw.command.status.code {
        case .Success:
            var keys: [NSData] = []
@@ -130,11 +163,79 @@ public struct KeyRangeResponse: ChannelResponse {
            }
            return KeyRangeResponse(success: true, error: nil,
                                    keys: keys,
                                    hasMore: keys.count == context)
                                    hasMore: keys.count == context.maxReturned,
                                    context: context )
        default:
            return KeyRangeResponse(success: false,
                error: KineticRemoteError.fromStatus(raw.command.status),
                keys: [], hasMore: false)
                keys: [], hasMore: false, context: context)
        }
    }
}

public class KeySequence<K: KeyType>: SequenceType {
    public typealias Generator = KeyGenerator<K>
    
    private let session: KineticSession
    private let range: Range<K>
    private let reverse: Bool
    private let batch: Int?
    
    internal init(session: KineticSession, range: Range<K>, reverse:Bool = false, batch: Int? = nil) {
        self.session = session
        self.range = range
        self.reverse = reverse
        self.batch = batch
    }
    
    public func generate() -> KeyGenerator<K> {
        return KeyGenerator<K>(session: session, range: self.range, reverse: self.reverse, batch: self.batch)
    }
}

public class KeyGenerator<K: KeyType>: GeneratorType {
    public typealias Element = NSData
    
    // Params
    private let session: KineticSession
    private let range: Range<K>
    private let reverse: Bool
    private let batch: Int?
    
    // state
    private var lastResponse: KeyRangeResponse?
    private var index: Int
    
    internal init(session: KineticSession, range: Range<K>, reverse:Bool = false, batch: Int? = nil) {
        self.session = session
        self.range = range
        self.reverse = reverse
        self.batch = batch
        self.index = 0
        
        let first = GetKeyRangeCommand(range, reverse: reverse, maxReturned: batch)
        do {
            self.lastResponse = try first.sendTo(self.session)
        } catch { /* TODO: log */ }
    }
    
    public func next() -> NSData? {
        if self.lastResponse == nil {
            return nil
        } else if self.index < self.lastResponse!.keys.count {
            return self.lastResponse!.keys[self.index++]
        } else if self.lastResponse!.hasMore {
            let cmd =  self.lastResponse!.next
            do {
                self.lastResponse = try cmd.sendTo(self.session)
            } catch {
                // TODO: log
                self.lastResponse = nil
            }
            self.index = 0
            return self.next()
        } else {
            return nil
        }
    }
}
@@ -158,12 +259,19 @@ extension GetKeyRangeCommand: CustomStringConvertible {
}

public extension KineticSession {
    
    func getKeyRange<K>(range: Range<K>, reverse: Bool = false) throws -> GetKeyRangeCommand<K>.ResponseType {
        let cmd = GetKeyRangeCommand(range, reverse: reverse, maxReturned: nil)
        return try cmd.sendTo(self)
    }
    
    func getKeyRange<K>(range: Range<K>, reverse: Bool, maxReturned: Int) throws -> GetKeyRangeCommand<K>.ResponseType {
        let cmd = GetKeyRangeCommand(range, reverse: reverse, maxReturned: maxReturned)
        return try cmd.sendTo(self)
    }
    
    func traverse<K>(range: Range<K>, reverse: Bool = false, batch: Int) throws -> KeySequence<K> {
        return KeySequence(session: self, range: range, reverse: reverse, batch: batch)
    }
    
}
 No newline at end of file
+7 −0
Original line number Diff line number Diff line
@@ -32,21 +32,28 @@ public protocol ValueType {
}

extension NSData: KeyType, ValueType {
    
    public func toData() -> NSData { return self }
    
    public func toBytes() -> Bytes {
        // TODO: figure out how to do this without copying!
        var buffer = Bytes(count: self.length, repeatedValue: 0)
        self.getBytes(&buffer, length: self.length)
        return buffer
    }
    
}

extension String: KeyType, ValueType {
    
    public var length: Int { return self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding) }
    
    public func toData() -> NSData {
        return NSData(data: self.dataUsingEncoding(NSUTF8StringEncoding)!)
    }
    
    public func toBytes() -> Bytes { return self.toUtf8() }
    
}

public class Builder {
+5 −0
Original line number Diff line number Diff line
@@ -53,4 +53,9 @@ try c.getKeyRange(.Prefix("demo"))
try c.getKeyRange(.Prefix("demo"), reverse: true)
try c.getKeyRange(.Prefix("demo"), reverse: true, maxReturned: 42)

//: If we wish to iterate through a range of keys that might span the entire 
//: contents of the device, we can use `KineticSession.traverse()`
let ks = try c.traverse(.From("", true), reverse: false, batch: 64)
for k in ks { print(k.toUtf8()) }

c.close()
+1 −1
Original line number Diff line number Diff line
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='osx' requires-full-environment='true' display-mode='rendered'>
<playground version='5.0' target-platform='osx' requires-full-environment='true' display-mode='raw'>
    <timeline fileName='timeline.xctimeline'/>
</playground>
 No newline at end of file