2 * Copyright (c) 2015 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-2014, 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 || DEPLOYMENT_TARGET_EMBEDDED_MINI || DEPLOYMENT_TARGET_LINUX
45 #define SCHEDULE_AFTER_WRITE (0)
46 #define SCHEDULE_AFTER_READ (1)
49 #define USE_RUNLOOP_ARRAY (5)
56 #ifdef REAL_FILE_SCHEDULING
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.
62 uint16_t scheduled
; // ref count of how many times we've been scheduled
66 } _CFFileStreamContext
;
69 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset
, "kCFStreamPropertyFileCurrentOffset");
70 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
71 CONST_STRING_DECL(_kCFStreamPropertyFileNativeHandle
, "_kCFStreamPropertyFileNativeHandle");
74 #ifdef REAL_FILE_SCHEDULING
75 extern void _CFFileDescriptorInduceFakeReadCallBack(CFFileDescriptorRef
);
76 static void fileCallBack(CFFileDescriptorRef f
, CFOptionFlags callBackTypes
, void *info
);
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
);
90 CFRelease(fileStream
->rlInfo
.rlArray
);
93 fileStream
->rlInfo
.cffd
= cffd
;
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
)
108 error
->error
= ENOENT
;
109 error
->domain
= kCFStreamErrorDomainPOSIX
;
112 if (__CFBitIsSet(fileStream
->flags
, APPEND
)) {
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);
123 if (fileStream
->fd
< 0)
126 if ((fileStream
->offset
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1))
129 #ifdef REAL_FILE_SCHEDULING
130 if (fileStream
->rlInfo
.rlArray
!= NULL
) {
131 constructCFFD(fileStream
, forRead
, stream
);
138 __CFBitSet(fileStream
->flags
, USE_RUNLOOP_ARRAY
);
139 error
->error
= errno
;
140 error
->domain
= kCFStreamErrorDomainPOSIX
;
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
;
150 if (constructFD(ctxt
, errorCode
, forRead
, stream
)) {
151 #ifndef REAL_FILE_SCHEDULING
152 if (ctxt
->scheduled
> 0) {
154 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
156 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
163 #ifdef REAL_FILE_SCHEDULING
164 } else if (ctxt
->rlInfo
.rlArray
!= NULL
) {
165 constructCFFD(ctxt
, forRead
, stream
);
171 CF_PRIVATE CFIndex
fdRead(int fd
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
) {
172 CFIndex bytesRead
= read(fd
, buffer
, bufferLength
);
174 errorCode
->error
= errno
;
175 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
178 *atEOF
= (bytesRead
== 0) ? TRUE
: FALSE
;
179 errorCode
->error
= 0;
184 static CFIndex
fileRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
, void *info
) {
185 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
) {
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
);
201 if (ctxt
->rlInfo
.cffd
) {
202 CFFileDescriptorEnableCallBacks(ctxt
->rlInfo
.cffd
, kCFFileDescriptorReadCallBack
);
207 __CFBitSet(ctxt
->flags
, AT_EOF
);
208 if (ctxt
->scheduled
> 0 && !*atEOF
) {
209 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
215 #ifdef REAL_FILE_SCHEDULING
216 CF_PRIVATE Boolean
fdCanRead(int fd
) {
217 struct timeval timeout
= {0, 0};
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
) {
224 readSetPtr
= &readSet
;
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
;
231 FD_SET(fd
, readSetPtr
);
232 result
= (select(fd
+ 1, readSetPtr
, NULL
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
233 if (readSetPtr
!= &readSet
) {
240 static Boolean
fileCanRead(CFReadStreamRef stream
, void *info
) {
241 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
242 #ifdef REAL_FILE_SCHEDULING
243 return fdCanRead(ctxt
->fd
);
245 return !__CFBitIsSet(ctxt
->flags
, AT_EOF
);
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
;
256 errorCode
->error
= 0;
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
);
272 if (fileStream
->scheduled
> 0) {
273 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
279 #ifdef REAL_FILE_SCHEDULING
280 CF_PRIVATE Boolean
fdCanWrite(int fd
) {
281 struct timeval timeout
= {0, 0};
285 if (fd
< FD_SETSIZE
) {
287 writeSetPtr
= &writeSet
;
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
;
294 FD_SET(fd
, writeSetPtr
);
295 result
= (select(fd
+ 1, NULL
, writeSetPtr
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
296 if (writeSetPtr
!= &writeSet
) {
303 static Boolean
fileCanWrite(CFWriteStreamRef stream
, void *info
) {
304 #ifdef REAL_FILE_SCHEDULING
305 return fdCanWrite(((_CFFileStreamContext
*)info
)->fd
);
311 static void fileClose(struct _CFStream
*stream
, void *info
) {
312 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
;
322 } else if (ctxt
->rlInfo
.rlArray
) {
323 CFRelease(ctxt
->rlInfo
.rlArray
);
324 ctxt
->rlInfo
.rlArray
= NULL
;
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
);
338 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_READ
);
339 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
352 #ifdef REAL_FILE_SCHEDULING
353 if (status
== kCFStreamStatusNotOpen
) {
354 if (!fileStream
->rlInfo
.rlArray
) {
355 fileStream
->rlInfo
.rlArray
= CFArrayCreateMutable(CFGetAllocator(stream
), 0, &kCFTypeArrayCallBacks
);
357 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoop
);
358 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoopMode
);
360 CFRunLoopSourceRef rlSrc
;
361 if (!fileStream
->rlInfo
.cffd
) {
362 constructCFFD(fileStream
, isReadStream
, stream
);
364 rlSrc
= CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.cffd
, 0);
365 CFRunLoopAddSource(runLoop
, rlSrc
, runLoopMode
);
369 fileStream
->scheduled
++;
370 if (fileStream
->scheduled
== 1 && fileStream
->fd
> 0 && status
== kCFStreamStatusOpen
) {
372 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
374 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
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
) {
386 if (fileStream
->rlInfo
.rlArray
) {
387 CFMutableArrayRef runloops
= fileStream
->rlInfo
.rlArray
;
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
);
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
;
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
);
410 CFRunLoopSourceRef rlSrc
= CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.cffd
, 0);
411 CFRunLoopRemoveSource(runLoop
, rlSrc
, runLoopMode
);
416 if (fileStream
->scheduled
> 0)
417 fileStream
->scheduled
--;
421 static CFTypeRef
fileCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
423 CFTypeRef result
= NULL
;
424 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
426 if (CFEqual(propertyName
, kCFStreamPropertyFileCurrentOffset
)) {
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
);
435 if (fileStream
->offset
!= -1) {
436 result
= CFNumberCreate(CFGetAllocator((CFTypeRef
)stream
), kCFNumberSInt64Type
, &(fileStream
->offset
));
438 #if DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
439 } else if (CFEqual(propertyName
, _kCFStreamPropertyFileNativeHandle
)) {
440 int fd
= fileStream
->fd
;
442 result
= CFDataCreate(CFGetAllocator((CFTypeRef
) stream
), (const uint8_t *)&fd
, sizeof(fd
));
450 static Boolean
fileSetProperty(struct _CFStream
*stream
, CFStringRef prop
, CFTypeRef val
, void *info
) {
452 Boolean result
= FALSE
;
453 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
455 if (CFEqual(prop
, kCFStreamPropertyAppendToFile
) && CFGetTypeID(stream
) == CFWriteStreamGetTypeID() &&
456 CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) == kCFStreamStatusNotOpen
)
458 if (val
== kCFBooleanTrue
) {
459 __CFBitSet(fileStream
->flags
, APPEND
);
460 fileStream
->offset
= -1; // Can't offset and append on the stream
462 __CFBitClear(fileStream
->flags
, APPEND
);
467 else if (CFEqual(prop
, kCFStreamPropertyFileCurrentOffset
)) {
469 if (!__CFBitIsSet(fileStream
->flags
, APPEND
))
471 result
= CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt64Type
, &(fileStream
->offset
));
474 if ((fileStream
->fd
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1)) {
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
;
488 CFRetain(newCtxt
->url
);
490 newCtxt
->fd
= ctxt
->fd
;
491 #ifdef REAL_FILE_SCHEDULING
492 newCtxt
->rlInfo
.cffd
= NULL
;
494 newCtxt
->scheduled
= 0;
497 newCtxt
->offset
= -1;
501 static void fileFinalize(struct _CFStream
*stream
, void *info
) {
502 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
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
;
512 #ifdef REAL_FILE_SCHEDULING
513 } else if (ctxt
->rlInfo
.rlArray
) {
514 CFRelease(ctxt
->rlInfo
.rlArray
);
518 CFRelease(ctxt
->url
);
520 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
523 static CFStringRef
fileCopyDescription(struct _CFStream
*stream
, void *info
) {
525 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
527 return CFCopyDescription(ctxt
->url
);
529 return CFStringCreateWithFormat(CFGetAllocator(stream
), NULL
, CFSTR("fd = %d"), ctxt
->fd
);
533 /* CFData stream callbacks */
535 CFDataRef data
; // Mutable if the stream was constructed writable
536 const UInt8
*loc
; // Current location in the file
539 } _CFReadDataStreamContext
;
541 #define BUF_SIZE 1024
542 typedef struct _CFStreamByteBuffer
{
544 CFIndex capacity
, length
;
545 struct _CFStreamByteBuffer
*next
;
546 } _CFStreamByteBuffer
;
549 _CFStreamByteBuffer
*firstBuf
, *currentBuf
;
550 CFAllocatorRef bufferAllocator
;
553 } _CFWriteDataStreamContext
;
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
);
561 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
564 errorCode
->error
= 0;
565 *openComplete
= TRUE
;
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
)
575 if (CFDataGetBytePtr(dataStream
->data
) + CFDataGetLength(dataStream
->data
) > dataStream
->loc
) {
576 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
578 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
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
;
591 if (bytesToCopy
< 0) {
594 if (bytesToCopy
!= 0) {
595 memmove(buffer
, dataCtxt
->loc
, bytesToCopy
);
596 dataCtxt
->loc
+= bytesToCopy
;
599 *atEOF
= (dataCtxt
->loc
< bytePtr
+ length
) ? FALSE
: TRUE
;
600 if (dataCtxt
->scheduled
&& !*atEOF
) {
601 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
;
613 *numBytesRead
= dataCtxt
->loc
- bytes
;
617 bytes
= dataCtxt
->loc
;
618 dataCtxt
->loc
+= *numBytesRead
;
619 if (dataCtxt
->scheduled
&& !*atEOF
) {
620 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
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
;
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
);
636 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
639 errorCode
->error
= 0;
640 *openComplete
= TRUE
;
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
)
650 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
651 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
653 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
658 static CFIndex
dataWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
659 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
661 CFIndex freeSpace
= dataStream
->currentBuf
->capacity
- dataStream
->currentBuf
->length
;
662 if (dataStream
->bufferAllocator
== kCFAllocatorNull
&& bufferLength
> freeSpace
) {
663 errorCode
->error
= ENOMEM
;
664 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
667 result
= bufferLength
;
668 while (bufferLength
> 0) {
669 CFIndex amountToCopy
= (bufferLength
> freeSpace
) ? freeSpace
: bufferLength
;
671 memmove(dataStream
->currentBuf
->bytes
+ dataStream
->currentBuf
->length
, buffer
, amountToCopy
);
672 buffer
+= amountToCopy
;
673 bufferLength
-= amountToCopy
;
674 dataStream
->currentBuf
->length
+= amountToCopy
;
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
;
684 newBuf
->bytes
= (UInt8
*)(newBuf
+ 1);
685 newBuf
->capacity
= bufSize
;
688 dataStream
->currentBuf
->next
= newBuf
;
689 dataStream
->currentBuf
= newBuf
;
694 errorCode
->error
= 0;
696 if (dataStream
->scheduled
&& (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
)) {
697 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
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
;
709 static CFPropertyListRef
dataCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
710 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
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
) {
721 bytes
= (UInt8
*)CFAllocatorAllocate(alloc
, size
, 0);
723 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
724 memmove(currByte
, buf
->bytes
, buf
->length
);
725 currByte
+= buf
->length
;
727 return CFDataCreateWithBytesNoCopy(alloc
, bytes
, size
, alloc
);
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
;
740 static void readDataFinalize(struct _CFStream
*stream
, void *info
) {
741 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
742 CFRelease(ctxt
->data
);
743 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
746 static CFStringRef
readDataCopyDescription(struct _CFStream
*stream
, void *info
) {
747 return CFCopyDescription(((_CFReadDataStreamContext
*)info
)->data
);
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
;
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
;
776 return (void *)newCtxt
;
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
) {
785 CFAllocatorDeallocate(ctxt
->bufferAllocator
, buf
);
788 CFRelease(ctxt
->bufferAllocator
);
790 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
793 static CFStringRef
writeDataCopyDescription(struct _CFStream
*stream
, void *info
) {
794 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFWriteDataContext %p>"), info
);
797 static const struct _CFStreamCallBacksV1 fileCallBacks
= {1, fileCreate
, fileFinalize
, fileCopyDescription
, fileOpen
, NULL
, fileRead
, NULL
, fileCanRead
, fileWrite
, fileCanWrite
, fileClose
, fileCopyProperty
, fileSetProperty
, NULL
, fileSchedule
, fileUnschedule
};
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
);
807 fileContext
.url
= fileURL
;
809 return _CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), forReading
);
812 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
813 return (CFReadStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, TRUE
);
816 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
817 return (CFWriteStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, FALSE
);
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
;
825 return (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), TRUE
);
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
;
833 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), FALSE
);
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
};
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
);
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
;
855 ctxt
.data
= (CFDataRef
)CFRetain(data
);
856 result
= (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&readDataCallBacks
), TRUE
);
861 CFWriteStreamRef
CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc
, UInt8
*buffer
, CFIndex bufferCapacity
) {
862 _CFStreamByteBuffer buf
;
863 _CFWriteDataStreamContext ctxt
;
865 buf
.capacity
= bufferCapacity
;
868 ctxt
.firstBuf
= &buf
;
869 ctxt
.currentBuf
= ctxt
.firstBuf
;
870 ctxt
.bufferAllocator
= kCFAllocatorNull
;
871 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&writeDataCallBacks
), FALSE
);
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
);