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