]> git.saurik.com Git - apple/cf.git/blob - CFConcreteStreams.c
CF-476.15.tar.gz
[apple/cf.git] / CFConcreteStreams.c
1 /*
2 * Copyright (c) 2008 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFConcreteStreams.c
24 Copyright 2000-2002, Apple, Inc. All rights reserved.
25 Responsibility: Becky Willrich
26 */
27
28 #define _DARWIN_UNLIMITED_SELECT 1
29
30 #include "CFStreamInternal.h"
31 #include "CFInternal.h"
32 #include "CFPriv.h"
33 #include <CoreFoundation/CFNumber.h>
34 #include <sys/types.h>
35 #include <stdlib.h>
36 #include <fcntl.h>
37 #include <string.h>
38 #include <stdio.h>
39 #if DEPLOYMENT_TARGET_MACOSX
40 #include <sys/time.h>
41 #include <unistd.h>
42 #endif
43
44 // On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32
45 // files and sockets are not interchangeable, and we do cheapo scheduling, where the file is
46 // always readable and writable until we hit EOF (similar to the way CFData streams are scheduled).
47 #if DEPLOYMENT_TARGET_MACOSX
48 #define REAL_FILE_SCHEDULING (1)
49 #endif
50
51 #define SCHEDULE_AFTER_WRITE (0)
52 #define SCHEDULE_AFTER_READ (1)
53 #define APPEND (3)
54 #define AT_EOF (4)
55 #define USE_RUNLOOP_ARRAY (5)
56
57
58 /* File callbacks */
59 typedef struct {
60 CFURLRef url;
61 int fd;
62 #ifdef REAL_FILE_SCHEDULING
63 union {
64 CFSocketRef sock; // socket created once we open and have an fd
65 CFMutableArrayRef rlArray; // scheduling information prior to open
66 } rlInfo; // If fd > 0, sock exists. Otherwise, rlArray.
67 #else
68 uint16_t scheduled; // ref count of how many times we've been scheduled
69 #endif
70 CFOptionFlags flags;
71 off_t offset;
72 } _CFFileStreamContext;
73
74
75 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset");
76
77
78 #ifdef REAL_FILE_SCHEDULING
79 static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
80
81 static void constructCFSocket(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) {
82 CFSocketContext context = {0, stream, NULL, NULL, CFCopyDescription};
83 CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator(stream), fileStream->fd, forRead ? kCFSocketReadCallBack : kCFSocketWriteCallBack, fileCallBack, &context);
84 CFSocketSetSocketFlags(sock, 0);
85 if (fileStream->rlInfo.rlArray) {
86 CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray);
87 CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(stream), sock, 0);
88 for (i = 0; i+1 < c; i += 2) {
89 CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i);
90 CFStringRef mode = CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1);
91 CFRunLoopAddSource(rl, src, mode);
92 }
93 CFRelease(fileStream->rlInfo.rlArray);
94 CFRelease(src);
95 }
96 fileStream->rlInfo.sock = sock;
97 }
98 #endif
99
100 static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) {
101 UInt8 path[1024];
102 int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY);
103
104 if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, path, 1024) == FALSE) {
105 error->error = ENOENT;
106 error->domain = kCFStreamErrorDomainPOSIX;
107 return FALSE;
108 }
109 if (__CFBitIsSet(fileStream->flags, APPEND)) {
110 flags |= O_APPEND;
111 if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther)) flags &= ~O_TRUNC;
112 }
113
114 do {
115 fileStream->fd = open((const char *)path, flags, 0666);
116
117 if (fileStream->fd < 0)
118 break;
119
120 if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1))
121 break;
122
123 #ifdef REAL_FILE_SCHEDULING
124 if (fileStream->rlInfo.rlArray != NULL) {
125 constructCFSocket(fileStream, forRead, stream);
126 }
127 #endif
128
129 return TRUE;
130 } while (1);
131
132 __CFBitSet(fileStream->flags, USE_RUNLOOP_ARRAY);
133 error->error = errno;
134 error->domain = kCFStreamErrorDomainPOSIX;
135
136 return FALSE;
137 }
138
139 static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
140 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
141 Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
142 *openComplete = TRUE;
143 if (ctxt->url) {
144 if (constructFD(ctxt, errorCode, forRead, stream)) {
145 #ifndef REAL_FILE_SCHEDULING
146 if (ctxt->scheduled > 0) {
147 if (forRead)
148 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
149 else
150 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
151 }
152 #endif
153 return TRUE;
154 } else {
155 return FALSE;
156 }
157 #ifdef REAL_FILE_SCHEDULING
158 } else if (ctxt->rlInfo.rlArray != NULL) {
159 constructCFSocket(ctxt, forRead, stream);
160 #endif
161 }
162 return TRUE;
163 }
164
165 __private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) {
166 CFIndex bytesRead = read(fd, buffer, bufferLength);
167 if (bytesRead < 0) {
168 errorCode->error = errno;
169 errorCode->domain = kCFStreamErrorDomainPOSIX;
170 return -1;
171 } else {
172 *atEOF = (bytesRead == 0) ? TRUE : FALSE;
173 errorCode->error = 0;
174 return bytesRead;
175 }
176 }
177
178 static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) {
179 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
180 CFIndex result;
181 result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF);
182 #ifdef REAL_FILE_SCHEDULING
183 if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) {
184 __CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ);
185 if (ctxt->rlInfo.sock) {
186 CFSocketEnableCallBacks(ctxt->rlInfo.sock, kCFSocketReadCallBack);
187 }
188 }
189 #else
190 if (*atEOF)
191 __CFBitSet(ctxt->flags, AT_EOF);
192 if (ctxt->scheduled > 0 && !*atEOF) {
193 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
194 }
195 #endif
196 return result;
197 }
198
199 #ifdef REAL_FILE_SCHEDULING
200 __private_extern__ Boolean fdCanRead(int fd) {
201 struct timeval timeout = {0, 0};
202 fd_set *readSetPtr;
203 fd_set readSet;
204 Boolean result;
205 // fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant
206 if (fd < FD_SETSIZE) {
207 FD_ZERO(&readSet);
208 readSetPtr = &readSet;
209 } else {
210 int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t);
211 uint32_t *fds_bits = (uint32_t *)malloc(size);
212 memset(fds_bits, 0, size);
213 readSetPtr = (fd_set *)fds_bits;
214 }
215 FD_SET(fd, readSetPtr);
216 result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE;
217 if (readSetPtr != &readSet) {
218 free(readSetPtr);
219 }
220 return result;
221 }
222 #endif
223
224 static Boolean fileCanRead(CFReadStreamRef stream, void *info) {
225 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
226 #ifdef REAL_FILE_SCHEDULING
227 return fdCanRead(ctxt->fd);
228 #else
229 return !__CFBitIsSet(ctxt->flags, AT_EOF);
230 #endif
231 }
232
233 __private_extern__ CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) {
234 CFIndex bytesWritten = write(fd, buffer, bufferLength);
235 if (bytesWritten < 0) {
236 errorCode->error = errno;
237 errorCode->domain = kCFStreamErrorDomainPOSIX;
238 return -1;
239 } else {
240 errorCode->error = 0;
241 return bytesWritten;
242 }
243 }
244
245 static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) {
246 _CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info);
247 CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode);
248 #ifdef REAL_FILE_SCHEDULING
249 if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) {
250 __CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE);
251 if (fileStream->rlInfo.sock) {
252 CFSocketEnableCallBacks(fileStream->rlInfo.sock, kCFSocketWriteCallBack);
253 }
254 }
255 #else
256 if (fileStream->scheduled > 0) {
257 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
258 }
259 #endif
260 return result;
261 }
262
263 #ifdef REAL_FILE_SCHEDULING
264 __private_extern__ Boolean fdCanWrite(int fd) {
265 struct timeval timeout = {0, 0};
266 fd_set *writeSetPtr;
267 fd_set writeSet;
268 Boolean result;
269 if (fd < FD_SETSIZE) {
270 FD_ZERO(&writeSet);
271 writeSetPtr = &writeSet;
272 } else {
273 int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t);
274 uint32_t *fds_bits = (uint32_t *)malloc(size);
275 memset(fds_bits, 0, size);
276 writeSetPtr = (fd_set *)fds_bits;
277 }
278 FD_SET(fd, writeSetPtr);
279 result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE;
280 if (writeSetPtr != &writeSet) {
281 free(writeSetPtr);
282 }
283 return result;
284 }
285 #endif
286
287 static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) {
288 #ifdef REAL_FILE_SCHEDULING
289 return fdCanWrite(((_CFFileStreamContext *)info)->fd);
290 #else
291 return TRUE;
292 #endif
293 }
294
295 static void fileClose(struct _CFStream *stream, void *info) {
296 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
297 if (ctxt->fd >= 0) {
298 close(ctxt->fd);
299 ctxt->fd = -1;
300 #ifdef REAL_FILE_SCHEDULING
301 if (ctxt->rlInfo.sock) {
302 CFSocketInvalidate(ctxt->rlInfo.sock);
303 CFRelease(ctxt->rlInfo.sock);
304 ctxt->rlInfo.sock = NULL;
305 }
306 } else if (ctxt->rlInfo.rlArray) {
307 CFRelease(ctxt->rlInfo.rlArray);
308 ctxt->rlInfo.rlArray = NULL;
309 #endif
310 }
311 }
312
313 #ifdef REAL_FILE_SCHEDULING
314 static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
315 struct _CFStream *stream = (struct _CFStream *)info;
316 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
317 _CFFileStreamContext *fileStream = isReadStream ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream);
318 if (type == kCFSocketWriteCallBack) {
319 __CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE);
320 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
321 } else {
322 // type == kCFSocketReadCallBack
323 __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ);
324 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
325 }
326 }
327 #endif
328
329 static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
330 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
331 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
332 CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream);
333 if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) {
334 // Stream's already closed or error-ed out
335 return;
336 }
337 #ifdef REAL_FILE_SCHEDULING
338 if (status == kCFStreamStatusNotOpen) {
339 if (!fileStream->rlInfo.rlArray) {
340 fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks);
341 }
342 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop);
343 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode);
344 } else {
345 CFRunLoopSourceRef rlSrc;
346 if (!fileStream->rlInfo.sock) {
347 constructCFSocket(fileStream, isReadStream, stream);
348 }
349 rlSrc = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
350 CFRunLoopAddSource(runLoop, rlSrc, runLoopMode);
351 CFRelease(rlSrc);
352 }
353 #else
354 fileStream->scheduled++;
355 if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) {
356 if (isReadStream)
357 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
358 else
359 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
360 }
361 #endif
362 }
363
364 static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
365 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
366 #ifdef REAL_FILE_SCHEDULING
367 Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
368 CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream);
369 if (status == kCFStreamStatusNotOpen) {
370 // Not opened yet
371 if (fileStream->rlInfo.rlArray) {
372 CFMutableArrayRef runloops = fileStream->rlInfo.rlArray;
373 CFIndex i, c;
374 for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) {
375 if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) {
376 CFArrayRemoveValueAtIndex(runloops, i);
377 CFArrayRemoveValueAtIndex(runloops, i);
378 break;
379 }
380 }
381 }
382 } else if (fileStream->rlInfo.sock) {
383 if (__CFBitIsSet(fileStream->flags, USE_RUNLOOP_ARRAY)) {
384 // we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.sock
385 CFMutableArrayRef runloops = fileStream->rlInfo.rlArray;
386 CFIndex i, c;
387 for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) {
388 if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) {
389 CFArrayRemoveValueAtIndex(runloops, i);
390 CFArrayRemoveValueAtIndex(runloops, i);
391 break;
392 }
393 }
394 } else {
395 CFRunLoopSourceRef sockSource = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
396 CFRunLoopRemoveSource(runLoop, sockSource, runLoopMode);
397 CFRelease(sockSource);
398 }
399 }
400 #else
401 if (fileStream->scheduled > 0)
402 fileStream->scheduled--;
403 #endif
404 }
405
406 static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
407
408 CFTypeRef result = NULL;
409 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
410
411 if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) {
412
413 // NOTE that this does a lseek of 0 from the current location in
414 // order to populate the offset field which will then be used to
415 // create the resulting value.
416 if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) {
417 fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR);
418 }
419
420 if (fileStream->offset != -1) {
421 result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset));
422 }
423 }
424
425 return result;
426 }
427
428 static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) {
429
430 Boolean result = FALSE;
431 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
432
433 if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() &&
434 CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen)
435 {
436 if (val == kCFBooleanTrue) {
437 __CFBitSet(fileStream->flags, APPEND);
438 fileStream->offset = -1; // Can't offset and append on the stream
439 } else {
440 __CFBitClear(fileStream->flags, APPEND);
441 }
442 result = TRUE;
443 }
444
445 else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) {
446
447 if (!__CFBitIsSet(fileStream->flags, APPEND))
448 {
449 result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset));
450 }
451
452 if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) {
453 result = FALSE;
454 }
455 }
456
457 return result;
458 }
459
460 static void *fileCreate(struct _CFStream *stream, void *info) {
461 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
462 _CFFileStreamContext *newCtxt = (_CFFileStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0);
463 if (!newCtxt) return NULL;
464 newCtxt->url = ctxt->url;
465 if (newCtxt->url) {
466 CFRetain(newCtxt->url);
467 }
468 newCtxt->fd = ctxt->fd;
469 #ifdef REAL_FILE_SCHEDULING
470 newCtxt->rlInfo.sock = NULL;
471 #else
472 newCtxt->scheduled = 0;
473 #endif
474 newCtxt->flags = 0;
475 newCtxt->offset = -1;
476 return newCtxt;
477 }
478
479 static void fileFinalize(struct _CFStream *stream, void *info) {
480 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
481 if (ctxt->fd > 0) {
482 #ifdef REAL_FILE_SCHEDULING
483 if (ctxt->rlInfo.sock) {
484 CFSocketInvalidate(ctxt->rlInfo.sock);
485 CFRelease(ctxt->rlInfo.sock);
486 }
487 #endif
488 close(ctxt->fd);
489 #ifdef REAL_FILE_SCHEDULING
490 } else if (ctxt->rlInfo.rlArray) {
491 CFRelease(ctxt->rlInfo.rlArray);
492 #endif
493 }
494 if (ctxt->url) {
495 CFRelease(ctxt->url);
496 }
497 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
498 }
499
500 static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) {
501 // This needs work
502 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
503 if (ctxt->url) {
504 return CFCopyDescription(ctxt->url);
505 } else {
506 return CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("fd = %d"), ctxt->fd);
507 }
508 }
509
510 /* CFData stream callbacks */
511 typedef struct {
512 CFDataRef data; // Mutable if the stream was constructed writable
513 const UInt8 *loc; // Current location in the file
514 Boolean scheduled;
515 char _padding[3];
516 } _CFReadDataStreamContext;
517
518 #define BUF_SIZE 1024
519 typedef struct _CFStreamByteBuffer {
520 UInt8 *bytes;
521 CFIndex capacity, length;
522 struct _CFStreamByteBuffer *next;
523 } _CFStreamByteBuffer;
524
525 typedef struct {
526 _CFStreamByteBuffer *firstBuf, *currentBuf;
527 CFAllocatorRef bufferAllocator;
528 Boolean scheduled;
529 char _padding[3];
530 } _CFWriteDataStreamContext;
531
532 static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
533 _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info;
534 if (dataStream->scheduled) {
535 if (CFDataGetLength(dataStream->data) != 0) {
536 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
537 } else {
538 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
539 }
540 }
541 errorCode->error = 0;
542 *openComplete = TRUE;
543 return TRUE;
544 }
545
546 static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) {
547 _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info;
548 if (dataStream->scheduled == FALSE) {
549 dataStream->scheduled = TRUE;
550 if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen)
551 return;
552 if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) {
553 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
554 } else {
555 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
556 }
557 }
558 }
559
560 static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) {
561 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
562 const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data);
563 CFIndex length = CFDataGetLength(dataCtxt->data);
564 CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc;
565 if (bytesToCopy > bufferLength) {
566 bytesToCopy = bufferLength;
567 }
568 if (bytesToCopy < 0) {
569 bytesToCopy = 0;
570 }
571 if (bytesToCopy != 0) {
572 memmove(buffer, dataCtxt->loc, bytesToCopy);
573 dataCtxt->loc += bytesToCopy;
574 }
575 error->error = 0;
576 *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE;
577 if (dataCtxt->scheduled && !*atEOF) {
578 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
579 }
580 return bytesToCopy;
581 }
582
583 static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) {
584 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
585 const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data);
586 if (dataCtxt->loc - bytes > maxBytesToRead) {
587 *numBytesRead = maxBytesToRead;
588 *atEOF = FALSE;
589 } else {
590 *numBytesRead = dataCtxt->loc - bytes;
591 *atEOF = TRUE;
592 }
593 error->error = 0;
594 bytes = dataCtxt->loc;
595 dataCtxt->loc += *numBytesRead;
596 if (dataCtxt->scheduled && !*atEOF) {
597 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
598 }
599 return bytes;
600 }
601
602 static Boolean dataCanRead(CFReadStreamRef stream, void *info) {
603 _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
604 return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE;
605 }
606
607 static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
608 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
609 if (dataStream->scheduled) {
610 if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) {
611 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
612 } else {
613 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
614 }
615 }
616 errorCode->error = 0;
617 *openComplete = TRUE;
618 return TRUE;
619 }
620
621 static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) {
622 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
623 if (dataStream->scheduled == FALSE) {
624 dataStream->scheduled = TRUE;
625 if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen)
626 return;
627 if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) {
628 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
629 } else {
630 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
631 }
632 }
633 }
634
635 static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) {
636 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
637 CFIndex result;
638 CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length;
639 if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) {
640 errorCode->error = ENOMEM;
641 errorCode->domain = kCFStreamErrorDomainPOSIX;
642 return -1;
643 } else {
644 result = bufferLength;
645 while (bufferLength > 0) {
646 CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength;
647 if (freeSpace > 0) {
648 memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy);
649 buffer += amountToCopy;
650 bufferLength -= amountToCopy;
651 dataStream->currentBuf->length += amountToCopy;
652 }
653 if (bufferLength > 0) {
654 CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength;
655 _CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0);
656 newBuf->bytes = (UInt8 *)(newBuf + 1);
657 newBuf->capacity = bufSize;
658 newBuf->length = 0;
659 newBuf->next = NULL;
660 dataStream->currentBuf->next = newBuf;
661 dataStream->currentBuf = newBuf;
662 freeSpace = bufSize;
663 }
664 }
665 errorCode->error = 0;
666 }
667 if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) {
668 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
669 }
670 return result;
671 }
672
673 static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) {
674 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
675 if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE;
676 if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE;
677 return FALSE;
678 }
679
680 static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
681 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
682 CFIndex size = 0;
683 _CFStreamByteBuffer *buf;
684 CFAllocatorRef alloc;
685 UInt8 *bytes, *currByte;
686 if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL;
687 if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL;
688 alloc = dataStream->bufferAllocator;
689 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
690 size += buf->length;
691 }
692 if (size == 0) return NULL;
693 bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0);
694 currByte = bytes;
695 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
696 memmove(currByte, buf->bytes, buf->length);
697 currByte += buf->length;
698 }
699 return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc);
700 }
701
702 static void *readDataCreate(struct _CFStream *stream, void *info) {
703 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
704 _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0);
705 if (!newCtxt) return NULL;
706 newCtxt->data = (CFDataRef)CFRetain(ctxt->data);
707 newCtxt->loc = CFDataGetBytePtr(newCtxt->data);
708 newCtxt->scheduled = FALSE;
709 return (void *)newCtxt;
710 }
711
712 static void readDataFinalize(struct _CFStream *stream, void *info) {
713 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
714 CFRelease(ctxt->data);
715 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
716 }
717
718 static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) {
719 return CFCopyDescription(((_CFReadDataStreamContext *)info)->data);
720 }
721
722 static void *writeDataCreate(struct _CFStream *stream, void *info) {
723 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
724 _CFWriteDataStreamContext *newCtxt;
725 if (ctxt->bufferAllocator != kCFAllocatorNull) {
726 if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault();
727 CFRetain(ctxt->bufferAllocator);
728 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0);
729 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1);
730 newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1);
731 newCtxt->firstBuf->capacity = BUF_SIZE;
732 newCtxt->firstBuf->length = 0;
733 newCtxt->firstBuf->next = NULL;
734 newCtxt->currentBuf = newCtxt->firstBuf;
735 newCtxt->bufferAllocator = ctxt->bufferAllocator;
736 newCtxt->scheduled = FALSE;
737 } else {
738 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0);
739 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1);
740 newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes;
741 newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity;
742 newCtxt->firstBuf->length = 0;
743 newCtxt->firstBuf->next = NULL;
744 newCtxt->currentBuf = newCtxt->firstBuf;
745 newCtxt->bufferAllocator = kCFAllocatorNull;
746 newCtxt->scheduled = FALSE;
747 }
748 return (void *)newCtxt;
749 }
750
751 static void writeDataFinalize(struct _CFStream *stream, void *info) {
752 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
753 if (ctxt->bufferAllocator != kCFAllocatorNull) {
754 _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next;
755 while (buf != NULL) {
756 next = buf->next;
757 CFAllocatorDeallocate(ctxt->bufferAllocator, buf);
758 buf = next;
759 }
760 CFRelease(ctxt->bufferAllocator);
761 }
762 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
763 }
764
765 static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) {
766 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info);
767 }
768
769 static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule};
770
771 static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) {
772 _CFFileStreamContext fileContext;
773 CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL;
774 if (!scheme || !CFEqual(scheme, CFSTR("file"))) {
775 if (scheme) CFRelease(scheme);
776 return NULL;
777 }
778 CFRelease(scheme);
779 fileContext.url = fileURL;
780 fileContext.fd = -1;
781 return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading);
782 }
783
784 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
785 return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE);
786 }
787
788 CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
789 return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE);
790 }
791
792 CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
793 _CFFileStreamContext fileContext;
794 fileContext.url = NULL;
795 fileContext.fd = fd;
796 return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE);
797 }
798
799 CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
800 _CFFileStreamContext fileContext;
801 fileContext.url = NULL;
802 fileContext.fd = fd;
803 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE);
804 }
805
806
807
808 static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL};
809 static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL};
810
811 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) {
812 _CFReadDataStreamContext ctxt;
813 CFReadStreamRef result;
814 ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator);
815 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
816 CFRelease(ctxt.data);
817 return result;
818 }
819
820 /* This needs to be exported to make it callable from Foundation. */
821 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) {
822 _CFReadDataStreamContext ctxt;
823 CFReadStreamRef result = NULL;
824
825 ctxt.data = CFRetain(data);
826 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
827 CFRelease(data);
828 return result;
829 }
830
831 CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) {
832 _CFStreamByteBuffer buf;
833 _CFWriteDataStreamContext ctxt;
834 buf.bytes = buffer;
835 buf.capacity = bufferCapacity;
836 buf.length = 0;
837 buf.next = NULL;
838 ctxt.firstBuf = &buf;
839 ctxt.currentBuf = ctxt.firstBuf;
840 ctxt.bufferAllocator = kCFAllocatorNull;
841 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
842 }
843
844 CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) {
845 _CFWriteDataStreamContext ctxt;
846 ctxt.firstBuf = NULL;
847 ctxt.currentBuf = NULL;
848 ctxt.bufferAllocator = bufferAllocator;
849 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
850 }
851
852 #undef BUF_SIZE
853