ObjFW  Check-in [c694569d86]

Overview
Comment:Generalize OFKernelEventObserver

It is no longer limited to streams now.

Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: c694569d86d87eeb79374b9cdae6243b61d5789d773f823ba4733111d0c66f54
User & Date: js on 2014-01-29 15:54:59
Other Links: manifest | tags
Context
2014-01-29
15:59
OFUDPSocket: Add observing check-in: c4f36e3692 user: js tags: trunk
15:54
Generalize OFKernelEventObserver check-in: c694569d86 user: js tags: trunk
09:54
resolver.h: Rename private to private_ for C++ check-in: dfd8e14567 user: js tags: trunk
Changes

Modified src/OFKernelEventObserver.h from [36076d4479] to [2129421b71].

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

40
41
42
43
44
45
46
47








48
49

50
51
52















53
54


55
56


57
58


59
60
61







62
63


64
65

66
67
68
69
70
71
72

73
74
75
76
77
78



79
80
81
82
83
84
85
14
15
16
17
18
19
20

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

38
39







40
41
42
43
44
45
46
47
48

49
50


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

67
68
69

70
71
72

73
74
75
76

77
78
79
80
81
82
83
84

85
86
87

88
89
90
91
92
93
94

95
96
97
98



99
100
101
102
103
104
105
106
107
108







-

















-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+

-
+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
+

-
+
+

-
+
+


-
+
+
+
+
+
+
+

-
+
+

-
+






-
+



-
-
-
+
+
+







 * file.
 */

#import "OFObject.h"

#import "socket.h"

@class OFStream;
@class OFMutableArray;
@class OFMutableDictionary;
@class OFDataArray;
#ifdef OF_HAVE_THREADS
@class OFMutex;
#endif
@class OFDate;

/*!
 * @brief A protocol that needs to be implemented by delegates for
 *	  OFKernelEventObserver.
 */
@protocol OFKernelEventObserverDelegate <OFObject>
#ifdef OF_HAVE_OPTIONAL_PROTOCOLS
@optional
#endif
/*!
 * @brief This callback is called when a stream did get ready for reading.
 * @brief This callback is called when an object did get ready for reading.
 *
 * @note When @ref OFStream::tryReadLine or
 *	 @ref OFStream::tryReadTillDelimiter: has been called on the stream,
 *	 this callback will not be called again until new data has been
 *	 received, even though there is still data in the cache. The reason for
 *	 this is to prevent spinning in a loop when there is an incomplete
 *	 string in the cache. Once the string is complete, the callback will be
 *	 called again if there is data in the cache.
 * @note If the object is a subclass of @ref OFStream and
 *	 @ref OFStream::tryReadLine or @ref OFStream::tryReadTillDelimiter: has
 *	 been called on the stream, this callback will not be called again
 *	 until new data has been received, even though there is still data in
 *	 the cache. The reason for this is to prevent spinning in a loop when
 *	 there is an incomplete string in the cache. Once the string has been
 *	 completed, the callback will be called again as long there is data in
 *	 the cache.
 *
 * @param stream The stream which did become ready for reading
 * @param object The object which did become ready for reading
 */
- (void)streamIsReadyForReading: (OFStream*)stream;

- (void)objectIsReadyForReading: (id)object;

/*!
 * @brief This callback is called when an object did get ready for writing.
 *
 * @param object The object which did become ready for writing
 */
- (void)objectIsReadyForWriting: (id)object;
@end

/*!
 * @brief This protocol is implemented by classes which can be observed for
 *	  readiness for reading by OFKernelEventObserver.
 */
@protocol OFReadyForReadingObserving <OFObject>
/*!
 * @brief This callback is called when a stream did get ready for writing.
 * @brief Returns the file descriptor for reading that should be checked by the
 *	  OFKernelEventObserver.
 *
 * @param stream The stream which did become ready for writing
 * @return The file descriptor for reading that should be checked by the
 *	   OFKernelEventObserver
 */
- (void)streamIsReadyForWriting: (OFStream*)stream;
- (int)fileDescriptorForReading;
@end

/*!
 * @brief This callback is called when an exception occurred on the stream.
 * @brief This protocol is implemented by classes which can be observed for
 *	  readiness for writing by OFKernelEventObserver.
 */
@protocol OFReadyForWritingObserving <OFObject>
/*!
 * @brief Returns the file descriptor for writing that should be checked by the
 *	  OFKernelEventObserver.
 *
 * @param stream The stream on which an exception occurred
 * @return The file descriptor for writing that should be checked by the
 *	   OFKernelEventObserver
 */
- (void)streamDidReceiveException: (OFStream*)stream;
- (int)fileDescriptorForWriting;
@end

/*!
 * @brief A class that can observe multiple kernel events (e.g. streams being
 *	  ready to read) at once.
 *
 * @note Currently, Win32 can only observe sockets and not files!
 * @note Currently, Win32 can only observe TCP sockets!
 */
@interface OFKernelEventObserver: OFObject
{
	OFMutableArray *_readStreams;
	OFMutableArray *_writeStreams;
	__unsafe_unretained OFStream **_FDToStream;
	OFMutableArray *_readObjects;
	OFMutableArray *_writeObjects;
	__unsafe_unretained id *_FDToObject;
	size_t _maxFD;
	OFMutableArray *_queue;
	OFDataArray *_queueInfo, *_queueFDs;
	id <OFKernelEventObserverDelegate> _delegate;
	int _cancelFD[2];
#ifndef OF_HAVE_PIPE
	struct sockaddr_in _cancelAddr;
111
112
113
114
115
116
117
118

119
120
121

122
123
124
125
126

127
128

129
130

131
132
133

134
135
136
137
138

139
140

141

142
143
144
145

146
147
148

149
150

151
152

153
154
155

156
157
158

159
160

161
162

163
164
165

166
167
168
169
170

171
172
173
174
175
176
177
178
179
180


181
182
183
184
185
186
187
188
189
190

191
192
193
194
195
196
197
198
199
134
135
136
137
138
139
140

141
142
143

144
145


146

147
148

149
150

151
152
153

154
155


156

157
158

159
160
161

162
163

164
165
166

167
168

169
170

171
172
173

174
175
176

177
178

179
180

181
182
183

184
185
186
187
188

189
190
191
192
193
194
195
196
197


198
199
200
201
202
203
204
205
206
207
208

209
210
211
212
213
214
215
216
217
218







-
+


-
+

-
-

-
+

-
+

-
+


-
+

-
-

-
+

-
+

+
-


-
+


-
+

-
+

-
+


-
+


-
+

-
+

-
+


-
+




-
+








-
-
+
+









-
+









 * @brief Sets the delegate for the OFKernelEventObserver.
 *
 * @param delegate The delegate for the OFKernelEventObserver
 */
- (void)setDelegate: (id <OFKernelEventObserverDelegate>)delegate;

/*!
 * @brief Adds a stream to observe for reading.
 * @brief Adds an object to observe for reading.
 *
 * This is also used to observe a listening socket for incoming connections,
 * which then triggers a read event for the observed stream.
 * which then triggers a read event for the observed object.
 *
 * It is recommended that the stream you add is set to non-blocking mode.
 *
 * If there is an @ref observe call blocking, it will be canceled. The reason
 * for this is to prevent blocking even though the new added stream is ready.
 * for this is to prevent blocking even though the newly added object is ready.
 *
 * @param stream The stream to observe for reading
 * @param object The object to observe for reading
 */
- (void)addStreamForReading: (OFStream*)stream;
- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object;

/*!
 * @brief Adds a stream to observe for writing.
 * @brief Adds an object to observe for writing.
 *
 * It is recommended that the stream you add is set to non-blocking mode.
 *
 * If there is an @ref observe call blocking, it will be canceled. The reason
 * for this is to prevent blocking even though the new added stream is ready.
 * for this is to prevent blocking even though the newly added object is ready.
 *
 * @param stream The stream to observe for writing
 * @param object The object to observe for writing
 */
- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object;
- (void)addStreamForWriting: (OFStream*)stream;

/*!
 * @brief Removes a stream to observe for reading.
 * @brief Removes an object to observe for reading.
 *
 * If there is an @ref observe call blocking, it will be canceled. The reason
 * for this is to prevent the removed stream from still being observed.
 * for this is to prevent the removed object from still being observed.
 *
 * @param stream The stream to remove from observing for reading
 * @param object The object to remove from observing for reading
 */
- (void)removeStreamForReading: (OFStream*)stream;
- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object;

/*!
 * @brief Removes a stream to observe for writing.
 * @brief Removes an object to observe for writing.
 *
 * If there is an @ref observe call blocking, it will be canceled. The reason
 * for this is to prevent the removed stream from still being observed.
 * for this is to prevent the removed object from still being observed.
 *
 * @param stream The stream to remove from observing for writing
 * @param object The object to remove from observing for writing
 */
- (void)removeStreamForWriting: (OFStream*)stream;
- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object;

/*!
 * @brief Observes all streams and blocks until an event happens on a stream.
 * @brief Observes all objects and blocks until an event happens on an object.
 */
- (void)observe;

/*!
 * @brief Observes all streams until an event happens on a stream or the
 * @brief Observes all objects until an event happens on an object or the
 *	  timeout is reached.
 *
 * @param timeInterval The time to wait for an event, in seconds
 * @return A boolean whether events occurred during the timeinterval
 */
- (bool)observeForTimeInterval: (of_time_interval_t)timeInterval;

/*!
 * @brief Observes all streams until an event happens on a stream or the
 *	  timeout is reached.
 * @brief Observes all objects until an event happens on an object or the
 *	  specified date is reached.
 *
 * @param date The until which to observe
 * @return A boolean whether events occurred until the specified date
 */
- (bool)observeUntilDate: (OFDate*)date;

/*!
 * @brief Cancels the currently blocking observe call.
 *
 * This is automatically done when a new stream is added or removed by another
 * This is automatically done when a new object is added or removed by another
 * thread, but in some circumstances, it might be desirable for a thread to
 * manually stop the observe running in another thread.
 */
- (void)cancel;
@end

@interface OFObject (OFKernelEventObserverDelegate)
    <OFKernelEventObserverDelegate>
@end

Modified src/OFKernelEventObserver.m from [106e3c4be1] to [71bb648f05].

42
43
44
45
46
47
48

49
50
51
52
53
54
55
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56







+







# import "OFKernelEventObserver_poll.h"
#endif
#if defined(HAVE_SYS_SELECT_H) || defined(_WIN32)
# import "OFKernelEventObserver_select.h"
#endif

#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFOutOfRangeException.h"

#import "autorelease.h"
#import "macros.h"
#import "socket_helpers.h"

enum {
100
101
102
103
104
105
106
107
108


109
110
111
112
113
114
115
101
102
103
104
105
106
107


108
109
110
111
112
113
114
115
116







-
-
+
+







#ifndef OF_HAVE_PIPE
		struct sockaddr_in cancelAddr2;
# ifndef __wii__
		socklen_t cancelAddrLen;
# endif
#endif

		_readStreams = [[OFMutableArray alloc] init];
		_writeStreams = [[OFMutableArray alloc] init];
		_readObjects = [[OFMutableArray alloc] init];
		_writeObjects = [[OFMutableArray alloc] init];
		_queue = [[OFMutableArray alloc] init];
		_queueInfo = [[OFDataArray alloc]
		    initWithItemSize: sizeof(int)];
		_queueFDs = [[OFDataArray alloc] initWithItemSize: sizeof(int)];

#ifdef OF_HAVE_PIPE
		if (pipe(_cancelFD))
150
151
152
153
154
155
156
157

158
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178


179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206

207
208






















209
210
211
212
213
214
215
216
217
218
219
220

221
222
223
224
225
226
227


228
229

230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269

270
271

272
273
274
275
276
277
278
151
152
153
154
155
156
157

158
159

160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177


178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199

200
201
202
203
204
205
206

207
208

209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241

242
243
244
245
246
247


248
249
250

251
252
253
254
255
256
257
258
259
260
261
262









263













264
265
266
267
268
269

270
271

272
273
274
275
276
277
278
279







-
+

-
+

















-
-
+
+




















-
+






-
+

-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+





-
-
+
+

-
+











-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-






-
+

-
+







		    &cancelAddrLen))
			@throw [OFInitializationFailedException
			    exceptionWithClass: [self class]];
# endif
#endif

		_maxFD = _cancelFD[0];
		_FDToStream = [self allocMemoryWithSize: sizeof(OFStream*)
		_FDToObject = [self allocMemoryWithSize: sizeof(id)
						  count: _maxFD + 1];
		_FDToStream[_cancelFD[0]] = nil;
		_FDToObject[_cancelFD[0]] = nil;

#ifdef OF_HAVE_THREADS
		_mutex = [[OFMutex alloc] init];
#endif
	} @catch (id e) {
		[self release];
		@throw e;
	}

	return self;
}

- (void)dealloc
{
	close(_cancelFD[0]);
	close(_cancelFD[1]);

	[_readStreams release];
	[_writeStreams release];
	[_readObjects release];
	[_writeObjects release];
	[_queue release];
	[_queueInfo release];
	[_queueFDs release];
#ifdef OF_HAVE_THREADS
	[_mutex release];
#endif

	[super dealloc];
}

- (id <OFKernelEventObserverDelegate>)delegate
{
	return _delegate;
}

- (void)setDelegate: (id <OFKernelEventObserverDelegate>)delegate
{
	_delegate = delegate;
}

- (void)addStreamForReading: (OFStream*)stream
- (void)addObjectForReading: (id <OFReadyForReadingObserving>)object
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		int qi = QUEUE_ADD | QUEUE_READ;
		int fd = [stream fileDescriptorForReading];
		int fd = [object fileDescriptorForReading];

		[_queue addObject: stream];
		[_queue addObject: object];
		[_queueInfo addItem: &qi];
		[_queueFDs addItem: &fd];
	} @finally {
#ifdef OF_HAVE_THREADS
		[_mutex unlock];
#endif
	}

	[self cancel];
}

- (void)addObjectForWriting: (id <OFReadyForWritingObserving>)object
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		int qi = QUEUE_ADD | QUEUE_WRITE;
		int fd = [object fileDescriptorForWriting];

		[_queue addObject: object];
		[_queueInfo addItem: &qi];
		[_queueFDs addItem: &fd];
	} @finally {
#ifdef OF_HAVE_THREADS
		[_mutex unlock];
#endif
	}

	[self cancel];
}

- (void)addStreamForWriting: (OFStream*)stream
- (void)removeObjectForReading: (id <OFReadyForReadingObserving>)object
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		int qi = QUEUE_ADD | QUEUE_WRITE;
		int fd = [stream fileDescriptorForWriting];
		int qi = QUEUE_REMOVE | QUEUE_READ;
		int fd = [object fileDescriptorForReading];

		[_queue addObject: stream];
		[_queue addObject: object];
		[_queueInfo addItem: &qi];
		[_queueFDs addItem: &fd];
	} @finally {
#ifdef OF_HAVE_THREADS
		[_mutex unlock];
#endif
	}

	[self cancel];
}

- (void)removeStreamForReading: (OFStream*)stream
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		int qi = QUEUE_REMOVE | QUEUE_READ;
		int fd = [stream fileDescriptorForReading];

- (void)removeObjectForWriting: (id <OFReadyForWritingObserving>)object
		[_queue addObject: stream];
		[_queueInfo addItem: &qi];
		[_queueFDs addItem: &fd];
	} @finally {
#ifdef OF_HAVE_THREADS
		[_mutex unlock];
#endif
	}

	[self cancel];
}

- (void)removeStreamForWriting: (OFStream*)stream
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		int qi = QUEUE_REMOVE | QUEUE_WRITE;
		int fd = [stream fileDescriptorForWriting];
		int fd = [object fileDescriptorForWriting];

		[_queue addObject: stream];
		[_queue addObject: object];
		[_queueInfo addItem: &qi];
		[_queueFDs addItem: &fd];
	} @finally {
#ifdef OF_HAVE_THREADS
		[_mutex unlock];
#endif
	}
302
303
304
305
306
307
308
309

310
311
312
313
314
315

316
317
318
319
320
321
322
323
324



325
326
327
328

329
330
331
332
333

334
335
336
337
338

339
340
341
342
343
344

345
346
347
348
349
350

351
352
353
354
355
356

357
358
359
360
361
362
363
303
304
305
306
307
308
309

310
311
312
313
314
315

316
317
318
319
320
321
322



323
324
325
326
327
328

329
330
331
332
333

334
335
336
337
338

339
340
341
342
343
344

345
346
347
348
349
350

351
352
353
354
355
356

357
358
359
360
361
362
363
364







-
+





-
+






-
-
-
+
+
+



-
+




-
+




-
+





-
+





-
+





-
+








- (void)OF_processQueue
{
#ifdef OF_HAVE_THREADS
	[_mutex lock];
#endif
	@try {
		OFStream **queueObjects = [_queue objects];
		id *queueObjects = [_queue objects];
		int *queueInfoItems = [_queueInfo items];
		int *queueFDsItems = [_queueFDs items];
		size_t i, count = [_queue count];

		for (i = 0; i < count; i++) {
			OFStream *stream = queueObjects[i];
			id object = queueObjects[i];
			int action = queueInfoItems[i];
			int fd = queueFDsItems[i];

			if ((action & QUEUE_ACTION) == QUEUE_ADD) {
				if (fd > _maxFD) {
					_maxFD = fd;
					_FDToStream = [self
					    resizeMemory: _FDToStream
						    size: sizeof(OFStream*)
					_FDToObject = [self
					    resizeMemory: _FDToObject
						    size: sizeof(id)
						   count: _maxFD + 1];
				}

				_FDToStream[fd] = stream;
				_FDToObject[fd] = object;
			}

			if ((action & QUEUE_ACTION) == QUEUE_REMOVE) {
				/* FIXME: Maybe downsize? */
				_FDToStream[fd] = nil;
				_FDToObject[fd] = nil;
			}

			switch (action) {
			case QUEUE_ADD | QUEUE_READ:
				[_readStreams addObject: stream];
				[_readObjects addObject: object];

				[self OF_addFileDescriptorForReading: fd];

				break;
			case QUEUE_ADD | QUEUE_WRITE:
				[_writeStreams addObject: stream];
				[_writeObjects addObject: object];

				[self OF_addFileDescriptorForWriting: fd];

				break;
			case QUEUE_REMOVE | QUEUE_READ:
				[_readStreams removeObjectIdenticalTo: stream];
				[_readObjects removeObjectIdenticalTo: object];

				[self OF_removeFileDescriptorForReading: fd];

				break;
			case QUEUE_REMOVE | QUEUE_WRITE:
				[_writeStreams removeObjectIdenticalTo: stream];
				[_writeObjects removeObjectIdenticalTo: object];

				[self OF_removeFileDescriptorForWriting: fd];

				break;
			default:
				assert(0);
			}
396
397
398
399
400
401
402
403
404


405
406
407

408

409
410
411
412
413
414


415
416
417
418
419
420
421
397
398
399
400
401
402
403


404
405
406
407
408
409

410
411
412
413
414


415
416
417
418
419
420
421
422
423







-
-
+
+



+
-
+




-
-
+
+







	OF_ENSURE(sendto(_cancelFD[1], "", 1, 0, (struct sockaddr*)&_cancelAddr,
	    sizeof(_cancelAddr)) > 0);
#endif
}

- (bool)OF_processCache
{
	OFStream **objects = [_readStreams objects];
	size_t i, count = [_readStreams count];
	id *objects = [_readObjects objects];
	size_t i, count = [_readObjects count];
	bool foundInCache = false;

	for (i = 0; i < count; i++) {
		if ([objects[i] isKindOfClass: [OFStream class]] &&
		if ([objects[i] numberOfBytesInReadBuffer] > 0 &&
		    [objects[i] numberOfBytesInReadBuffer] > 0 &&
		    ![objects[i] OF_isWaitingForDelimiter]) {
			void *pool = objc_autoreleasePoolPush();

			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForReading:)])
				[_delegate streamIsReadyForReading: objects[i]];
			    @selector(objectsIsReadyForReading:)])
				[_delegate objectIsReadyForReading: objects[i]];

			foundInCache = true;

			objc_autoreleasePoolPop(pool);
		}
	}

Modified src/OFKernelEventObserver_kqueue.m from [e09ea174d3] to [d4951ca160].

148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170



171
172
173
174
175
176



177
178
179
180
181
182
183
148
149
150
151
152
153
154










155
156
157



158
159
160
161
162
163



164
165
166
167
168
169
170
171
172
173







-
-
-
-
-
-
-
-
-
-



-
-
-
+
+
+



-
-
-
+
+
+







			continue;
		}

		realEvents++;

		pool = objc_autoreleasePoolPush();

		if (eventList[i].flags & EV_ERROR) {
			if ([_delegate respondsToSelector:
			    @selector(streamDidReceiveException:)])
				[_delegate streamDidReceiveException:
				    _FDToStream[eventList[i].ident]];

			objc_autoreleasePoolPop(pool);
			continue;
		}

		switch (eventList[i].filter) {
		case EVFILT_READ:
			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForReading:)])
				[_delegate streamIsReadyForReading:
				    _FDToStream[eventList[i].ident]];
			    @selector(objectIsReadyForReading:)])
				[_delegate objectIsReadyForReading:
				    _FDToObject[eventList[i].ident]];
			break;
		case EVFILT_WRITE:
			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForWriting:)])
				[_delegate streamIsReadyForWriting:
				    _FDToStream[eventList[i].ident]];
			    @selector(objectIsReadyForWriting:)])
				[_delegate objectIsReadyForWriting:
				    _FDToObject[eventList[i].ident]];
			break;
		default:
			assert(0);
		}

		objc_autoreleasePoolPop(pool);
	}

Modified src/OFKernelEventObserver_poll.m from [928947a9f6] to [b0a53ca94b].

78
79
80
81
82
83
84
85

86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

101
102
103
104
105
106
107
78
79
80
81
82
83
84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
107







-
+














-
+







			FDs[i].events |= events;
			found = true;
			break;
		}
	}

	if (!found) {
		struct pollfd p = { fd, events | POLLERR, 0 };
		struct pollfd p = { fd, events, 0 };
		[_FDs addItem: &p];
	}
}

- (void)OF_removeFileDescriptor: (int)fd
		     withEvents: (short)events
{
	struct pollfd *FDs = [_FDs items];
	size_t i, nFDs = [_FDs count];

	for (i = 0; i < nFDs; i++) {
		if (FDs[i].fd == fd) {
			FDs[i].events &= ~events;

			if ((FDs[i].events & ~POLLERR) == 0)
			if (FDs[i].events == 0)
				[_FDs removeItemAtIndex: i];

			break;
		}
	}
}

167
168
169
170
171
172
173
174
175
176



177
178
179
180
181
182
183
184
185



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
167
168
169
170
171
172
173



174
175
176
177
178
179
180
181
182



183
184
185









186
187
188
189
190
191
192







-
-
-
+
+
+






-
-
-
+
+
+
-
-
-
-
-
-
-
-
-







				FDs[i].revents = 0;

				objc_autoreleasePoolPop(pool);
				continue;
			}

			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForReading:)])
				[_delegate streamIsReadyForReading:
				    _FDToStream[FDs[i].fd]];
			    @selector(objectIsReadyForReading:)])
				[_delegate objectIsReadyForReading:
				    _FDToObject[FDs[i].fd]];

			realEvents++;
		}

		if (FDs[i].revents & POLLOUT) {
			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForWriting:)])
				[_delegate streamIsReadyForWriting:
				    _FDToStream[FDs[i].fd]];
			    @selector(objectIsReadyForWriting:)])
				[_delegate objectIsReadyForWriting:
				    _FDToObject[FDs[i].fd]];

			realEvents++;
		}

		if (FDs[i].revents & POLLERR) {
			if ([_delegate respondsToSelector:
			    @selector(streamDidReceiveException:)])
				[_delegate streamDidReceiveException:
				    _FDToStream[FDs[i].fd]];

			realEvents++;
		}

		FDs[i].revents = 0;

		objc_autoreleasePoolPop(pool);

Modified src/OFKernelEventObserver_select.h from [688ffde946] to [265931990a].

25
26
27
28
29
30
31
32

33
34
25
26
27
28
29
30
31

32
33
34







-
+


# include <sys/select.h>
#endif

#import "OFKernelEventObserver.h"

@interface OFKernelEventObserver_select: OFKernelEventObserver
{
	fd_set _readFDs, _writeFDs, _exceptFDs;
	fd_set _readFDs, _writeFDs;
}
@end

Modified src/OFKernelEventObserver_select.m from [d472e2c55c] to [d61b6807c3].

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
22
23
24
25
26
27
28

29
30
31
32
33
34
35







-







#include <math.h>

#include <sys/time.h>

#import "OFKernelEventObserver.h"
#import "OFKernelEventObserver+Private.h"
#import "OFKernelEventObserver_select.h"
#import "OFStream.h"
#import "OFArray.h"

#import "autorelease.h"
#import "macros.h"
#import "socket_helpers.h"

@implementation OFKernelEventObserver_select
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

116
117
118
119
120
121
122
123
124
125
126
127
128
129
130


131
132
133
134
135
136
137
138
139
140


141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164


165
166
167
168
169
170
171
172
173
174


175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
44
45
46
47
48
49
50

51
52
53
54
55

56
57
58
59
60



61
62
63
64
65



66
67
68
69
70

71
72
73

74
75
76
77
78
79
80
81
82
83
84
85
86
87
88

89
90
91

92
93
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108
109
110
111
112
113
114
115
116


117
118
119
120
121
122
123
124
125
126


127
128















129
130
131
132
133
134
135


136
137
138
139
140
141
142
143
144
145


146
147









148
149
150
151
152
153
154
155
156
157
158
159
160







-





-





-
-
-





-
-
-





-
+


-















-



-











-
+













-
-
+
+








-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







-
-
+
+








-
-
+
+
-
-
-
-
-
-
-
-
-














	return self;
}

- (void)OF_addFileDescriptorForReading: (int)fd
{
	FD_SET(fd, &_readFDs);
	FD_SET(fd, &_exceptFDs);
}

- (void)OF_addFileDescriptorForWriting: (int)fd
{
	FD_SET(fd, &_writeFDs);
	FD_SET(fd, &_exceptFDs);
}

- (void)OF_removeFileDescriptorForReading: (int)fd
{
	FD_CLR(fd, &_readFDs);

	if (!FD_ISSET(fd, &_writeFDs))
		FD_CLR(fd, &_exceptFDs);
}

- (void)OF_removeFileDescriptorForWriting: (int)fd
{
	FD_CLR(fd, &_writeFDs);

	if (!FD_ISSET(fd, &_readFDs))
		FD_CLR(fd, &_exceptFDs);
}

- (bool)observeForTimeInterval: (of_time_interval_t)timeInterval
{
	void *pool = objc_autoreleasePoolPush();
	OFStream **objects;
	id *objects;
	fd_set readFDs;
	fd_set writeFDs;
	fd_set exceptFDs;
	struct timeval timeout;
	size_t i, count, realEvents = 0;

	[self OF_processQueue];

	if ([self OF_processCache]) {
		objc_autoreleasePoolPop(pool);
		return true;
	}

	objc_autoreleasePoolPop(pool);

#ifdef FD_COPY
	FD_COPY(&_readFDs, &readFDs);
	FD_COPY(&_writeFDs, &writeFDs);
	FD_COPY(&_exceptFDs, &exceptFDs);
#else
	readFDs = _readFDs;
	writeFDs = _writeFDs;
	exceptFDs = _exceptFDs;
#endif

	/*
	 * We cast to int before assigning to tv_usec in order to avoid a
	 * warning with Apple GCC on PPC. POSIX defines this as suseconds_t,
	 * however, this is not available on Win32. As an int should always
	 * satisfy the required range, we just cast to int.
	 */
	timeout.tv_sec = (time_t)timeInterval;
	timeout.tv_usec = (int)lrint((timeInterval - timeout.tv_sec) * 1000);

	if (select((int)_maxFD + 1, &readFDs, &writeFDs, &exceptFDs,
	if (select((int)_maxFD + 1, &readFDs, &writeFDs, NULL,
	    (timeInterval != -1 ? &timeout : NULL)) < 1)
		return false;

	if (FD_ISSET(_cancelFD[0], &readFDs)) {
		char buffer;
#ifndef _WIN32
		OF_ENSURE(read(_cancelFD[0], &buffer, 1) > 0);
#else
		OF_ENSURE(recvfrom(_cancelFD[0], &buffer, 1, 0, NULL,
		    NULL) > 0);
#endif
	}

	objects = [_readStreams objects];
	count = [_readStreams count];
	objects = [_readObjects objects];
	count = [_readObjects count];

	for (i = 0; i < count; i++) {
		int fd = [objects[i] fileDescriptorForReading];

		pool = objc_autoreleasePoolPush();

		if (FD_ISSET(fd, &readFDs)) {
			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForReading:)])
				[_delegate streamIsReadyForReading: objects[i]];
			    @selector(objectIsReadyForReading:)])
				[_delegate objectIsReadyForReading: objects[i]];

			realEvents++;
		}

		if (FD_ISSET(fd, &exceptFDs)) {
			if ([_delegate respondsToSelector:
			    @selector(streamDidReceiveException:)])
				[_delegate streamDidReceiveException:
				    objects[i]];

			/*
			 * Prevent calling it twice in case the FD is in both
			 * sets.
			 */
			FD_CLR(fd, &exceptFDs);

			realEvents++;
		}

		objc_autoreleasePoolPop(pool);
	}

	objects = [_writeStreams objects];
	count = [_writeStreams count];
	objects = [_writeObjects objects];
	count = [_writeObjects count];

	for (i = 0; i < count; i++) {
		int fd = [objects[i] fileDescriptorForWriting];

		pool = objc_autoreleasePoolPush();

		if (FD_ISSET(fd, &writeFDs)) {
			if ([_delegate respondsToSelector:
			    @selector(streamIsReadyForWriting:)])
				[_delegate streamIsReadyForWriting: objects[i]];
			    @selector(objectIsReadyForWriting:)])
				[_delegate objectIsReadyForWriting: objects[i]];

			realEvents++;
		}

		if (FD_ISSET(fd, &exceptFDs)) {
			if ([_delegate respondsToSelector:
			    @selector(streamDidReceiveException:)])
				[_delegate streamDidReceiveException:
				    objects[i]];

			realEvents++;
		}

		objc_autoreleasePoolPop(pool);
	}

	if (realEvents == 0)
		return false;

	return true;
}
@end

Modified src/OFRunLoop.m from [50e05df3a1] to [870ab6e96c].

174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
174
175
176
177
178
179
180

181
182
183
184
185
186
187
188







-
+







		queue = [OFList list];					\
		[runLoop->_readQueues setObject: queue			\
					 forKey: stream];		\
	}								\
									\
	if ([queue count] == 0)						\
		[runLoop->_kernelEventObserver				\
		    addStreamForReading: stream];			\
		    addObjectForReading: stream];			\
									\
	queueItem = [[[type alloc] init] autorelease];			\
	code								\
	[queue appendObject: queueItem];				\
									\
	objc_autoreleasePoolPop(pool);

286
287
288
289
290
291
292
293

294
295
296
297
298
299
300
286
287
288
289
290
291
292

293
294
295
296
297
298
299
300







-
+







	void *pool = objc_autoreleasePoolPush();
	OFRunLoop *runLoop = [self currentRunLoop];
	OFList *queue;

	if ((queue = [runLoop->_readQueues objectForKey: stream]) != nil) {
		assert([queue count] > 0);

		[runLoop->_kernelEventObserver removeStreamForReading: stream];
		[runLoop->_kernelEventObserver removeObjectForReading: stream];
		[runLoop->_readQueues removeObjectForKey: stream];
	}

	objc_autoreleasePoolPop(pool);
}
#endif

383
384
385
386
387
388
389
390

391
392

393
394
395
396
397
398
399
400
401
402
403
404
405
406

407
408
409
410
411
412
413
414
415

416
417
418
419
420
421

422
423

424
425
426
427
428
429
430
431
432
433
434
435

436
437
438
439
440

441
442

443
444
445
446
447
448
449
450
451
452
453
454
455

456
457
458
459
460
461
462
463
464
465
466
467

468
469
470

471
472
473
474
475
476
477
478
479
480


481
482

483
484
485
486
487
488
489
490
491
492
493
494

495
496
497
498
499
500
501
502
503
504


505
506

507
508
509
510
511
512
513
514
515
516
517
518
519
520

521
522
523
524
525
526
527

528
529
530
531

532
533
534
535
536
537
538


539
540

541
542
543
544
545
546
547
548
549
550
551
552

553
554
555
556
557
558
559


560
561

562
563
564
565
566
567
568
569
570
571
572
573
574
575

576
577
578
579
580
581
582
583

584
585
586
587
588
589

590
591

592
593
594
595
596
597
598
599
600
601
602
603
604

605
606
607
608
609

610
611

612
613
614
615
616
617
618
383
384
385
386
387
388
389

390
391

392
393
394
395
396
397
398
399
400
401
402
403
404
405

406
407
408
409
410
411
412
413
414

415
416
417
418
419
420

421
422

423
424
425
426
427
428
429
430
431
432
433
434

435
436
437
438
439

440
441

442
443
444
445
446
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
462
463
464
465
466

467
468
469

470
471
472
473
474
475
476
477
478


479
480
481

482
483
484
485
486
487
488
489
490
491
492
493

494
495
496
497
498
499
500
501
502


503
504
505

506
507
508
509
510
511
512
513
514
515
516
517
518
519

520
521
522
523
524
525
526

527
528
529
530

531
532
533
534
535
536


537
538
539

540
541
542
543
544
545
546
547
548
549
550
551

552
553
554
555
556
557


558
559
560

561
562
563
564
565
566
567
568
569
570
571
572
573
574

575
576
577
578
579
580
581
582

583

584
585
586
587

588
589

590
591
592
593
594
595
596
597
598
599
600
601
602

603
604
605
606
607

608
609

610
611
612
613
614
615
616
617







-
+

-
+













-
+








-
+





-
+

-
+











-
+




-
+

-
+












-
+











-
+


-
+








-
-
+
+

-
+











-
+








-
-
+
+

-
+













-
+






-
+



-
+





-
-
+
+

-
+











-
+





-
-
+
+

-
+













-
+







-
+
-




-
+

-
+












-
+




-
+

-
+







	} @finally {
		[_timersQueueLock unlock];
	}
#endif
}

#ifdef OF_HAVE_SOCKETS
- (void)streamIsReadyForReading: (OFStream*)stream
- (void)objectIsReadyForReading: (id)object
{
	OFList *queue = [_readQueues objectForKey: stream];
	OFList *queue = [_readQueues objectForKey: object];
	of_list_object_t *listObject;

	assert(queue != nil);

	listObject = [queue firstListObject];

	if ([listObject->object isKindOfClass:
	    [OFRunLoop_ReadQueueItem class]]) {
		OFRunLoop_ReadQueueItem *queueItem = listObject->object;
		size_t length;
		OFException *exception = nil;

		@try {
			length = [stream readIntoBuffer: queueItem->_buffer
			length = [object readIntoBuffer: queueItem->_buffer
						 length: queueItem->_length];
		} @catch (OFException *e) {
			length = 0;
			exception = e;
		}

# ifdef OF_HAVE_BLOCKS
		if (queueItem->_block != NULL) {
			if (!queueItem->_block(stream, queueItem->_buffer,
			if (!queueItem->_block(object, queueItem->_buffer,
			    length, exception)) {
				[queue removeListObject: listObject];

				if ([queue count] == 0) {
					[_kernelEventObserver
					    removeStreamForReading: stream];
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: stream];
					    removeObjectForKey: object];
				}
			}
		} else {
# endif
			bool (*func)(id, SEL, OFStream*, void*, size_t,
			    OFException*) = (bool(*)(id, SEL, OFStream*, void*,
			    size_t, OFException*))
			    [queueItem->_target methodForSelector:
			    queueItem->_selector];

			if (!func(queueItem->_target, queueItem->_selector,
			    stream, queueItem->_buffer, length, exception)) {
			    object, queueItem->_buffer, length, exception)) {
				[queue removeListObject: listObject];

				if ([queue count] == 0) {
					[_kernelEventObserver
					    removeStreamForReading: stream];
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: stream];
					    removeObjectForKey: object];
				}
			}
# ifdef OF_HAVE_BLOCKS
		}
# endif
	} else if ([listObject->object isKindOfClass:
	    [OFRunLoop_ExactReadQueueItem class]]) {
		OFRunLoop_ExactReadQueueItem *queueItem = listObject->object;
		size_t length;
		OFException *exception = nil;

		@try {
			length = [stream
			length = [object
			    readIntoBuffer: (char*)queueItem->_buffer +
					    queueItem->_readLength
				    length: queueItem->_exactLength -
					    queueItem->_readLength];
		} @catch (OFException *e) {
			length = 0;
			exception = e;
		}

		queueItem->_readLength += length;
		if (queueItem->_readLength == queueItem->_exactLength ||
		    [stream isAtEndOfStream] || exception != nil) {
		    [object isAtEndOfStream] || exception != nil) {
# ifdef OF_HAVE_BLOCKS
			if (queueItem->_block != NULL) {
				if (queueItem->_block(stream,
				if (queueItem->_block(object,
				    queueItem->_buffer, queueItem->_readLength,
				    exception))
					queueItem->_readLength = 0;
				else {
					[queue removeListObject: listObject];

					if ([queue count] == 0) {
						[_kernelEventObserver
						    removeStreamForReading:
						    stream];
						    removeObjectForReading:
						    object];
						[_readQueues
						    removeObjectForKey: stream];
						    removeObjectForKey: object];
					}
				}
			} else {
# endif
				bool (*func)(id, SEL, OFStream*, void*,
				    size_t, OFException*) = (bool(*)(id, SEL,
				    OFStream*, void*, size_t, OFException*))
				    [queueItem->_target
				    methodForSelector: queueItem->_selector];

				if (func(queueItem->_target,
				    queueItem->_selector, stream,
				    queueItem->_selector, object,
				    queueItem->_buffer, queueItem->_readLength,
				    exception))
					queueItem->_readLength = 0;
				else {
					[queue removeListObject: listObject];

					if ([queue count] == 0) {
						[_kernelEventObserver
						    removeStreamForReading:
						    stream];
						    removeObjectForReading:
						    object];
						[_readQueues
						    removeObjectForKey: stream];
						    removeObjectForKey: object];
					}
				}
# ifdef OF_HAVE_BLOCKS
			}
# endif
		}
	} else if ([listObject->object isKindOfClass:
	    [OFRunLoop_ReadLineQueueItem class]]) {
		OFRunLoop_ReadLineQueueItem *queueItem = listObject->object;
		OFString *line;
		OFException *exception = nil;

		@try {
			line = [stream
			line = [object
			    tryReadLineWithEncoding: queueItem->_encoding];
		} @catch (OFException *e) {
			line = nil;
			exception = e;
		}

		if (line != nil || [stream isAtEndOfStream] ||
		if (line != nil || [object isAtEndOfStream] ||
		    exception != nil) {
# ifdef OF_HAVE_BLOCKS
			if (queueItem->_block != NULL) {
				if (!queueItem->_block(stream, line,
				if (!queueItem->_block(object, line,
				    exception)) {
					[queue removeListObject: listObject];

					if ([queue count] == 0) {
						[_kernelEventObserver
						    removeStreamForReading:
						    stream];
						    removeObjectForReading:
						    object];
						[_readQueues
						    removeObjectForKey: stream];
						    removeObjectForKey: object];
					}
				}
			} else {
# endif
				bool (*func)(id, SEL, OFStream*, OFString*,
				    OFException*) = (bool(*)(id, SEL, OFStream*,
				    OFString*, OFException*))
				    [queueItem->_target methodForSelector:
				    queueItem->_selector];

				if (!func(queueItem->_target,
				    queueItem->_selector, stream, line,
				    queueItem->_selector, object, line,
				    exception)) {
					[queue removeListObject: listObject];

					if ([queue count] == 0) {
						[_kernelEventObserver
						    removeStreamForReading:
						    stream];
						    removeObjectForReading:
						    object];
						[_readQueues
						    removeObjectForKey: stream];
						    removeObjectForKey: object];
					}
				}
# ifdef OF_HAVE_BLOCKS
			}
# endif
		}
	} else if ([listObject->object isKindOfClass:
	    [OFRunLoop_AcceptQueueItem class]]) {
		OFRunLoop_AcceptQueueItem *queueItem = listObject->object;
		OFTCPSocket *newSocket;
		OFException *exception = nil;

		@try {
			newSocket = [(OFTCPSocket*)stream accept];
			newSocket = [object accept];
		} @catch (OFException *e) {
			newSocket = nil;
			exception = e;
		}

# ifdef OF_HAVE_BLOCKS
		if (queueItem->_block != NULL) {
			if (!queueItem->_block((OFTCPSocket*)stream,
			if (!queueItem->_block(object, newSocket, exception)) {
			    newSocket, exception)) {
				[queue removeListObject: listObject];

				if ([queue count] == 0) {
					[_kernelEventObserver
					    removeStreamForReading: stream];
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: stream];
					    removeObjectForKey: object];
				}
			}
		} else {
# endif
			bool (*func)(id, SEL, OFTCPSocket*, OFTCPSocket*,
			    OFException*) =
			    (bool(*)(id, SEL, OFTCPSocket*, OFTCPSocket*,
			    OFException*))
			    [queueItem->_target methodForSelector:
			    queueItem->_selector];

			if (!func(queueItem->_target, queueItem->_selector,
			    (OFTCPSocket*)stream, newSocket, exception)) {
			    object, newSocket, exception)) {
				[queue removeListObject: listObject];

				if ([queue count] == 0) {
					[_kernelEventObserver
					    removeStreamForReading: stream];
					    removeObjectForReading: object];
					[_readQueues
					    removeObjectForKey: stream];
					    removeObjectForKey: object];
				}
			}
# ifdef OF_HAVE_BLOCKS
		}
# endif
	} else
		assert(0);
669
670
671
672
673
674
675
676

677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695

696
697
698
699
700
701
702
668
669
670
671
672
673
674

675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693

694
695
696
697
698
699
700
701







-
+


















-
+







			nextTimer = [[_timersQueue firstObject] fireDate];
#ifdef OF_HAVE_THREADS
		} @finally {
			[_timersQueueLock unlock];
		}
#endif

		/* Watch for stream events until the next timer is due */
		/* Watch for I/O events until the next timer is due */
		if (nextTimer != nil) {
			of_time_interval_t timeout =
			    [nextTimer timeIntervalSinceNow];

			if (timeout > 0) {
#if defined(OF_HAVE_SOCKETS)
				[_kernelEventObserver
				    observeForTimeInterval: timeout];
#elif defined(OF_HAVE_THREADS)
				[_condition lock];
				[_condition waitForTimeInterval: timeout];
				[_condition unlock];
#else
				[OFThread sleepForTimeInterval: timeout];
#endif
			}
		} else {
			/*
			 * No more timers: Just watch for streams until we get
			 * No more timers: Just watch for I/O until we get
			 * an event. If a timer is added by another thread, it
			 * cancels the observe.
			 */
#if defined(OF_HAVE_SOCKETS)
			[_kernelEventObserver observe];
#elif defined(OF_HAVE_THREADS)
			[_condition lock];

Modified src/OFStream.h from [ac84d7179d] to [f656291875].

21
22
23
24
25
26
27

28
29
30
31
32
33
34
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35







+







# define __STDC_CONSTANT_MACROS
#endif

#include <stdarg.h>

#import "OFObject.h"
#import "OFString.h"
#import "OFKernelEventObserver.h"

/*! @file */

@class OFStream;
@class OFDataArray;
@class OFException;

71
72
73
74
75
76
77
78


79
80
81
82
83
84
85
72
73
74
75
76
77
78

79
80
81
82
83
84
85
86
87







-
+
+







 *	 @ref lowlevelReadIntoBuffer:length:, @ref lowlevelWriteBuffer:length:
 *	 and @ref lowlevelIsAtEndOfStream, but nothing else, as those are are
 *	 the methods that do the actual work. OFStream uses those for all other
 *	 methods and does all the caching and other stuff for you. If you
 *	 override these methods without the lowlevel prefix, you *will* break
 *	 caching and get broken results!
 */
@interface OFStream: OFObject <OFCopying>
@interface OFStream: OFObject <OFCopying, OFReadyForReadingObserving,
    OFReadyForWritingObserving>
{
	char *_readBuffer, *_writeBuffer;
	size_t _readBufferLength, _writeBufferLength;
	bool _writeBufferEnabled, _blocking, _waitingForDelimiter;
}

#ifdef OF_HAVE_PROPERTIES
136
137
138
139
140
141
142



143
144
145
146
147
148
149
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154







+
+
+







 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
 * return 0 bytes - this does not necessarily mean that the stream ended, so
 * you still need to check @ref isAtEndOfStream.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be free'd before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
164
165
166
167
168
169
170



171
172
173
174
175
176
177
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185







+
+
+







 * @brief Asyncronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:target:selector:, this method does
 * not call the method when less than the specified length has been read -
 * instead, it waits until it got exactly the specified length, the stream has
 * ended or an exception occurred.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param target The target on which the selector should be called when the
 *		 data has been received. If the method returns true, it will be
 *		 called again with the same buffer and exact length when more
193
194
195
196
197
198
199



200
201
202
203
204
205
206
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217







+
+
+







 *	  buffer.
 *
 * On network streams, this might read less than the specified number of bytes.
 * If you want to read exactly the specified number of bytes, use
 * @ref asyncReadIntoBuffer:exactLength:block:. Note that a read can even
 * return 0 bytes - this does not necessarily mean that the stream ended, so
 * you still need to check @ref isAtEndOfStream.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param buffer The buffer into which the data is read.
 *		 The buffer must not be free'd before the async read completed!
 * @param length The length of the data that should be read at most.
 *		 The buffer *must* be *at least* this big!
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again with the same
216
217
218
219
220
221
222



223
224
225
226
227
228
229
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243







+
+
+







 * @brief Asyncronously reads exactly the specified length bytes from the
 *	  stream into a buffer.
 *
 * Unlike @ref asyncReadIntoBuffer:length:block:, this method does not invoke
 * the block when less than the specified length has been read - instead, it
 * waits until it got exactly the specified length, the stream has ended or an
 * exception occurred.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param buffer The buffer into which the data is read
 * @param length The length of the data that should be read.
 *		 The buffer *must* be *at least* this big!
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again with the same
 *		buffer and exact length when more data has been received. If
582
583
584
585
586
587
588



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604



605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623



624
625
626
627
628
629
630
631
632
633
634
635



636
637
638
639
640
641
642
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668







+
+
+
















+
+
+



















+
+
+












+
+
+







 */
- (OFString*)readLineWithEncoding: (of_string_encoding_t)encoding;

#ifdef OF_HAVE_SOCKETS
/*!
 * @brief Asyncronously reads until a newline, \\0, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param target The target on which to call the selector when the data has
 *		 been received. If the method returns true, it will be called
 *		 again when the next line has been received. If you want the
 *		 next method in the queue to handle the next line, you need to
 *		 return false from the method
 * @param selector The selector to call on the target. The signature must be
 *		   bool (OFStream *stream, OFString *line,
 *		   OFException *exception).
 */
- (void)asyncReadLineWithTarget: (id)target
		       selector: (SEL)selector;

/*!
 * @brief Asyncronously reads with the specified encoding until a newline, \\0,
 *	  end of stream or an exception occurs.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param target The target on which to call the selector when the data has
 *		 been received. If the method returns true, it will be called
 *		 again when the next line has been received. If you want the
 *		 next method in the queue to handle the next line, you need to
 *		 return false from the method
 * @param selector The selector to call on the target. The signature must be
 *		   bool (OFStream *stream, OFString *line,
 *		   OFException *exception).
 */
- (void)asyncReadLineWithEncoding: (of_string_encoding_t)encoding
			   target: (id)target
			 selector: (SEL)selector;

# ifdef OF_HAVE_BLOCKS
/*!
 * @brief Asyncronously reads until a newline, \\0, end of stream or an
 *	  exception occurs.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.
 */
- (void)asyncReadLineWithBlock: (of_stream_async_read_line_block_t)block;

/*!
 * @brief Asyncronously reads with the specified encoding until a newline, \\0,
 *	  end of stream or an exception occurs.
 *
 * @note The stream must implement @ref fileDescriptorForReading and return a
 *	 valid file descriptor in order for this to work!
 *
 * @param encoding The encoding used by the stream
 * @param block The block to call when the data has been received.
 *		If the block returns true, it will be called again when the next
 *		line has been received. If you want the next block in the queue
 *		to handle the next line, you need to return false from the
 *		block.