2 * Copyright (c) 2005 Apple Computer, 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
34 #include <CoreFoundation/CFNumber.h>
36 #include "CFStreamPriv.h"
37 #include "CFInternal.h"
38 #include "CFUtilitiesPriv.h"
40 // On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32
41 // files and sockets are not interchangeable, and we do cheapo scheduling, where the file is
42 // always readable and writable until we hit EOF (similar to the way CFData streams are scheduled).
43 #if !defined(__WIN32__)
44 #define REAL_FILE_SCHEDULING (1)
47 #define SCHEDULE_AFTER_WRITE (0)
48 #define SCHEDULE_AFTER_READ (1)
56 #ifdef REAL_FILE_SCHEDULING
58 CFSocketRef sock
; // socket created once we open and have an fd
59 CFMutableArrayRef rlArray
; // scheduling information prior to open
60 } rlInfo
; // If fd > 0, sock exists. Otherwise, rlArray.
62 uint16_t scheduled
; // ref count of how many times we've been scheduled
67 } _CFFileStreamContext
;
70 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset
, "kCFStreamPropertyFileCurrentOffset");
73 #ifdef REAL_FILE_SCHEDULING
74 static void fileCallBack(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
);
76 static void constructCFSocket(_CFFileStreamContext
*fileStream
, Boolean forRead
, struct _CFStream
*stream
) {
77 CFSocketContext context
= {0, stream
, NULL
, NULL
, CFCopyDescription
};
78 CFSocketRef sock
= CFSocketCreateWithNative(CFGetAllocator(stream
), fileStream
->fd
, forRead
? kCFSocketReadCallBack
: kCFSocketWriteCallBack
, fileCallBack
, &context
);
79 CFSocketSetSocketFlags(sock
, 0);
80 if (fileStream
->rlInfo
.rlArray
) {
81 CFIndex i
, c
= CFArrayGetCount(fileStream
->rlInfo
.rlArray
);
82 CFRunLoopSourceRef src
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), sock
, 0);
83 for (i
= 0; i
+1 < c
; i
+= 2) {
84 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(fileStream
->rlInfo
.rlArray
, i
);
85 CFStringRef mode
= CFArrayGetValueAtIndex(fileStream
->rlInfo
.rlArray
, i
+1);
86 CFRunLoopAddSource(rl
, src
, mode
);
88 CFRelease(fileStream
->rlInfo
.rlArray
);
91 fileStream
->rlInfo
.sock
= sock
;
95 static Boolean
constructFD(_CFFileStreamContext
*fileStream
, CFStreamError
*error
, Boolean forRead
, struct _CFStream
*stream
) {
97 int flags
= forRead
? O_RDONLY
: (O_CREAT
| O_TRUNC
| O_WRONLY
);
98 #if defined(__WIN32__)
99 flags
|= (_O_BINARY
|_O_NOINHERIT
);
102 __CFSetNastyFile(fileStream
->url
);
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(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 error
->error
= errno
;
133 error
->domain
= kCFStreamErrorDomainPOSIX
;
138 static Boolean
fileOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
139 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
141 // Open already occurred
142 errorCode
->error
= 0;
143 *openComplete
= TRUE
;
146 Boolean forRead
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
147 if (constructFD(ctxt
, errorCode
, forRead
, stream
)) {
148 *openComplete
= TRUE
;
149 #ifndef REAL_FILE_SCHEDULING
150 if (ctxt
->scheduled
> 0) {
152 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
154 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
163 __private_extern__ CFIndex
fdRead(int fd
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
) {
164 CFIndex bytesRead
= read(fd
, buffer
, bufferLength
);
166 errorCode
->error
= errno
;
167 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
170 *atEOF
= (bytesRead
== 0) ? TRUE
: FALSE
;
171 errorCode
->error
= 0;
176 static CFIndex
fileRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, Boolean
*atEOF
, void *info
) {
177 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
179 result
= fdRead(ctxt
->fd
, buffer
, bufferLength
, errorCode
, atEOF
);
180 #ifdef REAL_FILE_SCHEDULING
181 if (__CFBitIsSet(ctxt
->flags
, SCHEDULE_AFTER_READ
)) {
182 __CFBitClear(ctxt
->flags
, SCHEDULE_AFTER_READ
);
183 if (ctxt
->rlInfo
.sock
) {
184 CFSocketEnableCallBacks(ctxt
->rlInfo
.sock
, kCFSocketReadCallBack
);
189 __CFBitSet(ctxt
->flags
, AT_EOF
);
190 if (ctxt
->scheduled
> 0 && !*atEOF
) {
191 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
197 #ifdef REAL_FILE_SCHEDULING
198 __private_extern__ Boolean
fdCanRead(int fd
) {
199 struct timeval timeout
= {0, 0};
203 // fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant
204 if (fd
< FD_SETSIZE
) {
206 readSetPtr
= &readSet
;
208 int size
= howmany(fd
+1, NFDBITS
) * sizeof(uint32_t);
209 uint32_t *fds_bits
= (uint32_t *)malloc(size
);
210 memset(fds_bits
, 0, size
);
211 readSetPtr
= (fd_set
*)fds_bits
;
213 FD_SET(fd
, readSetPtr
);
214 result
= (select(fd
+ 1, readSetPtr
, NULL
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
215 if (readSetPtr
!= &readSet
) {
222 static Boolean
fileCanRead(CFReadStreamRef stream
, void *info
) {
223 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
224 #ifdef REAL_FILE_SCHEDULING
225 return fdCanRead(ctxt
->fd
);
227 return !__CFBitIsSet(ctxt
->flags
, AT_EOF
);
231 __private_extern__ CFIndex
fdWrite(int fd
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
) {
232 CFIndex bytesWritten
= write(fd
, buffer
, bufferLength
);
233 if (bytesWritten
< 0) {
234 errorCode
->error
= errno
;
235 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
238 errorCode
->error
= 0;
243 static CFIndex
fileWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
244 _CFFileStreamContext
*fileStream
= ((_CFFileStreamContext
*)info
);
245 CFIndex result
= fdWrite(fileStream
->fd
, buffer
, bufferLength
, errorCode
);
246 #ifdef REAL_FILE_SCHEDULING
247 if (__CFBitIsSet(fileStream
->flags
, SCHEDULE_AFTER_WRITE
)) {
248 __CFBitClear(fileStream
->flags
, SCHEDULE_AFTER_WRITE
);
249 if (fileStream
->rlInfo
.sock
) {
250 CFSocketEnableCallBacks(fileStream
->rlInfo
.sock
, kCFSocketWriteCallBack
);
254 if (fileStream
->scheduled
> 0) {
255 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
261 #ifdef REAL_FILE_SCHEDULING
262 __private_extern__ Boolean
fdCanWrite(int fd
) {
263 struct timeval timeout
= {0, 0};
267 if (fd
< FD_SETSIZE
) {
269 writeSetPtr
= &writeSet
;
271 int size
= howmany(fd
+1, NFDBITS
) * sizeof(uint32_t);
272 uint32_t *fds_bits
= (uint32_t *)malloc(size
);
273 memset(fds_bits
, 0, size
);
274 writeSetPtr
= (fd_set
*)fds_bits
;
276 FD_SET(fd
, writeSetPtr
);
277 result
= (select(fd
+ 1, NULL
, writeSetPtr
, NULL
, &timeout
) == 1) ? TRUE
: FALSE
;
278 if (writeSetPtr
!= &writeSet
) {
285 static Boolean
fileCanWrite(CFWriteStreamRef stream
, void *info
) {
286 #ifdef REAL_FILE_SCHEDULING
287 return fdCanWrite(((_CFFileStreamContext
*)info
)->fd
);
293 static void fileClose(struct _CFStream
*stream
, void *info
) {
294 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
298 #ifdef REAL_FILE_SCHEDULING
299 if (ctxt
->rlInfo
.sock
) {
300 CFSocketInvalidate(ctxt
->rlInfo
.sock
);
301 CFRelease(ctxt
->rlInfo
.sock
);
302 ctxt
->rlInfo
.sock
= NULL
;
304 } else if (ctxt
->rlInfo
.rlArray
) {
305 CFRelease(ctxt
->rlInfo
.rlArray
);
306 ctxt
->rlInfo
.rlArray
= NULL
;
311 #ifdef REAL_FILE_SCHEDULING
312 static void fileCallBack(CFSocketRef s
, CFSocketCallBackType type
, CFDataRef address
, const void *data
, void *info
) {
313 struct _CFStream
*stream
= (struct _CFStream
*)info
;
314 Boolean isReadStream
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
315 _CFFileStreamContext
*fileStream
= isReadStream
? CFReadStreamGetInfoPointer((CFReadStreamRef
)stream
) : CFWriteStreamGetInfoPointer((CFWriteStreamRef
)stream
);
316 if (type
== kCFSocketWriteCallBack
) {
317 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_WRITE
);
318 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
320 // type == kCFSocketReadCallBack
321 __CFBitSet(fileStream
->flags
, SCHEDULE_AFTER_READ
);
322 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
327 static void fileSchedule(struct _CFStream
*stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void *info
) {
328 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
329 Boolean isReadStream
= (CFGetTypeID(stream
) == CFReadStreamGetTypeID());
330 CFStreamStatus status
= isReadStream
? CFReadStreamGetStatus((CFReadStreamRef
)stream
) : CFWriteStreamGetStatus((CFWriteStreamRef
)stream
);
331 if (fileStream
->fd
< 0 && status
!= kCFStreamStatusNotOpen
) {
332 // Stream's already closed or error-ed out
335 #ifdef REAL_FILE_SCHEDULING
336 if (fileStream
->fd
< 0) {
337 if (!fileStream
->rlInfo
.rlArray
) {
338 fileStream
->rlInfo
.rlArray
= CFArrayCreateMutable(CFGetAllocator(stream
), 0, &kCFTypeArrayCallBacks
);
340 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoop
);
341 CFArrayAppendValue(fileStream
->rlInfo
.rlArray
, runLoopMode
);
343 CFRunLoopSourceRef rlSrc
;
344 if (!fileStream
->rlInfo
.sock
) {
345 constructCFSocket(fileStream
, isReadStream
, stream
);
347 rlSrc
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.sock
, 0);
348 CFRunLoopAddSource(runLoop
, rlSrc
, runLoopMode
);
352 fileStream
->scheduled
++;
353 if (fileStream
->scheduled
== 1 && fileStream
->fd
> 0 && status
== kCFStreamStatusOpen
) {
355 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
357 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
362 static void fileUnschedule(struct _CFStream
*stream
, CFRunLoopRef runLoop
, CFStringRef runLoopMode
, void *info
) {
363 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
364 #ifdef REAL_FILE_SCHEDULING
365 if (fileStream
->fd
< 0) {
367 if (fileStream
->rlInfo
.rlArray
) {
368 CFMutableArrayRef runloops
= fileStream
->rlInfo
.rlArray
;
370 for (i
= 0, c
= CFArrayGetCount(runloops
); i
+1 < c
; i
+= 2) {
371 if (CFEqual(CFArrayGetValueAtIndex(runloops
, i
), runLoop
) && CFEqual(CFArrayGetValueAtIndex(runloops
, i
+1), runLoopMode
)) {
372 CFArrayRemoveValueAtIndex(runloops
, i
);
373 CFArrayRemoveValueAtIndex(runloops
, i
);
378 } else if (fileStream
->rlInfo
.sock
) {
379 CFRunLoopSourceRef sockSource
= CFSocketCreateRunLoopSource(CFGetAllocator(stream
), fileStream
->rlInfo
.sock
, 0);
380 CFRunLoopRemoveSource(runLoop
, sockSource
, runLoopMode
);
381 CFRelease(sockSource
);
384 if (fileStream
->scheduled
> 0)
385 fileStream
->scheduled
--;
389 static CFTypeRef
fileCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
391 CFTypeRef result
= NULL
;
392 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
394 if (CFEqual(propertyName
, kCFStreamPropertyFileCurrentOffset
)) {
396 // NOTE that this does a lseek of 0 from the current location in
397 // order to populate the offset field which will then be used to
398 // create the resulting value.
399 if (!__CFBitIsSet(fileStream
->flags
, APPEND
) && fileStream
->fd
!= -1) {
400 fileStream
->offset
= lseek(fileStream
->fd
, 0, SEEK_CUR
);
403 if (fileStream
->offset
!= -1) {
404 result
= CFNumberCreate(CFGetAllocator((CFTypeRef
)stream
), kCFNumberSInt64Type
, &(fileStream
->offset
));
411 static Boolean
fileSetProperty(struct _CFStream
*stream
, CFStringRef prop
, CFTypeRef val
, void *info
) {
413 Boolean result
= FALSE
;
414 _CFFileStreamContext
*fileStream
= (_CFFileStreamContext
*)info
;
416 if (CFEqual(prop
, kCFStreamPropertyAppendToFile
) && CFGetTypeID(stream
) == CFWriteStreamGetTypeID() &&
417 CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) == kCFStreamStatusNotOpen
)
419 if (val
== kCFBooleanTrue
) {
420 __CFBitSet(fileStream
->flags
, APPEND
);
421 fileStream
->offset
= -1; // Can't offset and append on the stream
423 __CFBitClear(fileStream
->flags
, APPEND
);
428 else if (CFEqual(prop
, kCFStreamPropertyFileCurrentOffset
)) {
430 if (!__CFBitIsSet(fileStream
->flags
, APPEND
))
432 result
= CFNumberGetValue((CFNumberRef
)val
, kCFNumberSInt64Type
, &(fileStream
->offset
));
435 if ((fileStream
->fd
!= -1) && (lseek(fileStream
->fd
, fileStream
->offset
, SEEK_SET
) == -1)) {
443 static void *fileCreate(struct _CFStream
*stream
, void *info
) {
444 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
445 _CFFileStreamContext
*newCtxt
= CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFFileStreamContext
), 0);
446 if (!newCtxt
) return NULL
;
447 newCtxt
->url
= CFRetain(ctxt
->url
);
448 newCtxt
->fd
= ctxt
->fd
;
449 #ifdef REAL_FILE_SCHEDULING
450 newCtxt
->rlInfo
.sock
= NULL
;
452 newCtxt
->scheduled
= 0;
455 newCtxt
->offset
= -1;
459 static void fileFinalize(struct _CFStream
*stream
, void *info
) {
460 _CFFileStreamContext
*ctxt
= (_CFFileStreamContext
*)info
;
462 #ifdef REAL_FILE_SCHEDULING
463 if (ctxt
->rlInfo
.sock
) {
464 CFSocketInvalidate(ctxt
->rlInfo
.sock
);
465 CFRelease(ctxt
->rlInfo
.sock
);
469 #ifdef REAL_FILE_SCHEDULING
470 } else if (ctxt
->rlInfo
.rlArray
) {
471 CFRelease(ctxt
->rlInfo
.rlArray
);
474 CFRelease(ctxt
->url
);
475 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
478 static CFStringRef
fileCopyDescription(struct _CFStream
*stream
, void *info
) {
480 return CFCopyDescription(((_CFFileStreamContext
*)info
)->url
);
483 /* CFData stream callbacks */
485 CFDataRef data
; // Mutable if the stream was constructed writable
486 const UInt8
*loc
; // Current location in the file
489 } _CFReadDataStreamContext
;
491 #define BUF_SIZE 1024
492 typedef struct _CFStreamByteBuffer
{
494 CFIndex capacity
, length
;
495 struct _CFStreamByteBuffer
*next
;
496 } _CFStreamByteBuffer
;
499 _CFStreamByteBuffer
*firstBuf
, *currentBuf
;
500 CFAllocatorRef bufferAllocator
;
503 } _CFWriteDataStreamContext
;
505 static Boolean
readDataOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
506 _CFReadDataStreamContext
*dataStream
= (_CFReadDataStreamContext
*)info
;
507 if (dataStream
->scheduled
) {
508 if (CFDataGetLength(dataStream
->data
) != 0) {
509 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
511 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
514 errorCode
->error
= 0;
515 *openComplete
= TRUE
;
519 static void readDataSchedule(struct _CFStream
*stream
, CFRunLoopRef rl
, CFStringRef rlMode
, void *info
) {
520 _CFReadDataStreamContext
*dataStream
= (_CFReadDataStreamContext
*)info
;
521 if (dataStream
->scheduled
== FALSE
) {
522 dataStream
->scheduled
= TRUE
;
523 if (CFReadStreamGetStatus((CFReadStreamRef
)stream
) != kCFStreamStatusOpen
)
525 if (CFDataGetBytePtr(dataStream
->data
) + CFDataGetLength(dataStream
->data
) > dataStream
->loc
) {
526 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventHasBytesAvailable
, NULL
);
528 CFReadStreamSignalEvent((CFReadStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
533 static CFIndex
dataRead(CFReadStreamRef stream
, UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*error
, Boolean
*atEOF
, void *info
) {
534 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
535 const UInt8
*bytePtr
= CFDataGetBytePtr(dataCtxt
->data
);
536 CFIndex length
= CFDataGetLength(dataCtxt
->data
);
537 CFIndex bytesToCopy
= bytePtr
+ length
- dataCtxt
->loc
;
538 if (bytesToCopy
> bufferLength
) {
539 bytesToCopy
= bufferLength
;
541 if (bytesToCopy
< 0) {
544 if (bytesToCopy
!= 0) {
545 memmove(buffer
, dataCtxt
->loc
, bytesToCopy
);
546 dataCtxt
->loc
+= bytesToCopy
;
549 *atEOF
= (dataCtxt
->loc
< bytePtr
+ length
) ? FALSE
: TRUE
;
550 if (dataCtxt
->scheduled
&& !*atEOF
) {
551 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
556 static const UInt8
*dataGetBuffer(CFReadStreamRef stream
, CFIndex maxBytesToRead
, CFIndex
*numBytesRead
, CFStreamError
*error
, Boolean
*atEOF
, void *info
) {
557 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
558 const UInt8
*bytes
= CFDataGetBytePtr(dataCtxt
->data
);
559 if (dataCtxt
->loc
- bytes
> maxBytesToRead
) {
560 *numBytesRead
= maxBytesToRead
;
563 *numBytesRead
= dataCtxt
->loc
- bytes
;
567 bytes
= dataCtxt
->loc
;
568 dataCtxt
->loc
+= *numBytesRead
;
569 if (dataCtxt
->scheduled
&& !*atEOF
) {
570 CFReadStreamSignalEvent(stream
, kCFStreamEventHasBytesAvailable
, NULL
);
575 static Boolean
dataCanRead(CFReadStreamRef stream
, void *info
) {
576 _CFReadDataStreamContext
*dataCtxt
= (_CFReadDataStreamContext
*)info
;
577 return (CFDataGetBytePtr(dataCtxt
->data
) + CFDataGetLength(dataCtxt
->data
) > dataCtxt
->loc
) ? TRUE
: FALSE
;
580 static Boolean
writeDataOpen(struct _CFStream
*stream
, CFStreamError
*errorCode
, Boolean
*openComplete
, void *info
) {
581 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
582 if (dataStream
->scheduled
) {
583 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
584 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
586 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
589 errorCode
->error
= 0;
590 *openComplete
= TRUE
;
594 static void writeDataSchedule(struct _CFStream
*stream
, CFRunLoopRef rl
, CFStringRef rlMode
, void *info
) {
595 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
596 if (dataStream
->scheduled
== FALSE
) {
597 dataStream
->scheduled
= TRUE
;
598 if (CFWriteStreamGetStatus((CFWriteStreamRef
)stream
) != kCFStreamStatusOpen
)
600 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) {
601 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventCanAcceptBytes
, NULL
);
603 CFWriteStreamSignalEvent((CFWriteStreamRef
)stream
, kCFStreamEventEndEncountered
, NULL
);
608 static CFIndex
dataWrite(CFWriteStreamRef stream
, const UInt8
*buffer
, CFIndex bufferLength
, CFStreamError
*errorCode
, void *info
) {
609 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
611 CFIndex freeSpace
= dataStream
->currentBuf
->capacity
- dataStream
->currentBuf
->length
;
612 if (dataStream
->bufferAllocator
== kCFAllocatorNull
&& bufferLength
> freeSpace
) {
613 errorCode
->error
= ENOMEM
;
614 errorCode
->domain
= kCFStreamErrorDomainPOSIX
;
617 result
= bufferLength
;
618 while (bufferLength
> 0) {
619 CFIndex amountToCopy
= (bufferLength
> freeSpace
) ? freeSpace
: bufferLength
;
621 memmove(dataStream
->currentBuf
->bytes
+ dataStream
->currentBuf
->length
, buffer
, amountToCopy
);
622 buffer
+= amountToCopy
;
623 bufferLength
-= amountToCopy
;
624 dataStream
->currentBuf
->length
+= amountToCopy
;
626 if (bufferLength
> 0) {
627 CFIndex bufSize
= BUF_SIZE
> bufferLength
? BUF_SIZE
: bufferLength
;
628 _CFStreamByteBuffer
*newBuf
= (_CFStreamByteBuffer
*)CFAllocatorAllocate(dataStream
->bufferAllocator
, sizeof(_CFStreamByteBuffer
) + bufSize
, 0);
629 newBuf
->bytes
= (UInt8
*)(newBuf
+ 1);
630 newBuf
->capacity
= bufSize
;
633 dataStream
->currentBuf
->next
= newBuf
;
634 dataStream
->currentBuf
= newBuf
;
638 errorCode
->error
= 0;
640 if (dataStream
->scheduled
&& (dataStream
->bufferAllocator
!= kCFAllocatorNull
|| dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
)) {
641 CFWriteStreamSignalEvent(stream
, kCFStreamEventCanAcceptBytes
, NULL
);
646 static Boolean
dataCanWrite(CFWriteStreamRef stream
, void *info
) {
647 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
648 if (dataStream
->bufferAllocator
!= kCFAllocatorNull
) return TRUE
;
649 if (dataStream
->currentBuf
->capacity
> dataStream
->currentBuf
->length
) return TRUE
;
653 static CFPropertyListRef
dataCopyProperty(struct _CFStream
*stream
, CFStringRef propertyName
, void *info
) {
654 _CFWriteDataStreamContext
*dataStream
= (_CFWriteDataStreamContext
*)info
;
656 _CFStreamByteBuffer
*buf
;
657 CFAllocatorRef alloc
;
658 UInt8
*bytes
, *currByte
;
659 if (!CFEqual(propertyName
, kCFStreamPropertyDataWritten
)) return NULL
;
660 if (dataStream
->bufferAllocator
== kCFAllocatorNull
) return NULL
;
661 alloc
= dataStream
->bufferAllocator
;
662 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
665 if (size
== 0) return NULL
;
666 bytes
= CFAllocatorAllocate(alloc
, size
, 0);
668 for (buf
= dataStream
->firstBuf
; buf
!= NULL
; buf
= buf
->next
) {
669 memmove(currByte
, buf
->bytes
, buf
->length
);
670 currByte
+= buf
->length
;
672 return CFDataCreateWithBytesNoCopy(alloc
, bytes
, size
, alloc
);
675 static void *readDataCreate(struct _CFStream
*stream
, void *info
) {
676 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
677 _CFReadDataStreamContext
*newCtxt
= (_CFReadDataStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFReadDataStreamContext
), 0);
678 if (!newCtxt
) return NULL
;
679 newCtxt
->data
= CFRetain(ctxt
->data
);
680 newCtxt
->loc
= CFDataGetBytePtr(newCtxt
->data
);
681 newCtxt
->scheduled
= FALSE
;
682 return (void *)newCtxt
;
685 static void readDataFinalize(struct _CFStream
*stream
, void *info
) {
686 _CFReadDataStreamContext
*ctxt
= (_CFReadDataStreamContext
*)info
;
687 CFRelease(ctxt
->data
);
688 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
691 static CFStringRef
readDataCopyDescription(struct _CFStream
*stream
, void *info
) {
692 return CFCopyDescription(((_CFReadDataStreamContext
*)info
)->data
);
695 static void *writeDataCreate(struct _CFStream
*stream
, void *info
) {
696 _CFWriteDataStreamContext
*ctxt
= (_CFWriteDataStreamContext
*)info
;
697 _CFWriteDataStreamContext
*newCtxt
;
698 if (ctxt
->bufferAllocator
!= kCFAllocatorNull
) {
699 if (ctxt
->bufferAllocator
== NULL
) ctxt
->bufferAllocator
= CFAllocatorGetDefault();
700 CFRetain(ctxt
->bufferAllocator
);
701 newCtxt
= CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFWriteDataStreamContext
) + sizeof(_CFStreamByteBuffer
) + BUF_SIZE
, 0);
702 newCtxt
->firstBuf
= (_CFStreamByteBuffer
*)(newCtxt
+ 1);
703 newCtxt
->firstBuf
->bytes
= (UInt8
*)(newCtxt
->firstBuf
+ 1);
704 newCtxt
->firstBuf
->capacity
= BUF_SIZE
;
705 newCtxt
->firstBuf
->length
= 0;
706 newCtxt
->firstBuf
->next
= NULL
;
707 newCtxt
->currentBuf
= newCtxt
->firstBuf
;
708 newCtxt
->bufferAllocator
= ctxt
->bufferAllocator
;
709 newCtxt
->scheduled
= FALSE
;
711 newCtxt
= (_CFWriteDataStreamContext
*)CFAllocatorAllocate(CFGetAllocator(stream
), sizeof(_CFWriteDataStreamContext
) + sizeof(_CFStreamByteBuffer
), 0);
712 newCtxt
->firstBuf
= (_CFStreamByteBuffer
*)(newCtxt
+1);
713 newCtxt
->firstBuf
->bytes
= ctxt
->firstBuf
->bytes
;
714 newCtxt
->firstBuf
->capacity
= ctxt
->firstBuf
->capacity
;
715 newCtxt
->firstBuf
->length
= 0;
716 newCtxt
->firstBuf
->next
= NULL
;
717 newCtxt
->currentBuf
= newCtxt
->firstBuf
;
718 newCtxt
->bufferAllocator
= kCFAllocatorNull
;
719 newCtxt
->scheduled
= FALSE
;
721 return (void *)newCtxt
;
724 static void writeDataFinalize(struct _CFStream
*stream
, void *info
) {
725 _CFWriteDataStreamContext
*ctxt
= (_CFWriteDataStreamContext
*)info
;
726 if (ctxt
->bufferAllocator
!= kCFAllocatorNull
) {
727 _CFStreamByteBuffer
*buf
= ctxt
->firstBuf
->next
, *next
;
728 while (buf
!= NULL
) {
730 CFAllocatorDeallocate(ctxt
->bufferAllocator
, buf
);
733 CFRelease(ctxt
->bufferAllocator
);
735 CFAllocatorDeallocate(CFGetAllocator(stream
), ctxt
);
738 static CFStringRef
writeDataCopyDescription(struct _CFStream
*stream
, void *info
) {
739 return CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<CFWriteDataContext 0x%x>"), (int)info
);
742 static const struct _CFStreamCallBacks fileCallBacks
= {1, fileCreate
, fileFinalize
, fileCopyDescription
, fileOpen
, NULL
, fileRead
, NULL
, fileCanRead
, fileWrite
, fileCanWrite
, fileClose
, fileCopyProperty
, fileSetProperty
, NULL
, fileSchedule
, fileUnschedule
};
744 static struct _CFStream
*_CFStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
, Boolean forReading
) {
745 _CFFileStreamContext fileContext
;
746 CFStringRef scheme
= fileURL
? CFURLCopyScheme(fileURL
) : NULL
;
747 if (!scheme
|| !CFEqual(scheme
, CFSTR("file"))) {
748 if (scheme
) CFRelease(scheme
);
752 fileContext
.url
= fileURL
;
754 return _CFStreamCreateWithConstantCallbacks(alloc
, &fileContext
, &fileCallBacks
, forReading
);
757 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
758 return (CFReadStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, TRUE
);
761 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithFile(CFAllocatorRef alloc
, CFURLRef fileURL
) {
762 return (CFWriteStreamRef
)_CFStreamCreateWithFile(alloc
, fileURL
, FALSE
);
765 static const struct _CFStreamCallBacks readDataCallBacks
= {1, readDataCreate
, readDataFinalize
, readDataCopyDescription
, readDataOpen
, NULL
, dataRead
, dataGetBuffer
, dataCanRead
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, readDataSchedule
, NULL
};
766 static const struct _CFStreamCallBacks writeDataCallBacks
= {1, writeDataCreate
, writeDataFinalize
, writeDataCopyDescription
, writeDataOpen
, NULL
, NULL
, NULL
, NULL
, dataWrite
, dataCanWrite
, NULL
, dataCopyProperty
, NULL
, NULL
, writeDataSchedule
, NULL
};
768 CF_EXPORT CFReadStreamRef
CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc
, const UInt8
*bytes
, CFIndex length
, CFAllocatorRef bytesDeallocator
) {
769 _CFReadDataStreamContext ctxt
;
770 CFReadStreamRef result
;
771 ctxt
.data
= CFDataCreateWithBytesNoCopy(alloc
, bytes
, length
, bytesDeallocator
);
772 result
= (CFReadStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, &readDataCallBacks
, TRUE
);
773 CFRelease(ctxt
.data
);
777 CFWriteStreamRef
CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc
, UInt8
*buffer
, CFIndex bufferCapacity
) {
778 _CFStreamByteBuffer buf
;
779 _CFWriteDataStreamContext ctxt
;
781 buf
.capacity
= bufferCapacity
;
784 ctxt
.firstBuf
= &buf
;
785 ctxt
.currentBuf
= ctxt
.firstBuf
;
786 ctxt
.bufferAllocator
= kCFAllocatorNull
;
787 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, &writeDataCallBacks
, FALSE
);
790 CF_EXPORT CFWriteStreamRef
CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc
, CFAllocatorRef bufferAllocator
) {
791 _CFWriteDataStreamContext ctxt
;
792 ctxt
.firstBuf
= NULL
;
793 ctxt
.currentBuf
= NULL
;
794 ctxt
.bufferAllocator
= bufferAllocator
;
795 return (CFWriteStreamRef
)_CFStreamCreateWithConstantCallbacks(alloc
, &ctxt
, &writeDataCallBacks
, FALSE
);