2 * Copyright (c) 2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 /* CFConcreteStreams.c
25 Copyright (c) 2000-2011, Apple Inc. All rights reserved.
26 Responsibility: John Iarocci
29 #include "CFStreamInternal.h"
30 #include "CFInternal.h"
31 #include <CoreFoundation/CFPriv.h>
32 #include <CoreFoundation/CFNumber.h>
33 #include <sys/types.h>
39 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED
42 #elif DEPLOYMENT_TARGET_WINDOWS
44 #error Unknown or unspecified DEPLOYMENT_TARGET
48 #define SCHEDULE_AFTER_WRITE (0)
49 #define SCHEDULE_AFTER_READ (1)
52 #define USE_RUNLOOP_ARRAY (5)
59 #ifdef REAL_FILE_SCHEDULING
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.
65 uint16_t scheduled
; // ref count of how many times we've been scheduled
69 } _CFFileStreamContext
;
72 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset
, "kCFStreamPropertyFileCurrentOffset");
75 #ifdef REAL_FILE_SCHEDULING
76 extern void _CFFileDescriptorInduceFakeReadCallBack(CFFileDescriptorRef
);
77 static void fileCallBack(CFFileDescriptorRef f
, CFOptionFlags callBackTypes
, void *info
);
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
);
91 CFRelease(fileStream
->rlInfo
.rlArray
);
94 fileStream
->rlInfo
.cffd
= cffd
;
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
)
108 #error Unknown or unspecified DEPLOYMENT_TARGET
111 error
->error
= ENOENT
;
112 error
->domain
= kCFStreamErrorDomainPOSIX
;
115 if (__CFBitIsSet(fileStream
->flags
, APPEND
)) {
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);
126 if (fileStream
->fd
< 0)
129 if ((fileStream
->offset
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1))
132 #ifdef REAL_FILE_SCHEDULING
133 if (fileStream
->rlInfo
.rlArray
!= NULL
) {
134 constructCFFD(fileStream
, forRead
, stream
);
141 __CFBitSet(fileStream
->flags
, USE_RUNLOOP_ARRAY
);
142 error
->error
= errno
;
143 error
->domain
= kCFStreamErrorDomainPOSIX
;
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
;
153 if (constructFD(ctxt
, errorCode
, forRead
, stream
)) {
154 #ifndef REAL_FILE_SCHEDULING
155 if (ctxt
->scheduled
> 0) {
157 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
159 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
166 #ifdef REAL_FILE_SCHEDULING
167 } else if (ctxt
->rlInfo
.rlArray
!= NULL
) {
168 constructCFFD(ctxt
, forRead
, stream
);
174 __private_extern__ CFIndex
fdRead(int fd
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
) {
175 CFIndex bytesRead
= read(fd
, buffer
, bufferLength
);
177 errorCode
->error
= errno
;
178 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
181 *atEOF
= (bytesRead
== 0) ? TRUE
: FALSE
;
182 errorCode
->error
= 0;
187 static CFIndex
fileRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
, void *info
) {
188 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
) {
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
);
204 if (ctxt
->rlInfo
.cffd
) {
205 CFFileDescriptorEnableCallBacks(ctxt
->rlInfo
.cffd
, kCFFileDescriptorReadCallBack
);
210 __CFBitSet(ctxt
->flags
, AT_EOF
);
211 if (ctxt
->scheduled
> 0 && !*atEOF
) {
212 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
218 #ifdef REAL_FILE_SCHEDULING
219 __private_extern__ Boolean
fdCanRead(int fd
) {
220 struct timeval timeout
= {0, 0};
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
) {
227 readSetPtr
= &readSet
;
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
;
234 FD_SET(fd
, readSetPtr
);
235 result
= (select(fd
+ 1, readSetPtr
, NULL
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
236 if (readSetPtr
!= &readSet
) {
243 static Boolean
fileCanRead(CFReadStreamRef stream
, void *info
) {
244 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
245 #ifdef REAL_FILE_SCHEDULING
246 return fdCanRead(ctxt
->fd
);
248 return !__CFBitIsSet(ctxt
->flags
, AT_EOF
);
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
;
259 errorCode
->error
= 0;
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
);
275 if (fileStream
->scheduled
> 0) {
276 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
282 #ifdef REAL_FILE_SCHEDULING
283 __private_extern__ Boolean
fdCanWrite(int fd
) {
284 struct timeval timeout
= {0, 0};
288 if (fd
< FD_SETSIZE
) {
290 writeSetPtr
= &writeSet
;
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
;
297 FD_SET(fd
, writeSetPtr
);
298 result
= (select(fd
+ 1, NULL
, writeSetPtr
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
299 if (writeSetPtr
!= &writeSet
) {
306 static Boolean
fileCanWrite(CFWriteStreamRef stream
, void *info
) {
307 #ifdef REAL_FILE_SCHEDULING
308 return fdCanWrite(((_CFFileStreamContext
*)info
)->fd
);
314 static void fileClose(struct _CFStream
*stream
, void *info
) {
315 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
;
325 } else if (ctxt
->rlInfo
.rlArray
) {
326 CFRelease(ctxt
->rlInfo
.rlArray
);
327 ctxt
->rlInfo
.rlArray
= NULL
;
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
);
341 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_READ
);
342 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
355 #ifdef REAL_FILE_SCHEDULING
356 if (status
== kCFStreamStatusNotOpen
) {
357 if (!fileStream
->rlInfo
.rlArray
) {
358 fileStream
->rlInfo
.rlArray
= CFArrayCreateMutable(CFGetAllocator(stream
), 0, &kCFTypeArrayCallBacks
);
360 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoop
);
361 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoopMode
);
363 CFRunLoopSourceRef rlSrc
;
364 if (!fileStream
->rlInfo
.cffd
) {
365 constructCFFD(fileStream
, isReadStream
, stream
);
367 rlSrc
= CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.cffd
, 0);
368 CFRunLoopAddSource(runLoop
, rlSrc
, runLoopMode
);
372 fileStream
->scheduled
++;
373 if (fileStream
->scheduled
== 1 && fileStream
->fd
> 0 && status
== kCFStreamStatusOpen
) {
375 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
377 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
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
) {
389 if (fileStream
->rlInfo
.rlArray
) {
390 CFMutableArrayRef runloops
= fileStream
->rlInfo
.rlArray
;
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
);
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
;
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
);
413 CFRunLoopSourceRef rlSrc
= CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.cffd
, 0);
414 CFRunLoopRemoveSource(runLoop
, rlSrc
, runLoopMode
);
419 if (fileStream
->scheduled
> 0)
420 fileStream
->scheduled
--;
424 static CFTypeRef
fileCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
426 CFTypeRef result
= NULL
;
427 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
429 if (CFEqual(propertyName
, kCFStreamPropertyFileCurrentOffset
)) {
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
);
438 if (fileStream
->offset
!= -1) {
439 result
= CFNumberCreate(CFGetAllocator((CFTypeRef
)stream
), kCFNumberSInt64Type
, &(fileStream
->offset
));
446 static Boolean
fileSetProperty(struct _CFStream
*stream
, CFStringRef prop
, CFTypeRef val
, void *info
) {
448 Boolean result
= FALSE
;
449 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
451 if (CFEqual(prop
, kCFStreamPropertyAppendToFile
) && CFGetTypeID(stream
) == CFWriteStreamGetTypeID() &&
452 CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) == kCFStreamStatusNotOpen
)
454 if (val
== kCFBooleanTrue
) {
455 __CFBitSet(fileStream
->flags
, APPEND
);
456 fileStream
->offset
= -1; // Can't offset and append on the stream
458 __CFBitClear(fileStream
->flags
, APPEND
);
463 else if (CFEqual(prop
, kCFStreamPropertyFileCurrentOffset
)) {
465 if (!__CFBitIsSet(fileStream
->flags
, APPEND
))
467 result
= CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt64Type
, &(fileStream
->offset
));
470 if ((fileStream
->fd
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1)) {
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
;
484 CFRetain(newCtxt
->url
);
486 newCtxt
->fd
= ctxt
->fd
;
487 #ifdef REAL_FILE_SCHEDULING
488 newCtxt
->rlInfo
.cffd
= NULL
;
490 newCtxt
->scheduled
= 0;
493 newCtxt
->offset
= -1;
497 static void fileFinalize(struct _CFStream
*stream
, void *info
) {
498 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
;
508 #ifdef REAL_FILE_SCHEDULING
509 } else if (ctxt
->rlInfo
.rlArray
) {
510 CFRelease(ctxt
->rlInfo
.rlArray
);
514 CFRelease(ctxt
->url
);
516 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
519 static CFStringRef
fileCopyDescription(struct _CFStream
*stream
, void *info
) {
521 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
523 return CFCopyDescription(ctxt
->url
);
525 return CFStringCreateWithFormat(CFGetAllocator(stream
), NULL
, CFSTR("fd = %d"), ctxt
->fd
);
529 /* CFData stream callbacks */
531 CFDataRef data
; // Mutable if the stream was constructed writable
532 const UInt8
*loc
; // Current location in the file
535 } _CFReadDataStreamContext
;
537 #define BUF_SIZE 1024
538 typedef struct _CFStreamByteBuffer
{
540 CFIndex capacity
, length
;
541 struct _CFStreamByteBuffer
*next
;
542 } _CFStreamByteBuffer
;
545 _CFStreamByteBuffer
*firstBuf
, *currentBuf
;
546 CFAllocatorRef bufferAllocator
;
549 } _CFWriteDataStreamContext
;
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
);
557 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
560 errorCode
->error
= 0;
561 *openComplete
= TRUE
;
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
)
571 if (CFDataGetBytePtr(dataStream
->data
) + CFDataGetLength(dataStream
->data
) > dataStream
->loc
) {
572 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
574 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
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
;
587 if (bytesToCopy
< 0) {
590 if (bytesToCopy
!= 0) {
591 memmove(buffer
, dataCtxt
->loc
, bytesToCopy
);
592 dataCtxt
->loc
+= bytesToCopy
;
595 *atEOF
= (dataCtxt
->loc
< bytePtr
+ length
) ? FALSE
: TRUE
;
596 if (dataCtxt
->scheduled
&& !*atEOF
) {
597 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
;
609 *numBytesRead
= dataCtxt
->loc
- bytes
;
613 bytes
= dataCtxt
->loc
;
614 dataCtxt
->loc
+= *numBytesRead
;
615 if (dataCtxt
->scheduled
&& !*atEOF
) {
616 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
;
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
);
632 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
635 errorCode
->error
= 0;
636 *openComplete
= TRUE
;
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
)
646 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
647 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
649 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
654 static CFIndex
dataWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
655 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
657 CFIndex freeSpace
= dataStream
->currentBuf
->capacity
- dataStream
->currentBuf
->length
;
658 if (dataStream
->bufferAllocator
== kCFAllocatorNull
&& bufferLength
> freeSpace
) {
659 errorCode
->error
= ENOMEM
;
660 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
663 result
= bufferLength
;
664 while (bufferLength
> 0) {
665 CFIndex amountToCopy
= (bufferLength
> freeSpace
) ? freeSpace
: bufferLength
;
667 memmove(dataStream
->currentBuf
->bytes
+ dataStream
->currentBuf
->length
, buffer
, amountToCopy
);
668 buffer
+= amountToCopy
;
669 bufferLength
-= amountToCopy
;
670 dataStream
->currentBuf
->length
+= amountToCopy
;
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
;
679 dataStream
->currentBuf
->next
= newBuf
;
680 dataStream
->currentBuf
= newBuf
;
684 errorCode
->error
= 0;
686 if (dataStream
->scheduled
&& (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
)) {
687 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
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
;
699 static CFPropertyListRef
dataCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
700 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
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
) {
711 if (size
== 0) return NULL
;
712 bytes
= (UInt8
*)CFAllocatorAllocate(alloc
, size
, 0);
714 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
715 memmove(currByte
, buf
->bytes
, buf
->length
);
716 currByte
+= buf
->length
;
718 return CFDataCreateWithBytesNoCopy(alloc
, bytes
, size
, alloc
);
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
;
731 static void readDataFinalize(struct _CFStream
*stream
, void *info
) {
732 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
733 CFRelease(ctxt
->data
);
734 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
737 static CFStringRef
readDataCopyDescription(struct _CFStream
*stream
, void *info
) {
738 return CFCopyDescription(((_CFReadDataStreamContext
*)info
)->data
);
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
;
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
;
767 return (void *)newCtxt
;
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
) {
776 CFAllocatorDeallocate(ctxt
->bufferAllocator
, buf
);
779 CFRelease(ctxt
->bufferAllocator
);
781 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
784 static CFStringRef
writeDataCopyDescription(struct _CFStream
*stream
, void *info
) {
785 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFWriteDataContext %p>"), info
);
788 static const struct _CFStreamCallBacksV1 fileCallBacks
= {1, fileCreate
, fileFinalize
, fileCopyDescription
, fileOpen
, NULL
, fileRead
, NULL
, fileCanRead
, fileWrite
, fileCanWrite
, fileClose
, fileCopyProperty
, fileSetProperty
, NULL
, fileSchedule
, fileUnschedule
};
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
);
798 fileContext
.url
= fileURL
;
800 return _CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), forReading
);
803 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
804 return (CFReadStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, TRUE
);
807 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
808 return (CFWriteStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, FALSE
);
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
;
816 return (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), TRUE
);
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
;
824 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), FALSE
);
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
};
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
);
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
;
846 ctxt
.data
= (CFDataRef
)CFRetain(data
);
847 result
= (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&readDataCallBacks
), TRUE
);
852 CFWriteStreamRef
CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc
, UInt8
*buffer
, CFIndex bufferCapacity
) {
853 _CFStreamByteBuffer buf
;
854 _CFWriteDataStreamContext ctxt
;
856 buf
.capacity
= bufferCapacity
;
859 ctxt
.firstBuf
= &buf
;
860 ctxt
.currentBuf
= ctxt
.firstBuf
;
861 ctxt
.bufferAllocator
= kCFAllocatorNull
;
862 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&writeDataCallBacks
), FALSE
);
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
);
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
);