]> git.saurik.com Git - apple/cf.git/blob - CFConcreteStreams.c
CF-1152.14.tar.gz
[apple/cf.git] / CFConcreteStreams.c
1 /*
2 * Copyright (c) 2015 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-2014, 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 CF_PRIVATE 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 CF_PRIVATE 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 CF_PRIVATE 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 CF_PRIVATE 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 if (newBuf == NULL) {
680 errorCode->error = ENOMEM;
681 errorCode->domain = kCFStreamErrorDomainPOSIX;
682 return -1;
683 } else {
684 newBuf->bytes = (UInt8 *)(newBuf + 1);
685 newBuf->capacity = bufSize;
686 newBuf->length = 0;
687 newBuf->next = NULL;
688 dataStream->currentBuf->next = newBuf;
689 dataStream->currentBuf = newBuf;
690 freeSpace = bufSize;
691 }
692 }
693 }
694 errorCode->error = 0;
695 }
696 if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) {
697 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
698 }
699 return result;
700 }
701
702 static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) {
703 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
704 if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE;
705 if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE;
706 return FALSE;
707 }
708
709 static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
710 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
711 CFIndex size = 0;
712 _CFStreamByteBuffer *buf;
713 CFAllocatorRef alloc;
714 UInt8 *bytes, *currByte;
715 if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL;
716 if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL;
717 alloc = dataStream->bufferAllocator;
718 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
719 size += buf->length;
720 }
721 bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0);
722 currByte = bytes;
723 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
724 memmove(currByte, buf->bytes, buf->length);
725 currByte += buf->length;
726 }
727 return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc);
728 }
729
730 static void *readDataCreate(struct _CFStream *stream, void *info) {
731 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
732 _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0);
733 if (!newCtxt) return NULL;
734 newCtxt->data = (CFDataRef)CFRetain(ctxt->data);
735 newCtxt->loc = CFDataGetBytePtr(newCtxt->data);
736 newCtxt->scheduled = FALSE;
737 return (void *)newCtxt;
738 }
739
740 static void readDataFinalize(struct _CFStream *stream, void *info) {
741 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
742 CFRelease(ctxt->data);
743 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
744 }
745
746 static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) {
747 return CFCopyDescription(((_CFReadDataStreamContext *)info)->data);
748 }
749
750 static void *writeDataCreate(struct _CFStream *stream, void *info) {
751 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
752 _CFWriteDataStreamContext *newCtxt;
753 if (ctxt->bufferAllocator != kCFAllocatorNull) {
754 if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault();
755 CFRetain(ctxt->bufferAllocator);
756 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0);
757 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1);
758 newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1);
759 newCtxt->firstBuf->capacity = BUF_SIZE;
760 newCtxt->firstBuf->length = 0;
761 newCtxt->firstBuf->next = NULL;
762 newCtxt->currentBuf = newCtxt->firstBuf;
763 newCtxt->bufferAllocator = ctxt->bufferAllocator;
764 newCtxt->scheduled = FALSE;
765 } else {
766 newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0);
767 newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1);
768 newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes;
769 newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity;
770 newCtxt->firstBuf->length = 0;
771 newCtxt->firstBuf->next = NULL;
772 newCtxt->currentBuf = newCtxt->firstBuf;
773 newCtxt->bufferAllocator = kCFAllocatorNull;
774 newCtxt->scheduled = FALSE;
775 }
776 return (void *)newCtxt;
777 }
778
779 static void writeDataFinalize(struct _CFStream *stream, void *info) {
780 _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
781 if (ctxt->bufferAllocator != kCFAllocatorNull) {
782 _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next;
783 while (buf != NULL) {
784 next = buf->next;
785 CFAllocatorDeallocate(ctxt->bufferAllocator, buf);
786 buf = next;
787 }
788 CFRelease(ctxt->bufferAllocator);
789 }
790 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
791 }
792
793 static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) {
794 return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info);
795 }
796
797 static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule};
798
799 static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) {
800 _CFFileStreamContext fileContext;
801 CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL;
802 if (!scheme || !CFEqual(scheme, CFSTR("file"))) {
803 if (scheme) CFRelease(scheme);
804 return NULL;
805 }
806 CFRelease(scheme);
807 fileContext.url = fileURL;
808 fileContext.fd = -1;
809 return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading);
810 }
811
812 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
813 return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE);
814 }
815
816 CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
817 return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE);
818 }
819
820 // CFReadStreamRef takes ownership of the fd, and will close() it
821 CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
822 _CFFileStreamContext fileContext;
823 fileContext.url = NULL;
824 fileContext.fd = fd;
825 return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE);
826 }
827
828 // CFWriteStreamRef takes ownership of the fd, and will close() it
829 CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
830 _CFFileStreamContext fileContext;
831 fileContext.url = NULL;
832 fileContext.fd = fd;
833 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE);
834 }
835
836
837
838 static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL};
839 static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL};
840
841 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) {
842 _CFReadDataStreamContext ctxt;
843 CFReadStreamRef result;
844 ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator);
845 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
846 CFRelease(ctxt.data);
847 return result;
848 }
849
850 /* This needs to be exported to make it callable from Foundation. */
851 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) {
852 _CFReadDataStreamContext ctxt;
853 CFReadStreamRef result = NULL;
854
855 ctxt.data = (CFDataRef)CFRetain(data);
856 result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
857 CFRelease(data);
858 return result;
859 }
860
861 CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) {
862 _CFStreamByteBuffer buf;
863 _CFWriteDataStreamContext ctxt;
864 buf.bytes = buffer;
865 buf.capacity = bufferCapacity;
866 buf.length = 0;
867 buf.next = NULL;
868 ctxt.firstBuf = &buf;
869 ctxt.currentBuf = ctxt.firstBuf;
870 ctxt.bufferAllocator = kCFAllocatorNull;
871 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
872 }
873
874 CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) {
875 _CFWriteDataStreamContext ctxt;
876 ctxt.firstBuf = NULL;
877 ctxt.currentBuf = NULL;
878 ctxt.bufferAllocator = bufferAllocator;
879 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
880 }
881
882 #undef BUF_SIZE
883