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