TCPTransports/LineFraming.swift
| /* | 
| Copyright (C) 2018 Apple Inc. All Rights Reserved. | 
| See LICENSE.txt for this sample’s licensing information | 
| Abstract: | 
| Frames and unframes lines. | 
| */ | 
| import Foundation | 
| /// Takes an array of lines and frames them in the standard way used by Internet | 
| /// protocols, that is, adding a CR LF at the end. | 
| /// | 
| /// This cannot throw because, hey, you shouldn’t have given it bogus strings | 
| /// in the first place. | 
| /// | 
| /// - note: This is a method on a `struct` to match `LineUnframer`, which has to | 
| /// maintain complex state. It’s also possible to imagine a different type | 
| /// of framer that needs to maintain state. | 
| struct LineFramer { | 
|     func data(for lines: [String]) -> Data { | 
|         return lines.map { "\($0)\r\n" }.joined().data(using: .utf8)! | 
| } | 
| } | 
| /// Takes data and parses it looking for lines. | 
| /// | 
| /// This has to maintain a buffer of data looking for line breaks. You | 
| /// initialise it with the maximum line length, measured in bytes of | 
| /// UTF-8 encoded data. | 
| /// | 
| /// Also, this will throw if it gets a malformed line, including: | 
| /// | 
| /// * Code sequences that aren’t valid UTF-8 | 
| /// * The line is too long. | 
| struct LineUnframer { | 
|     init(maxLineLength: Int) { | 
| precondition(maxLineLength > 0) | 
| self.maxLineLength = maxLineLength | 
| } | 
| let maxLineLength: Int | 
|     private enum State { | 
| case standard(Data) | 
| case expectingLF(Data) | 
| case error(Swift.Error) | 
| } | 
| private var state: State = .standard(Data()) | 
|     mutating func lines(for data: Data) throws -> [String] { | 
|         if case .error(let error) = self.state { | 
| throw error | 
| } | 
| var result: [String] = [] | 
|         func extendLine(_ lineData: Data, _ bytes: [UInt8]) throws { | 
|             if lineData.count + bytes.count > self.maxLineLength { | 
| throw Error(code: .lineTooLong, linesSoFar: result) | 
| } | 
| self.state = .standard(lineData + bytes) | 
| } | 
|         for c in data { | 
|             switch (self.state, c) { | 
| case (.expectingLF(let lineData), 10): | 
|                 guard let line = String(bytes: lineData, encoding: .utf8) else { | 
| throw Error(code: .malformedUTF8, linesSoFar: result) | 
| } | 
| result.append(line) | 
| self.state = .standard(Data()) | 
| case (.expectingLF(let lineData), let b): | 
| try extendLine(lineData, [13, b]) | 
| case (.standard(let lineData), 13): | 
| self.state = .expectingLF(lineData) | 
| case (.standard(let lineData), let b): | 
| try extendLine(lineData, [b]) | 
| case (.error, _): | 
| fatalError() | 
| } | 
| } | 
| return result | 
| } | 
|     struct Error : Swift.Error, Equatable { | 
|         enum Code { | 
| case lineTooLong | 
| case malformedUTF8 | 
| } | 
| var code: Code | 
| var linesSoFar: [String] | 
|         static func ==(lhs: Error, rhs: Error) -> Bool { | 
| return lhs.code == rhs.code && lhs.linesSoFar == rhs.linesSoFar | 
| } | 
| } | 
| } | 
Copyright © 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-05-10