]> git.saurik.com Git - apple/cf.git/blob - Stream.subproj/CFConcreteStreams.c
CF-368.1.tar.gz
[apple/cf.git] / Stream.subproj / CFConcreteStreams.c
1 /*
2 * Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 /* CFConcreteStreams.c
24 Copyright 2000-2002, Apple, Inc. All rights reserved.
25 Responsibility: Becky Willrich
26 */
27
28 #include <sys/time.h>
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stdio.h>
34 #include <CoreFoundation/CFNumber.h>
35 #include "CFStream.h"
36 #include "CFStreamPriv.h"
37 #include "CFInternal.h"
38 #include "CFUtilitiesPriv.h"
39
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)
45 #endif
46
47 #define SCHEDULE_AFTER_WRITE (0)
48 #define SCHEDULE_AFTER_READ (1)
49 #define APPEND (3)
50 #define AT_EOF (4)
51
52 /* File callbacks */
53 typedef struct {
54 CFURLRef url;
55 int fd;
56 #ifdef REAL_FILE_SCHEDULING
57 union {
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.
61 #else
62 uint16_t scheduled; // ref count of how many times we've been scheduled
63 #endif
64 CFOptionFlags flags;
65
66 off_t offset;
67 } _CFFileStreamContext;
68
69
70 CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset");
71
72
73 #ifdef REAL_FILE_SCHEDULING
74 static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
75
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);
87 }
88 CFRelease(fileStream->rlInfo.rlArray);
89 CFRelease(src);
90 }
91 fileStream->rlInfo.sock = sock;
92 }
93 #endif
94
95 static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) {
96 UInt8 path[1024];
97 int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY);
98 #if defined(__WIN32__)
99 flags |= (_O_BINARY|_O_NOINHERIT);
100 #endif
101
102 __CFSetNastyFile(fileStream->url);
103
104 if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, path, 1024) == FALSE) {
105 error->error = ENOENT;
106 error->domain = kCFStreamErrorDomainPOSIX;
107 return FALSE;
108 }
109 if (__CFBitIsSet(fileStream->flags, APPEND)) {
110 flags |= O_APPEND;
111 if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther)) flags &= ~O_TRUNC;
112 }
113
114 do {
115 fileStream->fd = open(path, flags, 0666);
116
117 if (fileStream->fd < 0)
118 break;
119
120 if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1))
121 break;
122
123 #ifdef REAL_FILE_SCHEDULING
124 if (fileStream->rlInfo.rlArray != NULL) {
125 constructCFSocket(fileStream, forRead, stream);
126 }
127 #endif
128
129 return TRUE;
130 } while (1);
131
132 error->error = errno;
133 error->domain = kCFStreamErrorDomainPOSIX;
134
135 return FALSE;
136 }
137
138 static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
139 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
140 if (ctxt->fd >= 0) {
141 // Open already occurred
142 errorCode->error = 0;
143 *openComplete = TRUE;
144 return TRUE;
145 }
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) {
151 if (forRead)
152 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
153 else
154 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
155 }
156 #endif
157 return TRUE;
158 } else {
159 return FALSE;
160 }
161 }
162
163 __private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) {
164 CFIndex bytesRead = read(fd, buffer, bufferLength);
165 if (bytesRead < 0) {
166 errorCode->error = errno;
167 errorCode->domain = kCFStreamErrorDomainPOSIX;
168 return -1;
169 } else {
170 *atEOF = (bytesRead == 0) ? TRUE : FALSE;
171 errorCode->error = 0;
172 return bytesRead;
173 }
174 }
175
176 static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) {
177 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
178 CFIndex result;
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);
185 }
186 }
187 #else
188 if (*atEOF)
189 __CFBitSet(ctxt->flags, AT_EOF);
190 if (ctxt->scheduled > 0 && !*atEOF) {
191 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
192 }
193 #endif
194 return result;
195 }
196
197 #ifdef REAL_FILE_SCHEDULING
198 __private_extern__ Boolean fdCanRead(int fd) {
199 struct timeval timeout = {0, 0};
200 fd_set *readSetPtr;
201 fd_set readSet;
202 Boolean result;
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) {
205 FD_ZERO(&readSet);
206 readSetPtr = &readSet;
207 } else {
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;
212 }
213 FD_SET(fd, readSetPtr);
214 result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE;
215 if (readSetPtr != &readSet) {
216 free(readSetPtr);
217 }
218 return result;
219 }
220 #endif
221
222 static Boolean fileCanRead(CFReadStreamRef stream, void *info) {
223 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
224 #ifdef REAL_FILE_SCHEDULING
225 return fdCanRead(ctxt->fd);
226 #else
227 return !__CFBitIsSet(ctxt->flags, AT_EOF);
228 #endif
229 }
230
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;
236 return -1;
237 } else {
238 errorCode->error = 0;
239 return bytesWritten;
240 }
241 }
242
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);
251 }
252 }
253 #else
254 if (fileStream->scheduled > 0) {
255 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
256 }
257 #endif
258 return result;
259 }
260
261 #ifdef REAL_FILE_SCHEDULING
262 __private_extern__ Boolean fdCanWrite(int fd) {
263 struct timeval timeout = {0, 0};
264 fd_set *writeSetPtr;
265 fd_set writeSet;
266 Boolean result;
267 if (fd < FD_SETSIZE) {
268 FD_ZERO(&writeSet);
269 writeSetPtr = &writeSet;
270 } else {
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;
275 }
276 FD_SET(fd, writeSetPtr);
277 result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE;
278 if (writeSetPtr != &writeSet) {
279 free(writeSetPtr);
280 }
281 return result;
282 }
283 #endif
284
285 static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) {
286 #ifdef REAL_FILE_SCHEDULING
287 return fdCanWrite(((_CFFileStreamContext *)info)->fd);
288 #else
289 return TRUE;
290 #endif
291 }
292
293 static void fileClose(struct _CFStream *stream, void *info) {
294 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
295 if (ctxt->fd >= 0) {
296 close(ctxt->fd);
297 ctxt->fd = -1;
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;
303 }
304 } else if (ctxt->rlInfo.rlArray) {
305 CFRelease(ctxt->rlInfo.rlArray);
306 ctxt->rlInfo.rlArray = NULL;
307 #endif
308 }
309 }
310
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);
319 } else {
320 // type == kCFSocketReadCallBack
321 __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ);
322 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
323 }
324 }
325 #endif
326
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
333 return;
334 }
335 #ifdef REAL_FILE_SCHEDULING
336 if (fileStream->fd < 0) {
337 if (!fileStream->rlInfo.rlArray) {
338 fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks);
339 }
340 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop);
341 CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode);
342 } else {
343 CFRunLoopSourceRef rlSrc;
344 if (!fileStream->rlInfo.sock) {
345 constructCFSocket(fileStream, isReadStream, stream);
346 }
347 rlSrc = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
348 CFRunLoopAddSource(runLoop, rlSrc, runLoopMode);
349 CFRelease(rlSrc);
350 }
351 #else
352 fileStream->scheduled++;
353 if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) {
354 if (isReadStream)
355 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
356 else
357 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
358 }
359 #endif
360 }
361
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) {
366 // Not opened yet
367 if (fileStream->rlInfo.rlArray) {
368 CFMutableArrayRef runloops = fileStream->rlInfo.rlArray;
369 CFIndex i, c;
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);
374 break;
375 }
376 }
377 }
378 } else if (fileStream->rlInfo.sock) {
379 CFRunLoopSourceRef sockSource = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
380 CFRunLoopRemoveSource(runLoop, sockSource, runLoopMode);
381 CFRelease(sockSource);
382 }
383 #else
384 if (fileStream->scheduled > 0)
385 fileStream->scheduled--;
386 #endif
387 }
388
389 static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
390
391 CFTypeRef result = NULL;
392 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
393
394 if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) {
395
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);
401 }
402
403 if (fileStream->offset != -1) {
404 result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset));
405 }
406 }
407
408 return result;
409 }
410
411 static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) {
412
413 Boolean result = FALSE;
414 _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
415
416 if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() &&
417 CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen)
418 {
419 if (val == kCFBooleanTrue) {
420 __CFBitSet(fileStream->flags, APPEND);
421 fileStream->offset = -1; // Can't offset and append on the stream
422 } else {
423 __CFBitClear(fileStream->flags, APPEND);
424 }
425 result = TRUE;
426 }
427
428 else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) {
429
430 if (!__CFBitIsSet(fileStream->flags, APPEND))
431 {
432 result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset));
433 }
434
435 if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) {
436 result = FALSE;
437 }
438 }
439
440 return result;
441 }
442
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;
451 #else
452 newCtxt->scheduled = 0;
453 #endif
454 newCtxt->flags = 0;
455 newCtxt->offset = -1;
456 return newCtxt;
457 }
458
459 static void fileFinalize(struct _CFStream *stream, void *info) {
460 _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
461 if (ctxt->fd > 0) {
462 #ifdef REAL_FILE_SCHEDULING
463 if (ctxt->rlInfo.sock) {
464 CFSocketInvalidate(ctxt->rlInfo.sock);
465 CFRelease(ctxt->rlInfo.sock);
466 }
467 #endif
468 close(ctxt->fd);
469 #ifdef REAL_FILE_SCHEDULING
470 } else if (ctxt->rlInfo.rlArray) {
471 CFRelease(ctxt->rlInfo.rlArray);
472 #endif
473 }
474 CFRelease(ctxt->url);
475 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
476 }
477
478 static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) {
479 // This needs work
480 return CFCopyDescription(((_CFFileStreamContext *)info)->url);
481 }
482
483 /* CFData stream callbacks */
484 typedef struct {
485 CFDataRef data; // Mutable if the stream was constructed writable
486 const UInt8 *loc; // Current location in the file
487 Boolean scheduled;
488 char _padding[3];
489 } _CFReadDataStreamContext;
490
491 #define BUF_SIZE 1024
492 typedef struct _CFStreamByteBuffer {
493 UInt8 *bytes;
494 CFIndex capacity, length;
495 struct _CFStreamByteBuffer *next;
496 } _CFStreamByteBuffer;
497
498 typedef struct {
499 _CFStreamByteBuffer *firstBuf, *currentBuf;
500 CFAllocatorRef bufferAllocator;
501 Boolean scheduled;
502 char _padding[3];
503 } _CFWriteDataStreamContext;
504
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);
510 } else {
511 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
512 }
513 }
514 errorCode->error = 0;
515 *openComplete = TRUE;
516 return TRUE;
517 }
518
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)
524 return;
525 if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) {
526 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
527 } else {
528 CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
529 }
530 }
531 }
532
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;
540 }
541 if (bytesToCopy < 0) {
542 bytesToCopy = 0;
543 }
544 if (bytesToCopy != 0) {
545 memmove(buffer, dataCtxt->loc, bytesToCopy);
546 dataCtxt->loc += bytesToCopy;
547 }
548 error->error = 0;
549 *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE;
550 if (dataCtxt->scheduled && !*atEOF) {
551 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
552 }
553 return bytesToCopy;
554 }
555
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;
561 *atEOF = FALSE;
562 } else {
563 *numBytesRead = dataCtxt->loc - bytes;
564 *atEOF = TRUE;
565 }
566 error->error = 0;
567 bytes = dataCtxt->loc;
568 dataCtxt->loc += *numBytesRead;
569 if (dataCtxt->scheduled && !*atEOF) {
570 CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
571 }
572 return bytes;
573 }
574
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;
578 }
579
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);
585 } else {
586 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
587 }
588 }
589 errorCode->error = 0;
590 *openComplete = TRUE;
591 return TRUE;
592 }
593
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)
599 return;
600 if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) {
601 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
602 } else {
603 CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
604 }
605 }
606 }
607
608 static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) {
609 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
610 CFIndex result;
611 CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length;
612 if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) {
613 errorCode->error = ENOMEM;
614 errorCode->domain = kCFStreamErrorDomainPOSIX;
615 return -1;
616 } else {
617 result = bufferLength;
618 while (bufferLength > 0) {
619 CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength;
620 if (freeSpace > 0) {
621 memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy);
622 buffer += amountToCopy;
623 bufferLength -= amountToCopy;
624 dataStream->currentBuf->length += amountToCopy;
625 }
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;
631 newBuf->length = 0;
632 newBuf->next = NULL;
633 dataStream->currentBuf->next = newBuf;
634 dataStream->currentBuf = newBuf;
635 freeSpace = bufSize;
636 }
637 }
638 errorCode->error = 0;
639 }
640 if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) {
641 CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
642 }
643 return result;
644 }
645
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;
650 return FALSE;
651 }
652
653 static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
654 _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
655 CFIndex size = 0;
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) {
663 size += buf->length;
664 }
665 if (size == 0) return NULL;
666 bytes = CFAllocatorAllocate(alloc, size, 0);
667 currByte = bytes;
668 for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
669 memmove(currByte, buf->bytes, buf->length);
670 currByte += buf->length;
671 }
672 return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc);
673 }
674
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;
683 }
684
685 static void readDataFinalize(struct _CFStream *stream, void *info) {
686 _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
687 CFRelease(ctxt->data);
688 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
689 }
690
691 static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) {
692 return CFCopyDescription(((_CFReadDataStreamContext *)info)->data);
693 }
694
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;
710 } else {
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;
720 }
721 return (void *)newCtxt;
722 }
723
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) {
729 next = buf->next;
730 CFAllocatorDeallocate(ctxt->bufferAllocator, buf);
731 buf = next;
732 }
733 CFRelease(ctxt->bufferAllocator);
734 }
735 CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
736 }
737
738 static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) {
739 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<CFWriteDataContext 0x%x>"), (int)info);
740 }
741
742 static const struct _CFStreamCallBacks fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule};
743
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);
749 return NULL;
750 }
751 CFRelease(scheme);
752 fileContext.url = fileURL;
753 fileContext.fd = -1;
754 return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, &fileCallBacks, forReading);
755 }
756
757 CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
758 return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE);
759 }
760
761 CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
762 return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE);
763 }
764
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};
767
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);
774 return result;
775 }
776
777 CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) {
778 _CFStreamByteBuffer buf;
779 _CFWriteDataStreamContext ctxt;
780 buf.bytes = buffer;
781 buf.capacity = bufferCapacity;
782 buf.length = 0;
783 buf.next = NULL;
784 ctxt.firstBuf = &buf;
785 ctxt.currentBuf = ctxt.firstBuf;
786 ctxt.bufferAllocator = kCFAllocatorNull;
787 return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, &writeDataCallBacks, FALSE);
788 }
789
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);
796 }
797
798 #undef BUF_SIZE