Convert PacketSteram to Obj-C

For better TCP efficiency.
This commit is contained in:
Davide De Rosa 2019-04-25 11:15:25 +02:00
parent 3d914f72c4
commit 1b8647bcac
8 changed files with 279 additions and 142 deletions

View File

@ -17,7 +17,6 @@ custom_categories:
- IOInterface - IOInterface
- LinkInterface - LinkInterface
- TunnelInterface - TunnelInterface
- PacketStream
- BidirectionalState - BidirectionalState
- StaticKey - StaticKey
- SessionProxy - SessionProxy

View File

@ -67,6 +67,10 @@
0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; }; 0E3B65772249254000EFF4DA /* tunnelbear.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E3B65712249247E00EFF4DA /* tunnelbear.key */; };
0E3E0F212108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; }; 0E3E0F212108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; };
0E3E0F222108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; }; 0E3E0F222108A8CC00B371C1 /* SessionProxy+SessionReply.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */; };
0E48AC642271ADA9009B1A98 /* PacketStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E48AC622271ADA8009B1A98 /* PacketStream.h */; };
0E48AC652271ADA9009B1A98 /* PacketStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0E48AC622271ADA8009B1A98 /* PacketStream.h */; };
0E48AC662271ADA9009B1A98 /* PacketStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E48AC632271ADA9009B1A98 /* PacketStream.m */; };
0E48AC672271ADA9009B1A98 /* PacketStream.m in Sources */ = {isa = PBXBuildFile; fileRef = 0E48AC632271ADA9009B1A98 /* PacketStream.m */; };
0E500EA622493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; }; 0E500EA622493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; };
0E500EA722493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; }; 0E500EA722493B5B00CAE560 /* tunnelbear.enc.1.key in Resources */ = {isa = PBXBuildFile; fileRef = 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */; };
0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; }; 0E50D57521634E0A00FC87A8 /* ControlChannelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */; };
@ -307,6 +311,8 @@
0E3B656E224923EC00EFF4DA /* tunnelbear.enc.1.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.1.ovpn; sourceTree = "<group>"; }; 0E3B656E224923EC00EFF4DA /* tunnelbear.enc.1.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.enc.1.ovpn; sourceTree = "<group>"; };
0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = "<group>"; }; 0E3B65712249247E00EFF4DA /* tunnelbear.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = tunnelbear.key; sourceTree = "<group>"; };
0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+SessionReply.swift"; sourceTree = "<group>"; }; 0E3E0F202108A8CC00B371C1 /* SessionProxy+SessionReply.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SessionProxy+SessionReply.swift"; sourceTree = "<group>"; };
0E48AC622271ADA8009B1A98 /* PacketStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PacketStream.h; sourceTree = "<group>"; };
0E48AC632271ADA9009B1A98 /* PacketStream.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PacketStream.m; sourceTree = "<group>"; };
0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.1.key; sourceTree = "<group>"; }; 0E500EA522493B5B00CAE560 /* tunnelbear.enc.1.key */ = {isa = PBXFileReference; lastKnownFileType = text; path = tunnelbear.enc.1.key; sourceTree = "<group>"; };
0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = "<group>"; }; 0E50D57421634E0A00FC87A8 /* ControlChannelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControlChannelTests.swift; sourceTree = "<group>"; };
0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; }; 0E58BF2F22405410006FB157 /* lzoconf.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lzoconf.h; sourceTree = "<group>"; };
@ -653,6 +659,8 @@
0EFEB43E2006D3C800F81029 /* Packet.swift */, 0EFEB43E2006D3C800F81029 /* Packet.swift */,
0EE7A79420F61EDC00B42E6A /* PacketMacros.h */, 0EE7A79420F61EDC00B42E6A /* PacketMacros.h */,
0EE7A79720F6296F00B42E6A /* PacketMacros.m */, 0EE7A79720F6296F00B42E6A /* PacketMacros.m */,
0E48AC622271ADA8009B1A98 /* PacketStream.h */,
0E48AC632271ADA9009B1A98 /* PacketStream.m */,
0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */, 0EFEB4382006D3C800F81029 /* ProtocolMacros.swift */,
0EFEB4392006D3C800F81029 /* ReplayProtector.h */, 0EFEB4392006D3C800F81029 /* ReplayProtector.h */,
0EFEB4482006D3C800F81029 /* ReplayProtector.m */, 0EFEB4482006D3C800F81029 /* ReplayProtector.m */,
@ -736,6 +744,7 @@
0EF5CF262141E142004FF1BD /* PacketMacros.h in Headers */, 0EF5CF262141E142004FF1BD /* PacketMacros.h in Headers */,
0EFEB4642006D3C800F81029 /* ReplayProtector.h in Headers */, 0EFEB4642006D3C800F81029 /* ReplayProtector.h in Headers */,
0EFEB4612006D3C800F81029 /* Errors.h in Headers */, 0EFEB4612006D3C800F81029 /* Errors.h in Headers */,
0E48AC642271ADA9009B1A98 /* PacketStream.h in Headers */,
0E58BF3722405410006FB157 /* minilzo.h in Headers */, 0E58BF3722405410006FB157 /* minilzo.h in Headers */,
0E07596E20EF79B400F38FD8 /* CryptoCBC.h in Headers */, 0E07596E20EF79B400F38FD8 /* CryptoCBC.h in Headers */,
0E58BF3522405410006FB157 /* lzodefs.h in Headers */, 0E58BF3522405410006FB157 /* lzodefs.h in Headers */,
@ -764,6 +773,7 @@
0EF5CF272141E15B004FF1BD /* PacketMacros.h in Headers */, 0EF5CF272141E15B004FF1BD /* PacketMacros.h in Headers */,
0EEC49E620B5F7F6008FEB91 /* MSS.h in Headers */, 0EEC49E620B5F7F6008FEB91 /* MSS.h in Headers */,
0EEC49E520B5F7F6008FEB91 /* Errors.h in Headers */, 0EEC49E520B5F7F6008FEB91 /* Errors.h in Headers */,
0E48AC652271ADA9009B1A98 /* PacketStream.h in Headers */,
0E58BF3822405410006FB157 /* minilzo.h in Headers */, 0E58BF3822405410006FB157 /* minilzo.h in Headers */,
0E07596F20EF79B400F38FD8 /* CryptoCBC.h in Headers */, 0E07596F20EF79B400F38FD8 /* CryptoCBC.h in Headers */,
0E58BF3622405410006FB157 /* lzodefs.h in Headers */, 0E58BF3622405410006FB157 /* lzodefs.h in Headers */,
@ -1219,6 +1229,7 @@
0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */, 0EFEB4662006D3C800F81029 /* ZeroingData.swift in Sources */,
0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */, 0EBBF2F3208505D300E36B40 /* NEUDPInterface.swift in Sources */,
0EFEB4682006D3C800F81029 /* MSS.m in Sources */, 0EFEB4682006D3C800F81029 /* MSS.m in Sources */,
0E48AC662271ADA9009B1A98 /* PacketStream.m in Sources */,
0EFEB45B2006D3C800F81029 /* TLSBox.m in Sources */, 0EFEB45B2006D3C800F81029 /* TLSBox.m in Sources */,
0EFEB4792006D3C800F81029 /* TunnelKitProvider+Interaction.swift in Sources */, 0EFEB4792006D3C800F81029 /* TunnelKitProvider+Interaction.swift in Sources */,
0E58BF3922405410006FB157 /* minilzo.c in Sources */, 0E58BF3922405410006FB157 /* minilzo.c in Sources */,
@ -1287,6 +1298,7 @@
0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */, 0E07598120F0060E00F38FD8 /* CryptoAEAD.m in Sources */,
0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */, 0EFEB49C2006D7F300F81029 /* Data+Manipulation.swift in Sources */,
0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */, 0EBBF2F4208505D400E36B40 /* NEUDPInterface.swift in Sources */,
0E48AC672271ADA9009B1A98 /* PacketStream.m in Sources */,
0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */, 0EFEB4902006D7F300F81029 /* TunnelInterface.swift in Sources */,
0EFEB49E2006D7F300F81029 /* Allocation.m in Sources */, 0EFEB49E2006D7F300F81029 /* Allocation.m in Sources */,
0E58BF3A22405410006FB157 /* minilzo.c in Sources */, 0E58BF3A22405410006FB157 /* minilzo.c in Sources */,

View File

@ -38,6 +38,7 @@
import Foundation import Foundation
import NetworkExtension import NetworkExtension
import SwiftyBeaver import SwiftyBeaver
import __TunnelKitNative
private let log = SwiftyBeaver.self private let log = SwiftyBeaver.self
@ -223,7 +224,8 @@ class NETCPLink: LinkInterface {
var newBuffer = buffer var newBuffer = buffer
newBuffer.append(contentsOf: data) newBuffer.append(contentsOf: data)
let (until, packets) = PacketStream.packets(from: newBuffer) var until = 0
let packets = PacketStream.packets(fromStream: newBuffer, until: &until)
newBuffer = newBuffer.subdata(in: until..<newBuffer.count) newBuffer = newBuffer.subdata(in: until..<newBuffer.count)
self?.loopReadPackets(queue, newBuffer, handler) self?.loopReadPackets(queue, newBuffer, handler)
@ -233,14 +235,14 @@ class NETCPLink: LinkInterface {
} }
func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) { func writePacket(_ packet: Data, completionHandler: ((Error?) -> Void)?) {
let stream = PacketStream.stream(from: packet) let stream = PacketStream.stream(fromPacket: packet)
impl.write(stream) { (error) in impl.write(stream) { (error) in
completionHandler?(error) completionHandler?(error)
} }
} }
func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) { func writePackets(_ packets: [Data], completionHandler: ((Error?) -> Void)?) {
let stream = PacketStream.stream(from: packets) let stream = PacketStream.stream(fromPackets: packets)
impl.write(stream) { (error) in impl.write(stream) { (error) in
completionHandler?(error) completionHandler?(error)
} }

View File

@ -40,65 +40,65 @@ import __TunnelKitNative
// TODO: convert to C for efficiency // TODO: convert to C for efficiency
/// Reads and writes packets as a stream. Useful for stream-oriented links (e.g TCP/IP). ///// Reads and writes packets as a stream. Useful for stream-oriented links (e.g TCP/IP).
public class PacketStream { //public class PacketStream {
//
/** // /**
Parses packets from a stream. // Parses packets from a stream.
//
- Parameter stream: The data stream. // - Parameter stream: The data stream.
- Returns: A pair where the first value is the `Int` offset up to which // - Returns: A pair where the first value is the `Int` offset up to which
the stream could be parsed, and the second value is an array containing // the stream could be parsed, and the second value is an array containing
the parsed packets up to such offset. // the parsed packets up to such offset.
*/ // */
public static func packets(from stream: Data) -> (Int, [Data]) { // public static func packets(from stream: Data) -> (Int, [Data]) {
var ni = 0 // var ni = 0
var parsed: [Data] = [] // var parsed: [Data] = []
while (ni + 2 <= stream.count) { // while (ni + 2 <= stream.count) {
let packlen = Int(stream.networkUInt16Value(from: ni)) // let packlen = Int(stream.networkUInt16Value(from: ni))
let start = ni + 2 // let start = ni + 2
let end = start + packlen // let end = start + packlen
guard (end <= stream.count) else { // guard (end <= stream.count) else {
break // break
} // }
let packet = stream.subdata(offset: start, count: end - start) // let packet = stream.subdata(offset: start, count: end - start)
parsed.append(packet) // parsed.append(packet)
ni = end // ni = end
} // }
return (ni, parsed) // return (ni, parsed)
} // }
//
/** // /**
Creates a contiguous stream of packets. // Creates a contiguous stream of packets.
//
- Parameter packet: The packet. // - Parameter packet: The packet.
- Returns: A stream made of the packet. // - Returns: A stream made of the packet.
*/ // */
public static func stream(from packet: Data) -> Data { // public static func stream(from packet: Data) -> Data {
var raw = Data(capacity: 2 + packet.count) // var raw = Data(capacity: 2 + packet.count)
raw.append(UInt16(packet.count).bigEndian) // raw.append(UInt16(packet.count).bigEndian)
raw.append(contentsOf: packet) // raw.append(contentsOf: packet)
return raw // return raw
} // }
//
/** // /**
Creates a contiguous stream of packets. // Creates a contiguous stream of packets.
//
- Parameter packets: The array of packets. // - Parameter packets: The array of packets.
- Returns: A stream made of the array of packets. // - Returns: A stream made of the array of packets.
*/ // */
public static func stream(from packets: [Data]) -> Data { // public static func stream(from packets: [Data]) -> Data {
var raw = Data() // var raw = Data()
for payload in packets { // for payload in packets {
raw.append(UInt16(payload.count).bigEndian) // raw.append(UInt16(payload.count).bigEndian)
raw.append(payload) // raw.append(payload)
} // }
return raw // return raw
} // }
//
private init() { // private init() {
} // }
} //}
/// :nodoc: /// :nodoc:
extension ControlPacket { extension ControlPacket {

View File

@ -0,0 +1,38 @@
//
// PacketStream.h
// TunnelKit
//
// Created by Davide De Rosa on 4/25/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface PacketStream : NSObject
+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(nullable NSInteger *)until;
+ (NSData *)streamFromPacket:(NSData *)packet;
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets;
@end
NS_ASSUME_NONNULL_END

View File

@ -0,0 +1,84 @@
//
// PacketStream.m
// TunnelKit
//
// Created by Davide De Rosa on 4/25/19.
// Copyright (c) 2019 Davide De Rosa. All rights reserved.
//
// https://github.com/keeshux
//
// This file is part of TunnelKit.
//
// TunnelKit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// TunnelKit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with TunnelKit. If not, see <http://www.gnu.org/licenses/>.
//
#import "PacketStream.h"
static const NSInteger PacketStreamHeaderLength = sizeof(uint16_t);
@implementation PacketStream
+ (NSArray<NSData *> *)packetsFromStream:(NSData *)stream until:(NSInteger *)until
{
NSInteger ni = 0;
NSMutableArray<NSData *> *parsed = [[NSMutableArray alloc] init];
while (ni + PacketStreamHeaderLength <= stream.length) {
const NSInteger packlen = CFSwapInt16BigToHost(*(uint16_t *)(stream.bytes + ni));
const NSInteger start = ni + PacketStreamHeaderLength;
const NSInteger end = start + packlen;
if (end > stream.length) {
break;
}
NSData *packet = [stream subdataWithRange:NSMakeRange(start, packlen)];
[parsed addObject:packet];
ni = end;
}
if (until) {
*until = ni;
}
return parsed;
}
+ (NSData *)streamFromPacket:(NSData *)packet
{
NSMutableData *raw = [[NSMutableData alloc] initWithLength:(PacketStreamHeaderLength + packet.length)];
uint8_t *ptr = raw.mutableBytes;
*(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length);
ptr += PacketStreamHeaderLength;
memcpy(ptr, packet.bytes, packet.length);
return raw;
}
+ (NSData *)streamFromPackets:(NSArray<NSData *> *)packets;
{
NSInteger streamLength = 0;
for (NSData *p in packets) {
streamLength += PacketStreamHeaderLength + p.length;
}
NSMutableData *raw = [[NSMutableData alloc] initWithLength:streamLength];
uint8_t *ptr = raw.mutableBytes;
for (NSData *packet in packets) {
*(uint16_t *)ptr = CFSwapInt16HostToBig(packet.length);
ptr += PacketStreamHeaderLength;
memcpy(ptr, packet.bytes, packet.length);
ptr += packet.length;
}
return raw;
}
@end

View File

@ -42,6 +42,7 @@ module __TunnelKitNative {
header "CryptoBox.h" header "CryptoBox.h"
header "MSS.h" header "MSS.h"
header "PacketMacros.h" header "PacketMacros.h"
header "PacketStream.h"
header "ControlPacket.h" header "ControlPacket.h"
header "ReplayProtector.h" header "ReplayProtector.h"
header "CompressionFramingNative.h" header "CompressionFramingNative.h"

View File

@ -37,6 +37,7 @@
import XCTest import XCTest
@testable import TunnelKit @testable import TunnelKit
@testable import __TunnelKitNative
class LinkTests: XCTestCase { class LinkTests: XCTestCase {
@ -62,85 +63,85 @@ class LinkTests: XCTestCase {
// TCP // TCP
func testPacketStream() { // private func testPacketStream() {
var bytes: [UInt8] = [] // var bytes: [UInt8] = []
var until: Int // var until: Int
var packets: [Data] // var packets: [Data]
//
bytes.append(contentsOf: [0x00, 0x04]) // bytes.append(contentsOf: [0x00, 0x04])
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40]) // bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
bytes.append(contentsOf: [0x00, 0x07]) // bytes.append(contentsOf: [0x00, 0x07])
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40, 0x50, 0x66, 0x77]) // bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40, 0x50, 0x66, 0x77])
bytes.append(contentsOf: [0x00, 0x01]) // bytes.append(contentsOf: [0x00, 0x01])
bytes.append(contentsOf: [0xff]) // bytes.append(contentsOf: [0xff])
bytes.append(contentsOf: [0x00, 0x03]) // bytes.append(contentsOf: [0x00, 0x03])
bytes.append(contentsOf: [0xaa]) // bytes.append(contentsOf: [0xaa])
XCTAssertEqual(bytes.count, 21) // XCTAssertEqual(bytes.count, 21)
//
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 18) // XCTAssertEqual(until, 18)
XCTAssertEqual(packets.count, 3) // XCTAssertEqual(packets.count, 3)
//
bytes.append(contentsOf: [0xbb, 0xcc]) // bytes.append(contentsOf: [0xbb, 0xcc])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 23) // XCTAssertEqual(until, 23)
XCTAssertEqual(packets.count, 4) // XCTAssertEqual(packets.count, 4)
//
bytes.append(contentsOf: [0x00, 0x05]) // bytes.append(contentsOf: [0x00, 0x05])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 23) // XCTAssertEqual(until, 23)
XCTAssertEqual(packets.count, 4) // XCTAssertEqual(packets.count, 4)
//
bytes.append(contentsOf: [0x11, 0x22, 0x33, 0x44]) // bytes.append(contentsOf: [0x11, 0x22, 0x33, 0x44])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 23) // XCTAssertEqual(until, 23)
XCTAssertEqual(packets.count, 4) // XCTAssertEqual(packets.count, 4)
//
bytes.append(contentsOf: [0x55]) // bytes.append(contentsOf: [0x55])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 30) // XCTAssertEqual(until, 30)
XCTAssertEqual(packets.count, 5) // XCTAssertEqual(packets.count, 5)
//
// // //
//
bytes.removeSubrange(0..<until) // bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 0) // XCTAssertEqual(bytes.count, 0)
//
bytes.append(contentsOf: [0x00, 0x04]) // bytes.append(contentsOf: [0x00, 0x04])
bytes.append(contentsOf: [0x10, 0x20]) // bytes.append(contentsOf: [0x10, 0x20])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 0) // XCTAssertEqual(until, 0)
XCTAssertEqual(packets.count, 0) // XCTAssertEqual(packets.count, 0)
bytes.removeSubrange(0..<until) // bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 4) // XCTAssertEqual(bytes.count, 4)
//
bytes.append(contentsOf: [0x30, 0x40]) // bytes.append(contentsOf: [0x30, 0x40])
bytes.append(contentsOf: [0x00, 0x07]) // bytes.append(contentsOf: [0x00, 0x07])
bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40]) // bytes.append(contentsOf: [0x10, 0x20, 0x30, 0x40])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 6) // XCTAssertEqual(until, 6)
XCTAssertEqual(packets.count, 1) // XCTAssertEqual(packets.count, 1)
bytes.removeSubrange(0..<until) // bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 6) // XCTAssertEqual(bytes.count, 6)
//
bytes.append(contentsOf: [0x50, 0x66, 0x77]) // bytes.append(contentsOf: [0x50, 0x66, 0x77])
bytes.append(contentsOf: [0x00, 0x01]) // bytes.append(contentsOf: [0x00, 0x01])
bytes.append(contentsOf: [0xff]) // bytes.append(contentsOf: [0xff])
bytes.append(contentsOf: [0x00, 0x03]) // bytes.append(contentsOf: [0x00, 0x03])
bytes.append(contentsOf: [0xaa]) // bytes.append(contentsOf: [0xaa])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 12) // XCTAssertEqual(until, 12)
XCTAssertEqual(packets.count, 2) // XCTAssertEqual(packets.count, 2)
bytes.removeSubrange(0..<until) // bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 3) // XCTAssertEqual(bytes.count, 3)
//
bytes.append(contentsOf: [0xbb, 0xcc]) // bytes.append(contentsOf: [0xbb, 0xcc])
(until, packets) = PacketStream.packets(from: Data(bytes)) // (until, packets) = PacketStream.packets(from: Data(bytes))
XCTAssertEqual(until, 5) // XCTAssertEqual(until, 5)
XCTAssertEqual(packets.count, 1) // XCTAssertEqual(packets.count, 1)
bytes.removeSubrange(0..<until) // bytes.removeSubrange(0..<until)
XCTAssertEqual(bytes.count, 0) // XCTAssertEqual(bytes.count, 0)
} // }
// helpers // helpers