2 * Copyright (c) 2008 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@
23 /* CFConcreteStreams.c
24 Copyright 2000-2002, Apple, Inc. All rights reserved.
25 Responsibility: Becky Willrich
28 #define _DARWIN_UNLIMITED_SELECT 1
30 #include "CFStreamInternal.h"
31 #include "CFInternal.h"
33 #include <CoreFoundation/CFNumber.h>
34 #include <sys/types.h>
39 #if DEPLOYMENT_TARGET_MACOSX
44 // On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32
45 // files and sockets are not interchangeable, and we do cheapo scheduling, where the file is
46 // always readable and writable until we hit EOF (similar to the way CFData streams are scheduled).
47 #if DEPLOYMENT_TARGET_MACOSX
48 #define REAL_FILE_SCHEDULING (1)
51 #define SCHEDULE_AFTER_WRITE (0)
52 #define SCHEDULE_AFTER_READ (1)
55 #define USE_RUNLOOP_ARRAY (5)
62 #ifdef REAL_FILE_SCHEDULING
64 CFSocketRef sock
; // socket created once we open and have an fd
65 CFMutableArrayRef rlArray
; // scheduling information prior to open
66 } rlInfo
; // If fd > 0, sock exists. Otherwise, rlArray.
68 uint16_t scheduled
; // ref count of how many times we've been scheduled
72 } _CFFileStreamContext
;
75 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset
, "kCFStreamPropertyFileCurrentOffset");
78 #ifdef REAL_FILE_SCHEDULING
79 static void fileCallBack(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
);
81 static void constructCFSocket(_CFFileStreamContext
*fileStream
, Boolean forRead
, struct _CFStream
*stream
) {
82 CFSocketContext context
= {0, stream
, NULL
, NULL
, CFCopyDescription
};
83 CFSocketRef sock
= CFSocketCreateWithNative(CFGetAllocator(stream
), fileStream
->fd
, forRead
? kCFSocketReadCallBack
: kCFSocketWriteCallBack
, fileCallBack
, &context
);
84 CFSocketSetSocketFlags(sock
, 0);
85 if (fileStream
->rlInfo
.rlArray
) {
86 CFIndex i
, c
= CFArrayGetCount(fileStream
->rlInfo
.rlArray
);
87 CFRunLoopSourceRef src
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), sock
, 0);
88 for (i
= 0; i
+1 < c
; i
+= 2) {
89 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(fileStream
->rlInfo
.rlArray
, i
);
90 CFStringRef mode
= CFArrayGetValueAtIndex(fileStream
->rlInfo
.rlArray
, i
+1);
91 CFRunLoopAddSource(rl
, src
, mode
);
93 CFRelease(fileStream
->rlInfo
.rlArray
);
96 fileStream
->rlInfo
.sock
= sock
;
100 static Boolean
constructFD(_CFFileStreamContext
*fileStream
, CFStreamError
*error
, Boolean forRead
, struct _CFStream
*stream
) {
102 int flags
= forRead
? O_RDONLY
: (O_CREAT
| O_TRUNC
| O_WRONLY
);
104 if (CFURLGetFileSystemRepresentation(fileStream
->url
, TRUE
, path
, 1024) == FALSE
) {
105 error
->error
= ENOENT
;
106 error
->domain
= kCFStreamErrorDomainPOSIX
;
109 if (__CFBitIsSet(fileStream
->flags
, APPEND
)) {
111 if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther
)) flags
&= ~O_TRUNC
;
115 fileStream
->fd
= open((const char *)path
, flags
, 0666);
117 if (fileStream
->fd
< 0)
120 if ((fileStream
->offset
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1))
123 #ifdef REAL_FILE_SCHEDULING
124 if (fileStream
->rlInfo
.rlArray
!= NULL
) {
125 constructCFSocket(fileStream
, forRead
, stream
);
132 __CFBitSet(fileStream
->flags
, USE_RUNLOOP_ARRAY
);
133 error
->error
= errno
;
134 error
->domain
= kCFStreamErrorDomainPOSIX
;
139 static Boolean
fileOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
140 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
141 Boolean forRead
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
142 *openComplete
= TRUE
;
144 if (constructFD(ctxt
, errorCode
, forRead
, stream
)) {
145 #ifndef REAL_FILE_SCHEDULING
146 if (ctxt
->scheduled
> 0) {
148 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
150 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
157 #ifdef REAL_FILE_SCHEDULING
158 } else if (ctxt
->rlInfo
.rlArray
!= NULL
) {
159 constructCFSocket(ctxt
, forRead
, stream
);
165 __private_extern__ CFIndex
fdRead(int fd
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
) {
166 CFIndex bytesRead
= read(fd
, buffer
, bufferLength
);
168 errorCode
->error
= errno
;
169 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
172 *atEOF
= (bytesRead
== 0) ? TRUE
: FALSE
;
173 errorCode
->error
= 0;
178 static CFIndex
fileRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
, void *info
) {
179 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
181 result
= fdRead(ctxt
->fd
, buffer
, bufferLength
, errorCode
, atEOF
);
182 #ifdef REAL_FILE_SCHEDULING
183 if (__CFBitIsSet(ctxt
->flags
, SCHEDULE_AFTER_READ
)) {
184 __CFBitClear(ctxt
->flags
, SCHEDULE_AFTER_READ
);
185 if (ctxt
->rlInfo
.sock
) {
186 CFSocketEnableCallBacks(ctxt
->rlInfo
.sock
, kCFSocketReadCallBack
);
191 __CFBitSet(ctxt
->flags
, AT_EOF
);
192 if (ctxt
->scheduled
> 0 && !*atEOF
) {
193 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
199 #ifdef REAL_FILE_SCHEDULING
200 __private_extern__ Boolean
fdCanRead(int fd
) {
201 struct timeval timeout
= {0, 0};
205 // fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant
206 if (fd
< FD_SETSIZE
) {
208 readSetPtr
= &readSet
;
210 int size
= howmany(fd
+1, NFDBITS
) * sizeof(uint32_t);
211 uint32_t *fds_bits
= (uint32_t *)malloc(size
);
212 memset(fds_bits
, 0, size
);
213 readSetPtr
= (fd_set
*)fds_bits
;
215 FD_SET(fd
, readSetPtr
);
216 result
= (select(fd
+ 1, readSetPtr
, NULL
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
217 if (readSetPtr
!= &readSet
) {
224 static Boolean
fileCanRead(CFReadStreamRef stream
, void *info
) {
225 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
226 #ifdef REAL_FILE_SCHEDULING
227 return fdCanRead(ctxt
->fd
);
229 return !__CFBitIsSet(ctxt
->flags
, AT_EOF
);
233 __private_extern__ CFIndex
fdWrite(int fd
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
) {
234 CFIndex bytesWritten
= write(fd
, buffer
, bufferLength
);
235 if (bytesWritten
< 0) {
236 errorCode
->error
= errno
;
237 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
240 errorCode
->error
= 0;
245 static CFIndex
fileWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
246 _CFFileStreamContext
*fileStream
= ((_CFFileStreamContext
*)info
);
247 CFIndex result
= fdWrite(fileStream
->fd
, buffer
, bufferLength
, errorCode
);
248 #ifdef REAL_FILE_SCHEDULING
249 if (__CFBitIsSet(fileStream
->flags
, SCHEDULE_AFTER_WRITE
)) {
250 __CFBitClear(fileStream
->flags
, SCHEDULE_AFTER_WRITE
);
251 if (fileStream
->rlInfo
.sock
) {
252 CFSocketEnableCallBacks(fileStream
->rlInfo
.sock
, kCFSocketWriteCallBack
);
256 if (fileStream
->scheduled
> 0) {
257 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
263 #ifdef REAL_FILE_SCHEDULING
264 __private_extern__ Boolean
fdCanWrite(int fd
) {
265 struct timeval timeout
= {0, 0};
269 if (fd
< FD_SETSIZE
) {
271 writeSetPtr
= &writeSet
;
273 int size
= howmany(fd
+1, NFDBITS
) * sizeof(uint32_t);
274 uint32_t *fds_bits
= (uint32_t *)malloc(size
);
275 memset(fds_bits
, 0, size
);
276 writeSetPtr
= (fd_set
*)fds_bits
;
278 FD_SET(fd
, writeSetPtr
);
279 result
= (select(fd
+ 1, NULL
, writeSetPtr
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
280 if (writeSetPtr
!= &writeSet
) {
287 static Boolean
fileCanWrite(CFWriteStreamRef stream
, void *info
) {
288 #ifdef REAL_FILE_SCHEDULING
289 return fdCanWrite(((_CFFileStreamContext
*)info
)->fd
);
295 static void fileClose(struct _CFStream
*stream
, void *info
) {
296 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
300 #ifdef REAL_FILE_SCHEDULING
301 if (ctxt
->rlInfo
.sock
) {
302 CFSocketInvalidate(ctxt
->rlInfo
.sock
);
303 CFRelease(ctxt
->rlInfo
.sock
);
304 ctxt
->rlInfo
.sock
= NULL
;
306 } else if (ctxt
->rlInfo
.rlArray
) {
307 CFRelease(ctxt
->rlInfo
.rlArray
);
308 ctxt
->rlInfo
.rlArray
= NULL
;
313 #ifdef REAL_FILE_SCHEDULING
314 static void fileCallBack(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
) {
315 struct _CFStream
*stream
= (struct _CFStream
*)info
;
316 Boolean isReadStream
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
317 _CFFileStreamContext
*fileStream
= isReadStream
? CFReadStreamGetInfoPointer((CFReadStreamRef
)stream
) : CFWriteStreamGetInfoPointer((CFWriteStreamRef
)stream
);
318 if (type
== kCFSocketWriteCallBack
) {
319 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_WRITE
);
320 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
322 // type == kCFSocketReadCallBack
323 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_READ
);
324 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
329 static void fileSchedule(struct _CFStream
*stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void *info
) {
330 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
331 Boolean isReadStream
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
332 CFStreamStatus status
= isReadStream
? CFReadStreamGetStatus((CFReadStreamRef
)stream
) : CFWriteStreamGetStatus((CFWriteStreamRef
)stream
);
333 if (fileStream
->fd
< 0 && status
!= kCFStreamStatusNotOpen
) {
334 // Stream's already closed or error-ed out
337 #ifdef REAL_FILE_SCHEDULING
338 if (status
== kCFStreamStatusNotOpen
) {
339 if (!fileStream
->rlInfo
.rlArray
) {
340 fileStream
->rlInfo
.rlArray
= CFArrayCreateMutable(CFGetAllocator(stream
), 0, &kCFTypeArrayCallBacks
);
342 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoop
);
343 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoopMode
);
345 CFRunLoopSourceRef rlSrc
;
346 if (!fileStream
->rlInfo
.sock
) {
347 constructCFSocket(fileStream
, isReadStream
, stream
);
349 rlSrc
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.sock
, 0);
350 CFRunLoopAddSource(runLoop
, rlSrc
, runLoopMode
);
354 fileStream
->scheduled
++;
355 if (fileStream
->scheduled
== 1 && fileStream
->fd
> 0 && status
== kCFStreamStatusOpen
) {
357 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
359 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
364 static void fileUnschedule(struct _CFStream
*stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void *info
) {
365 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
366 #ifdef REAL_FILE_SCHEDULING
367 Boolean isReadStream
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
368 CFStreamStatus status
= isReadStream
? CFReadStreamGetStatus((CFReadStreamRef
)stream
) : CFWriteStreamGetStatus((CFWriteStreamRef
)stream
);
369 if (status
== kCFStreamStatusNotOpen
) {
371 if (fileStream
->rlInfo
.rlArray
) {
372 CFMutableArrayRef runloops
= fileStream
->rlInfo
.rlArray
;
374 for (i
= 0, c
= CFArrayGetCount(runloops
); i
+1 < c
; i
+= 2) {
375 if (CFEqual(CFArrayGetValueAtIndex(runloops
, i
), runLoop
) && CFEqual(CFArrayGetValueAtIndex(runloops
, i
+1), runLoopMode
)) {
376 CFArrayRemoveValueAtIndex(runloops
, i
);
377 CFArrayRemoveValueAtIndex(runloops
, i
);
382 } else if (fileStream
->rlInfo
.sock
) {
383 if (__CFBitIsSet(fileStream
->flags
, USE_RUNLOOP_ARRAY
)) {
384 // we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.sock
385 CFMutableArrayRef runloops
= fileStream
->rlInfo
.rlArray
;
387 for (i
= 0, c
= CFArrayGetCount(runloops
); i
+1 < c
; i
+= 2) {
388 if (CFEqual(CFArrayGetValueAtIndex(runloops
, i
), runLoop
) && CFEqual(CFArrayGetValueAtIndex(runloops
, i
+1), runLoopMode
)) {
389 CFArrayRemoveValueAtIndex(runloops
, i
);
390 CFArrayRemoveValueAtIndex(runloops
, i
);
395 CFRunLoopSourceRef sockSource
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.sock
, 0);
396 CFRunLoopRemoveSource(runLoop
, sockSource
, runLoopMode
);
397 CFRelease(sockSource
);
401 if (fileStream
->scheduled
> 0)
402 fileStream
->scheduled
--;
406 static CFTypeRef
fileCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
408 CFTypeRef result
= NULL
;
409 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
411 if (CFEqual(propertyName
, kCFStreamPropertyFileCurrentOffset
)) {
413 // NOTE that this does a lseek of 0 from the current location in
414 // order to populate the offset field which will then be used to
415 // create the resulting value.
416 if (!__CFBitIsSet(fileStream
->flags
, APPEND
) && fileStream
->fd
!= -1) {
417 fileStream
->offset
= lseek(fileStream
->fd
, 0, SEEK_CUR
);
420 if (fileStream
->offset
!= -1) {
421 result
= CFNumberCreate(CFGetAllocator((CFTypeRef
)stream
), kCFNumberSInt64Type
, &(fileStream
->offset
));
428 static Boolean
fileSetProperty(struct _CFStream
*stream
, CFStringRef prop
, CFTypeRef val
, void *info
) {
430 Boolean result
= FALSE
;
431 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
433 if (CFEqual(prop
, kCFStreamPropertyAppendToFile
) && CFGetTypeID(stream
) == CFWriteStreamGetTypeID() &&
434 CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) == kCFStreamStatusNotOpen
)
436 if (val
== kCFBooleanTrue
) {
437 __CFBitSet(fileStream
->flags
, APPEND
);
438 fileStream
->offset
= -1; // Can't offset and append on the stream
440 __CFBitClear(fileStream
->flags
, APPEND
);
445 else if (CFEqual(prop
, kCFStreamPropertyFileCurrentOffset
)) {
447 if (!__CFBitIsSet(fileStream
->flags
, APPEND
))
449 result
= CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt64Type
, &(fileStream
->offset
));
452 if ((fileStream
->fd
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1)) {
460 static void *fileCreate(struct _CFStream
*stream
, void *info
) {
461 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
462 _CFFileStreamContext
*newCtxt
= (_CFFileStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFFileStreamContext
), 0);
463 if (!newCtxt
) return NULL
;
464 newCtxt
->url
= ctxt
->url
;
466 CFRetain(newCtxt
->url
);
468 newCtxt
->fd
= ctxt
->fd
;
469 #ifdef REAL_FILE_SCHEDULING
470 newCtxt
->rlInfo
.sock
= NULL
;
472 newCtxt
->scheduled
= 0;
475 newCtxt
->offset
= -1;
479 static void fileFinalize(struct _CFStream
*stream
, void *info
) {
480 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
482 #ifdef REAL_FILE_SCHEDULING
483 if (ctxt
->rlInfo
.sock
) {
484 CFSocketInvalidate(ctxt
->rlInfo
.sock
);
485 CFRelease(ctxt
->rlInfo
.sock
);
489 #ifdef REAL_FILE_SCHEDULING
490 } else if (ctxt
->rlInfo
.rlArray
) {
491 CFRelease(ctxt
->rlInfo
.rlArray
);
495 CFRelease(ctxt
->url
);
497 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
500 static CFStringRef
fileCopyDescription(struct _CFStream
*stream
, void *info
) {
502 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
504 return CFCopyDescription(ctxt
->url
);
506 return CFStringCreateWithFormat(CFGetAllocator(stream
), NULL
, CFSTR("fd = %d"), ctxt
->fd
);
510 /* CFData stream callbacks */
512 CFDataRef data
; // Mutable if the stream was constructed writable
513 const UInt8
*loc
; // Current location in the file
516 } _CFReadDataStreamContext
;
518 #define BUF_SIZE 1024
519 typedef struct _CFStreamByteBuffer
{
521 CFIndex capacity
, length
;
522 struct _CFStreamByteBuffer
*next
;
523 } _CFStreamByteBuffer
;
526 _CFStreamByteBuffer
*firstBuf
, *currentBuf
;
527 CFAllocatorRef bufferAllocator
;
530 } _CFWriteDataStreamContext
;
532 static Boolean
readDataOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
533 _CFReadDataStreamContext
*dataStream
= (_CFReadDataStreamContext
*)info
;
534 if (dataStream
->scheduled
) {
535 if (CFDataGetLength(dataStream
->data
) != 0) {
536 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
538 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
541 errorCode
->error
= 0;
542 *openComplete
= TRUE
;
546 static void readDataSchedule(struct _CFStream
*stream
, CFRunLoopRef rl
, CFStringRef rlMode
, void *info
) {
547 _CFReadDataStreamContext
*dataStream
= (_CFReadDataStreamContext
*)info
;
548 if (dataStream
->scheduled
== FALSE
) {
549 dataStream
->scheduled
= TRUE
;
550 if (CFReadStreamGetStatus((CFReadStreamRef
)stream
) != kCFStreamStatusOpen
)
552 if (CFDataGetBytePtr(dataStream
->data
) + CFDataGetLength(dataStream
->data
) > dataStream
->loc
) {
553 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
555 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
560 static CFIndex
dataRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*error
, Boolean
*atEOF
, void *info
) {
561 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
562 const UInt8
*bytePtr
= CFDataGetBytePtr(dataCtxt
->data
);
563 CFIndex length
= CFDataGetLength(dataCtxt
->data
);
564 CFIndex bytesToCopy
= bytePtr
+ length
- dataCtxt
->loc
;
565 if (bytesToCopy
> bufferLength
) {
566 bytesToCopy
= bufferLength
;
568 if (bytesToCopy
< 0) {
571 if (bytesToCopy
!= 0) {
572 memmove(buffer
, dataCtxt
->loc
, bytesToCopy
);
573 dataCtxt
->loc
+= bytesToCopy
;
576 *atEOF
= (dataCtxt
->loc
< bytePtr
+ length
) ? FALSE
: TRUE
;
577 if (dataCtxt
->scheduled
&& !*atEOF
) {
578 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
583 static const UInt8
*dataGetBuffer(CFReadStreamRef stream
, CFIndex maxBytesToRead
, CFIndex
*numBytesRead
, CFStreamError
*error
, Boolean
*atEOF
, void *info
) {
584 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
585 const UInt8
*bytes
= CFDataGetBytePtr(dataCtxt
->data
);
586 if (dataCtxt
->loc
- bytes
> maxBytesToRead
) {
587 *numBytesRead
= maxBytesToRead
;
590 *numBytesRead
= dataCtxt
->loc
- bytes
;
594 bytes
= dataCtxt
->loc
;
595 dataCtxt
->loc
+= *numBytesRead
;
596 if (dataCtxt
->scheduled
&& !*atEOF
) {
597 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
602 static Boolean
dataCanRead(CFReadStreamRef stream
, void *info
) {
603 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
604 return (CFDataGetBytePtr(dataCtxt
->data
) + CFDataGetLength(dataCtxt
->data
) > dataCtxt
->loc
) ? TRUE
: FALSE
;
607 static Boolean
writeDataOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
608 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
609 if (dataStream
->scheduled
) {
610 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
611 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
613 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
616 errorCode
->error
= 0;
617 *openComplete
= TRUE
;
621 static void writeDataSchedule(struct _CFStream
*stream
, CFRunLoopRef rl
, CFStringRef rlMode
, void *info
) {
622 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
623 if (dataStream
->scheduled
== FALSE
) {
624 dataStream
->scheduled
= TRUE
;
625 if (CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) != kCFStreamStatusOpen
)
627 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
628 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
630 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
635 static CFIndex
dataWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
636 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
638 CFIndex freeSpace
= dataStream
->currentBuf
->capacity
- dataStream
->currentBuf
->length
;
639 if (dataStream
->bufferAllocator
== kCFAllocatorNull
&& bufferLength
> freeSpace
) {
640 errorCode
->error
= ENOMEM
;
641 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
644 result
= bufferLength
;
645 while (bufferLength
> 0) {
646 CFIndex amountToCopy
= (bufferLength
> freeSpace
) ? freeSpace
: bufferLength
;
648 memmove(dataStream
->currentBuf
->bytes
+ dataStream
->currentBuf
->length
, buffer
, amountToCopy
);
649 buffer
+= amountToCopy
;
650 bufferLength
-= amountToCopy
;
651 dataStream
->currentBuf
->length
+= amountToCopy
;
653 if (bufferLength
> 0) {
654 CFIndex bufSize
= BUF_SIZE
> bufferLength
? BUF_SIZE
: bufferLength
;
655 _CFStreamByteBuffer
*newBuf
= (_CFStreamByteBuffer
*)CFAllocatorAllocate(dataStream
->bufferAllocator
, sizeof(_CFStreamByteBuffer
) + bufSize
, 0);
656 newBuf
->bytes
= (UInt8
*)(newBuf
+ 1);
657 newBuf
->capacity
= bufSize
;
660 dataStream
->currentBuf
->next
= newBuf
;
661 dataStream
->currentBuf
= newBuf
;
665 errorCode
->error
= 0;
667 if (dataStream
->scheduled
&& (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
)) {
668 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
673 static Boolean
dataCanWrite(CFWriteStreamRef stream
, void *info
) {
674 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
675 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
) return TRUE
;
676 if (dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) return TRUE
;
680 static CFPropertyListRef
dataCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
681 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
683 _CFStreamByteBuffer
*buf
;
684 CFAllocatorRef alloc
;
685 UInt8
*bytes
, *currByte
;
686 if (!CFEqual(propertyName
, kCFStreamPropertyDataWritten
)) return NULL
;
687 if (dataStream
->bufferAllocator
== kCFAllocatorNull
) return NULL
;
688 alloc
= dataStream
->bufferAllocator
;
689 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
692 if (size
== 0) return NULL
;
693 bytes
= (UInt8
*)CFAllocatorAllocate(alloc
, size
, 0);
695 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
696 memmove(currByte
, buf
->bytes
, buf
->length
);
697 currByte
+= buf
->length
;
699 return CFDataCreateWithBytesNoCopy(alloc
, bytes
, size
, alloc
);
702 static void *readDataCreate(struct _CFStream
*stream
, void *info
) {
703 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
704 _CFReadDataStreamContext
*newCtxt
= (_CFReadDataStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFReadDataStreamContext
), 0);
705 if (!newCtxt
) return NULL
;
706 newCtxt
->data
= (CFDataRef
)CFRetain(ctxt
->data
);
707 newCtxt
->loc
= CFDataGetBytePtr(newCtxt
->data
);
708 newCtxt
->scheduled
= FALSE
;
709 return (void *)newCtxt
;
712 static void readDataFinalize(struct _CFStream
*stream
, void *info
) {
713 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
714 CFRelease(ctxt
->data
);
715 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
718 static CFStringRef
readDataCopyDescription(struct _CFStream
*stream
, void *info
) {
719 return CFCopyDescription(((_CFReadDataStreamContext
*)info
)->data
);
722 static void *writeDataCreate(struct _CFStream
*stream
, void *info
) {
723 _CFWriteDataStreamContext
*ctxt
= (_CFWriteDataStreamContext
*)info
;
724 _CFWriteDataStreamContext
*newCtxt
;
725 if (ctxt
->bufferAllocator
!= kCFAllocatorNull
) {
726 if (ctxt
->bufferAllocator
== NULL
) ctxt
->bufferAllocator
= CFAllocatorGetDefault();
727 CFRetain(ctxt
->bufferAllocator
);
728 newCtxt
= (_CFWriteDataStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFWriteDataStreamContext
) + sizeof(_CFStreamByteBuffer
) + BUF_SIZE
, 0);
729 newCtxt
->firstBuf
= (_CFStreamByteBuffer
*)(newCtxt
+ 1);
730 newCtxt
->firstBuf
->bytes
= (UInt8
*)(newCtxt
->firstBuf
+ 1);
731 newCtxt
->firstBuf
->capacity
= BUF_SIZE
;
732 newCtxt
->firstBuf
->length
= 0;
733 newCtxt
->firstBuf
->next
= NULL
;
734 newCtxt
->currentBuf
= newCtxt
->firstBuf
;
735 newCtxt
->bufferAllocator
= ctxt
->bufferAllocator
;
736 newCtxt
->scheduled
= FALSE
;
738 newCtxt
= (_CFWriteDataStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFWriteDataStreamContext
) + sizeof(_CFStreamByteBuffer
), 0);
739 newCtxt
->firstBuf
= (_CFStreamByteBuffer
*)(newCtxt
+1);
740 newCtxt
->firstBuf
->bytes
= ctxt
->firstBuf
->bytes
;
741 newCtxt
->firstBuf
->capacity
= ctxt
->firstBuf
->capacity
;
742 newCtxt
->firstBuf
->length
= 0;
743 newCtxt
->firstBuf
->next
= NULL
;
744 newCtxt
->currentBuf
= newCtxt
->firstBuf
;
745 newCtxt
->bufferAllocator
= kCFAllocatorNull
;
746 newCtxt
->scheduled
= FALSE
;
748 return (void *)newCtxt
;
751 static void writeDataFinalize(struct _CFStream
*stream
, void *info
) {
752 _CFWriteDataStreamContext
*ctxt
= (_CFWriteDataStreamContext
*)info
;
753 if (ctxt
->bufferAllocator
!= kCFAllocatorNull
) {
754 _CFStreamByteBuffer
*buf
= ctxt
->firstBuf
->next
, *next
;
755 while (buf
!= NULL
) {
757 CFAllocatorDeallocate(ctxt
->bufferAllocator
, buf
);
760 CFRelease(ctxt
->bufferAllocator
);
762 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
765 static CFStringRef
writeDataCopyDescription(struct _CFStream
*stream
, void *info
) {
766 return CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFWriteDataContext %p>"), info
);
769 static const struct _CFStreamCallBacksV1 fileCallBacks
= {1, fileCreate
, fileFinalize
, fileCopyDescription
, fileOpen
, NULL
, fileRead
, NULL
, fileCanRead
, fileWrite
, fileCanWrite
, fileClose
, fileCopyProperty
, fileSetProperty
, NULL
, fileSchedule
, fileUnschedule
};
771 static struct _CFStream
*_CFStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
, Boolean forReading
) {
772 _CFFileStreamContext fileContext
;
773 CFStringRef scheme
= fileURL
? CFURLCopyScheme(fileURL
) : NULL
;
774 if (!scheme
|| !CFEqual(scheme
, CFSTR("file"))) {
775 if (scheme
) CFRelease(scheme
);
779 fileContext
.url
= fileURL
;
781 return _CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), forReading
);
784 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
785 return (CFReadStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, TRUE
);
788 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
789 return (CFWriteStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, FALSE
);
792 CFReadStreamRef
_CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc
, int fd
) {
793 _CFFileStreamContext fileContext
;
794 fileContext
.url
= NULL
;
796 return (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), TRUE
);
799 CFWriteStreamRef
_CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc
, int fd
) {
800 _CFFileStreamContext fileContext
;
801 fileContext
.url
= NULL
;
803 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, (struct _CFStreamCallBacks
*)(&fileCallBacks
), FALSE
);
808 static const struct _CFStreamCallBacksV1 readDataCallBacks
= {1, readDataCreate
, readDataFinalize
, readDataCopyDescription
, readDataOpen
, NULL
, dataRead
, dataGetBuffer
, dataCanRead
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, readDataSchedule
, NULL
};
809 static const struct _CFStreamCallBacksV1 writeDataCallBacks
= {1, writeDataCreate
, writeDataFinalize
, writeDataCopyDescription
, writeDataOpen
, NULL
, NULL
, NULL
, NULL
, dataWrite
, dataCanWrite
, NULL
, dataCopyProperty
, NULL
, NULL
, writeDataSchedule
, NULL
};
811 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex length
, CFAllocatorRef bytesDeallocator
) {
812 _CFReadDataStreamContext ctxt
;
813 CFReadStreamRef result
;
814 ctxt
.data
= CFDataCreateWithBytesNoCopy(alloc
, bytes
, length
, bytesDeallocator
);
815 result
= (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&readDataCallBacks
), TRUE
);
816 CFRelease(ctxt
.data
);
820 /* This needs to be exported to make it callable from Foundation. */
821 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithData(CFAllocatorRef alloc
, CFDataRef data
) {
822 _CFReadDataStreamContext ctxt
;
823 CFReadStreamRef result
= NULL
;
825 ctxt
.data
= CFRetain(data
);
826 result
= (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&readDataCallBacks
), TRUE
);
831 CFWriteStreamRef
CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc
, UInt8
*buffer
, CFIndex bufferCapacity
) {
832 _CFStreamByteBuffer buf
;
833 _CFWriteDataStreamContext ctxt
;
835 buf
.capacity
= bufferCapacity
;
838 ctxt
.firstBuf
= &buf
;
839 ctxt
.currentBuf
= ctxt
.firstBuf
;
840 ctxt
.bufferAllocator
= kCFAllocatorNull
;
841 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&writeDataCallBacks
), FALSE
);
844 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc
, CFAllocatorRef bufferAllocator
) {
845 _CFWriteDataStreamContext ctxt
;
846 ctxt
.firstBuf
= NULL
;
847 ctxt
.currentBuf
= NULL
;
848 ctxt
.bufferAllocator
= bufferAllocator
;
849 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, (struct _CFStreamCallBacks
*)(&writeDataCallBacks
), FALSE
);