Skip to content

Commit bc23b94

Browse files
committed
Implementing (initial) of Stream and InputStream.
1 parent fe685b4 commit bc23b94

File tree

4 files changed

+194
-10
lines changed

4 files changed

+194
-10
lines changed

CoreFoundation/Stream.subproj/CFStream.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ const CFStringRef kCFStreamPropertyDataWritten;
7878
CF_EXPORT
7979
CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator);
8080

81+
CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data);
82+
8183
/* The stream writes into the buffer given; when bufferCapacity is exhausted, the stream is exhausted (status becomes kCFStreamStatusAtEnd) */
8284
CF_EXPORT
8385
CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity);

Foundation/NSStream.swift

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
#if os(Linux)
11+
import CoreFoundation
12+
#endif
13+
1014
extension Stream {
1115
public struct PropertyKey : RawRepresentable, Equatable, Hashable, Comparable {
1216
public private(set) var rawValue: String
@@ -60,17 +64,17 @@ public func <(lhs: Stream.PropertyKey, rhs: Stream.PropertyKey) -> Bool {
6064
// NSStream is an abstract class encapsulating the common API to NSInputStream and NSOutputStream.
6165
// Subclassers of NSInputStream and NSOutputStream must also implement these methods.
6266
public class Stream: NSObject {
63-
67+
6468
public override init() {
65-
69+
6670
}
6771

6872
public func open() {
69-
NSUnimplemented()
73+
streamOpen()
7074
}
7175

7276
public func close() {
73-
NSUnimplemented()
77+
streamClose()
7478
}
7579

7680
public weak var delegate: StreamDelegate?
@@ -95,20 +99,37 @@ public class Stream: NSObject {
9599
}
96100

97101
public var streamStatus: Status {
98-
NSUnimplemented()
102+
return getStreamStatus()
99103
}
100104

101105
/*@NSCopying */public var streamError: NSError? {
102106
NSUnimplemented()
103107
}
108+
109+
internal func streamOpen() {
110+
preconditionFailure("This method must be overriden.")
111+
}
112+
113+
internal func streamClose() {
114+
preconditionFailure("This method must be overriden.")
115+
}
116+
117+
internal func getStreamStatus() -> Status {
118+
// Initial status of steam is 'notOpen'.
119+
// Subsequent status is set in the subclass function.
120+
return Status.notOpen
121+
}
104122
}
105123

106124
// NSInputStream is an abstract class representing the base functionality of a read stream.
107125
// Subclassers are required to implement these methods.
108126
public class InputStream: Stream {
127+
128+
private var _stream: CFReadStream!
129+
109130
// reads up to length bytes into the supplied buffer, which must be at least of size len. Returns the actual number of bytes read.
110131
public func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) -> Int {
111-
NSUnimplemented()
132+
return CFReadStreamRead(_stream, buffer, CFIndex(len._bridgeToObject()))
112133
}
113134

114135
// returns in O(1) a pointer to the buffer in 'buffer' and by reference in 'len' how many bytes are available. This buffer is only valid until the next stream operation. Subclassers may return NO for this if it is not appropriate for the stream type. This may return NO if the buffer is not available.
@@ -118,19 +139,34 @@ public class InputStream: Stream {
118139

119140
// returns YES if the stream has bytes available or if it impossible to tell without actually doing the read.
120141
public var hasBytesAvailable: Bool {
121-
NSUnimplemented()
142+
return CFReadStreamHasBytesAvailable(_stream)
122143
}
123144

124145
public init(data: Data) {
125-
NSUnimplemented()
146+
let nsData = data._bridgeToObjectiveC()
147+
let _data: CFData = CFDataCreate(kCFAllocatorDefault,
148+
UnsafePointer<UInt8>(nsData.bytes), nsData.length)
149+
_stream = CFReadStreamCreateWithData(kCFAllocatorSystemDefault, _data)
126150
}
127151

128152
public init?(url: URL) {
129-
NSUnimplemented()
153+
_stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url._cfObject)
130154
}
131155

132156
public convenience init?(fileAtPath path: String) {
133-
NSUnimplemented()
157+
self.init(url: URL(fileURLWithPath: path))
158+
}
159+
160+
internal override func streamOpen() {
161+
CFReadStreamOpen(_stream)
162+
}
163+
164+
internal override func streamClose() {
165+
CFReadStreamClose(_stream)
166+
}
167+
168+
internal override func getStreamStatus() -> Stream.Status {
169+
return Stream.Status(rawValue: UInt(CFReadStreamGetStatus(_stream)))!
134170
}
135171
}
136172

TestFoundation/TestNSStream.swift

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
//
9+
10+
#if DEPLOYMENT_RUNTIME_OBJC || os(Linux)
11+
import Foundation
12+
import XCTest
13+
#else
14+
import SwiftFoundation
15+
import SwiftXCTest
16+
#endif
17+
18+
class TestNSStream : XCTestCase {
19+
static var allTests: [(String, (TestNSStream) -> () throws -> Void)] {
20+
return [
21+
("test_InputStreamWithData", test_InputStreamWithData),
22+
("test_InputStreamWithUrl", test_InputStreamWithUrl),
23+
("test_InputStreamWithFile", test_InputStreamWithFile),
24+
("test_InputStreamHasBytesAvailable", test_InputStreamHasBytesAvailable),
25+
("test_InputStreamInvalidPath", test_InputStreamInvalidPath),
26+
]
27+
}
28+
29+
func test_InputStreamWithData(){
30+
let message: NSString = "Hello, playground"
31+
let messageData: Data = message.data(using: String.Encoding.utf8.rawValue)!
32+
let dataStream: InputStream = InputStream(data: messageData)
33+
XCTAssertEqual(Stream.Status.notOpen, dataStream.streamStatus)
34+
dataStream.open()
35+
XCTAssertEqual(Stream.Status.open, dataStream.streamStatus)
36+
var buffer = [UInt8](repeating: 0, count: 20)
37+
if dataStream.hasBytesAvailable {
38+
let result: Int = dataStream.read(&buffer, maxLength: buffer.count)
39+
dataStream.close()
40+
XCTAssertEqual(Stream.Status.closed, dataStream.streamStatus)
41+
if(result > 0){
42+
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
43+
XCTAssertEqual(message, output!)
44+
}
45+
}
46+
}
47+
48+
func test_InputStreamWithUrl() {
49+
let message: NSString = "Hello, playground"
50+
let messageData: Data = message.data(using: String.Encoding.utf8.rawValue)!
51+
//Initialiser with url
52+
let testFile = createTestFile("testFile_in.txt", _contents: messageData)
53+
if testFile != nil {
54+
let url = URL(fileURLWithPath: testFile!)
55+
let urlStream: InputStream = InputStream(url: url)!
56+
XCTAssertEqual(Stream.Status.notOpen, urlStream.streamStatus)
57+
urlStream.open()
58+
XCTAssertEqual(Stream.Status.open, urlStream.streamStatus)
59+
var buffer = [UInt8](repeating: 0, count: 20)
60+
if urlStream.hasBytesAvailable {
61+
let result :Int = urlStream.read(&buffer, maxLength: buffer.count)
62+
urlStream.close()
63+
XCTAssertEqual(Stream.Status.closed, urlStream.streamStatus)
64+
XCTAssertEqual(messageData.count, result)
65+
if(result > 0) {
66+
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
67+
XCTAssertEqual(message, output!)
68+
}
69+
}
70+
removeTestFile(testFile!)
71+
} else {
72+
XCTFail("Unable to create temp file")
73+
}
74+
}
75+
76+
func test_InputStreamWithFile() {
77+
let message: NSString = "Hello, playground"
78+
let messageData: Data = message.data(using: String.Encoding.utf8.rawValue)!
79+
//Initialiser with file
80+
let testFile = createTestFile("testFile_in.txt", _contents: messageData)
81+
if testFile != nil {
82+
let fileStream: InputStream = InputStream(fileAtPath: testFile!)!
83+
XCTAssertEqual(Stream.Status.notOpen, fileStream.streamStatus)
84+
fileStream.open()
85+
XCTAssertEqual(Stream.Status.open, fileStream.streamStatus)
86+
var buffer = [UInt8](repeating: 0, count: 20)
87+
if fileStream.hasBytesAvailable {
88+
let result: Int = fileStream.read(&buffer, maxLength: buffer.count)
89+
fileStream.close()
90+
XCTAssertEqual(Stream.Status.closed, fileStream.streamStatus)
91+
XCTAssertEqual(messageData.count, result)
92+
if(result > 0){
93+
let output = NSString(bytes: &buffer, length: buffer.count, encoding: String.Encoding.utf8.rawValue)
94+
XCTAssertEqual(message, output!)
95+
}
96+
}
97+
removeTestFile(testFile!)
98+
} else {
99+
XCTFail("Unable to create temp file")
100+
}
101+
}
102+
103+
func test_InputStreamHasBytesAvailable() {
104+
let message: NSString = "Hello, playground"
105+
let messageData: Data = message.data(using: String.Encoding.utf8.rawValue)!
106+
let stream: InputStream = InputStream(data: messageData)
107+
var buffer = [UInt8](repeating: 0, count: 20)
108+
stream.open()
109+
XCTAssertTrue(stream.hasBytesAvailable)
110+
_ = stream.read(&buffer, maxLength: buffer.count)
111+
XCTAssertFalse(stream.hasBytesAvailable)
112+
}
113+
114+
func test_InputStreamInvalidPath() {
115+
let fileStream: InputStream = InputStream(fileAtPath: "/tmp/file.txt")!
116+
XCTAssertEqual(Stream.Status.notOpen, fileStream.streamStatus)
117+
fileStream.open()
118+
XCTAssertEqual(Stream.Status.error, fileStream.streamStatus)
119+
}
120+
121+
private func createTestFile(_ path: String,_contents: Data) -> String? {
122+
let tempDir = "/tmp/TestFoundation_Playground_" + NSUUID().UUIDString + "/"
123+
do {
124+
try FileManager.default().createDirectory(atPath: tempDir, withIntermediateDirectories: false, attributes: nil)
125+
if FileManager.default().createFile(atPath: tempDir + "/" + path, contents: _contents, attributes: nil) {
126+
return tempDir + path
127+
} else {
128+
return nil
129+
}
130+
} catch _ {
131+
return nil
132+
}
133+
134+
}
135+
136+
private func removeTestFile(_ location: String) {
137+
do {
138+
try FileManager.default().removeItem(atPath: location)
139+
} catch _ {
140+
141+
}
142+
}
143+
}
144+
145+

TestFoundation/main.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ XCTMain([
5757
testCase(TestNSRunLoop.allTests),
5858
testCase(TestNSScanner.allTests),
5959
testCase(TestNSSet.allTests),
60+
testCase(TestNSStream.allTests),
6061
testCase(TestNSString.allTests),
6162
// testCase(TestNSThread.allTests),
6263
testCase(TestNSTask.allTests),

0 commit comments

Comments
 (0)