Commit 629a5c87 authored by Ignacio Corderi's avatar Ignacio Corderi
Browse files

Refactored KineticEncoding

parent 8412edd1
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@

/* Begin PBXBuildFile section */
		3E07EA7F1B78E3B500DAB3F1 /* Kinetic.proto.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3E07EA7E1B78E3B500DAB3F1 /* Kinetic.proto.swift */; };
		3ED681EB1B7CEA0600AFDF79 /* Encoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ED681EA1B7CEA0600AFDF79 /* Encoding.swift */; };
		3EDAAB411B66D32D00F30808 /* Kinetic.h in Headers */ = {isa = PBXBuildFile; fileRef = 3EDAAB401B66D32D00F30808 /* Kinetic.h */; settings = {ATTRIBUTES = (Public, ); }; };
		3EDAAB481B66D32D00F30808 /* Kinetic.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3EDAAB3D1B66D32D00F30808 /* Kinetic.framework */; };
		3EDAAB4D1B66D32D00F30808 /* KineticTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EDAAB4C1B66D32D00F30808 /* KineticTests.swift */; };
@@ -38,6 +39,7 @@
/* Begin PBXFileReference section */
		382D1C00FC0EC4F1BF676411 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		3E07EA7E1B78E3B500DAB3F1 /* Kinetic.proto.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Kinetic.proto.swift; sourceTree = "<group>"; };
		3ED681EA1B7CEA0600AFDF79 /* Encoding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Encoding.swift; sourceTree = "<group>"; };
		3EDAAB3D1B66D32D00F30808 /* Kinetic.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kinetic.framework; sourceTree = BUILT_PRODUCTS_DIR; };
		3EDAAB401B66D32D00F30808 /* Kinetic.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Kinetic.h; sourceTree = "<group>"; };
		3EDAAB421B66D32D00F30808 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -117,6 +119,7 @@
				3EFB7F1F1B7A6C3300988886 /* Errors.swift */,
				3EFB7F1B1B7A55D100988886 /* Core.swift */,
				3EFB7F2A1B7B963800988886 /* Channel.swift */,
				3ED681EA1B7CEA0600AFDF79 /* Encoding.swift */,
				3EFB7F2C1B7BAF8D00988886 /* NetworkChannel.swift */,
				3EFB7F2E1B7BB4E700988886 /* Authentication.swift */,
				3EDAAB571B66D47200F30808 /* Session.swift */,
@@ -311,6 +314,7 @@
				3EFB7F2B1B7B963800988886 /* Channel.swift in Sources */,
				3EFB7F201B7A6C3300988886 /* Errors.swift in Sources */,
				3EFB7F1E1B7A6A9800988886 /* Put.swift in Sources */,
				3ED681EB1B7CEA0600AFDF79 /* Encoding.swift in Sources */,
				3EFB7F291B7AB06800988886 /* Delete.swift in Sources */,
				3EFB7F2D1B7BAF8D00988886 /* NetworkChannel.swift in Sources */,
				3EDAAB581B66D47200F30808 /* Session.swift in Sources */,

Kinetic/Encoding.swift

0 → 100644
+90 −0
Original line number Diff line number Diff line
// Copyright (c) 2015 Seagate Technology

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// @author: Ignacio Corderi

public struct KineticEncoding {
    
    public struct Header {
        public let bytes: Bytes
        
        public var isValid: Bool { return self.bytes[0] == 70 }
        
        public var protoLength: Int {
            return Int(bytesToUInt32(self.bytes, offset: 1))
        }
        
        public var valueLength: Int {
            return Int(bytesToUInt32(self.bytes, offset: 5))
        }
        
        public init(bytes: Bytes) {
            self.bytes = bytes
        }
        
        public init(protoLength: Int, valueLength: Int?) {
            var buffer = Bytes(count: 9, repeatedValue: 0)
            buffer[0] = 70 // Magic
            copyFromUInt32(&buffer, offset: 1, value: UInt32(protoLength))
            if valueLength != nil {
                copyFromUInt32(&buffer, offset: 5, value: UInt32(valueLength!))
            }
            self.bytes = buffer
        }
    }
    
    public let header: Header
    public private(set) var proto: Bytes?
    public private(set) var value: Bytes?
    
    public init(_ header: Header, _ proto: Bytes, _ value: Bytes?) {
        self.header = header
        self.proto = proto
        self.value = value
    }
    
    public static func encode(builder: Builder) throws -> KineticEncoding {
        let proto = try builder.message.build()
        let protoData = proto.data()
        let header = Header(protoLength: protoData.length, valueLength: builder.value?.count)
        
        return KineticEncoding(header, protoData.asBytes(), builder.value)
    }
    
    public func decode() throws -> RawResponse {
        if !self.header.isValid {
            throw KineticConnectionErrors.InvalidMagicNumber
        }
        
        // TODO: make nocopy
        let protoData = NSData(bytes: self.proto!, length: self.proto!.count)
        let msg = try Message.parseFromData(protoData)
        // TODO: verify HMAC
        let cmd = try Command.parseFromData(msg.commandBytes)
        
        return RawResponse(message: msg, command: cmd, value: self.value)
    }
}

public extension Builder {
    public func encode() throws -> KineticEncoding {
        return try KineticEncoding.encode(self)
    }
}
 No newline at end of file
+35 −65
Original line number Diff line number Diff line
@@ -22,71 +22,26 @@

public let connect = NetworkChannel.connect

protocol StreamChannel {
    var inp: NSInputStream? { get set }
    var out: NSOutputStream? { get set }
}

extension StreamChannel {
extension NSInputStream {
    
    func rawSend(proto: NSData, value: Bytes?) throws {
        // Prepare 9 bytes header
        // 1 byte - magic number | 4 bytes - proto length | 4 bytes - value length
        var headerBuffer = Bytes(count: 9, repeatedValue: 0)
        headerBuffer[0] = 70 // Magic
        copyFromUInt32(&headerBuffer, offset: 1, value: UInt32(proto.length))
        if value != nil {
            copyFromUInt32(&headerBuffer, offset: 5, value: UInt32(value!.count))
    func read(fully length: Int) -> Bytes {
        var buffer = Bytes(count:length, repeatedValue: 0)
        // TODO: loop until you read it all
        let _ = self.read(&buffer, maxLength: length)
        return buffer
    }
    
        // Send header, proto and value
        let outputStream = self.out!
        outputStream.write(headerBuffer, maxLength: headerBuffer.count)
        var array = Bytes(count: proto.length, repeatedValue: 0)
        // TODO: make sure this is a non-memcopy operation
        proto.getBytes(&array, length: proto.length)
        outputStream.write(array, maxLength: array.count)
        if value != nil {
            outputStream.write(value!, maxLength: value!.count)
}
    }
    
    func rawReceive() throws -> (Message, Bytes) {
        let inputStream = self.inp!

        var headerBuffer = Bytes(count:9, repeatedValue: 0)
extension NSOutputStream {
    
        // TODO: what are the semantics of read in swift? does it read all?
        let _ = inputStream.read(&headerBuffer, maxLength: headerBuffer.count)
        
        if headerBuffer[0] != 70 {
            throw KineticConnectionErrors.InvalidMagicNumber
    func write(bytes: Bytes) -> Int {
        return self.write(bytes, maxLength: bytes.count)
    }
    
        let protoLength = Int(bytesToUInt32(headerBuffer, offset: 1))
        let valueLength = Int(bytesToUInt32(headerBuffer, offset: 5))
        
        var protoBuffer = Array<UInt8>(count:protoLength, repeatedValue: 0)
        // TODO: what are the semantics of read in swift? does it read all?
        let _ = inputStream.read(&protoBuffer, maxLength: protoBuffer.count)
        
        let proto = NSData(bytes: &protoBuffer, length: protoLength)
        let msg = try Message.parseFromData(proto)
        // TODO: verify HMAC
        
        if valueLength > 0 {
            var value = Bytes(count:valueLength, repeatedValue: 0)
            // TODO: what are the semantics of read in swift? does it read all?
            let _ = inputStream.read(&value, maxLength: value.count)
            
            return (msg, value)
        } else {
            return (msg, [])
        }
    }
}

public class NetworkChannel: CustomStringConvertible, KineticChannel, StreamChannel {
public class NetworkChannel: CustomStringConvertible, KineticChannel {
    
    public let host: String
    public let port: Int
@@ -119,8 +74,8 @@ public class NetworkChannel: CustomStringConvertible, KineticChannel, StreamChan
        
        var device:KineticDevice? = nil
        do {
            let (msg, _) = try c.rawReceive()
            device = KineticDevice(handshake: try Command.parseFromData(msg.commandBytes))
            let r = try c.receive()
            device = KineticDevice(handshake: r.command)
        } catch let err {
            c.error = err
        }
@@ -143,15 +98,30 @@ public class NetworkChannel: CustomStringConvertible, KineticChannel, StreamChan
    }
    
    public func send(builder: Builder) throws {
        let msgProto = try builder.message.build()
        try self.rawSend(msgProto.data(), value: builder.value)
        let outputStream = self.out!
        
        let encoded = try builder.encode()
        
        outputStream.write(encoded.header.bytes)
        outputStream.write(encoded.proto!)
        if encoded.value != nil {
            outputStream.write(encoded.value!)
        }
    }
    
    public func receive() throws -> RawResponse {
        let (msg, value) = try self.rawReceive()
        let cmd = try Command.parseFromData(msg.commandBytes)
        let inputStream = self.inp!
        
        let header = KineticEncoding.Header(bytes: inputStream.read(fully: 9))
        let proto = inputStream.read(fully: header.protoLength)
        var value: Bytes? = nil
        if header.valueLength > 0 {
            value = inputStream.read(fully: header.valueLength)
        }
        
        let encoding = KineticEncoding(header, proto, value)
        
        return RawResponse(message: msg, command: cmd, value: value)
        return try encoding.decode()
    }
}

+7 −0
Original line number Diff line number Diff line
@@ -57,6 +57,13 @@ public extension String {

extension NSData {
    
    public func asBytes() -> 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
    }
    
    public func toUtf8() -> String {
        return NSString(data: self, encoding:NSUTF8StringEncoding)!.description
    }