]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (c) 2011 Apple 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 | ||
24 | /* CFConcreteStreams.c | |
25 | Copyright (c) 2000-2011, Apple Inc. All rights reserved. | |
26 | Responsibility: John Iarocci | |
27 | */ | |
28 | ||
29 | #include "CFStreamInternal.h" | |
30 | #include "CFInternal.h" | |
31 | #include <CoreFoundation/CFPriv.h> | |
32 | #include <CoreFoundation/CFNumber.h> | |
33 | #include <sys/types.h> | |
34 | #include <stdlib.h> | |
35 | #include <fcntl.h> | |
36 | #include <string.h> | |
37 | #include <stdio.h> | |
38 | #include <sys/stat.h> | |
39 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
40 | #include <sys/time.h> | |
41 | #include <unistd.h> | |
42 | #elif DEPLOYMENT_TARGET_WINDOWS | |
43 | #else | |
44 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
45 | #endif | |
46 | ||
47 | ||
48 | #define SCHEDULE_AFTER_WRITE (0) | |
49 | #define SCHEDULE_AFTER_READ (1) | |
50 | #define APPEND (3) | |
51 | #define AT_EOF (4) | |
52 | #define USE_RUNLOOP_ARRAY (5) | |
53 | ||
54 | ||
55 | /* File callbacks */ | |
56 | typedef struct { | |
57 | CFURLRef url; | |
58 | int fd; | |
59 | #ifdef REAL_FILE_SCHEDULING | |
60 | union { | |
61 | CFFileDescriptorRef cffd; // ref created once we open and have an fd | |
62 | CFMutableArrayRef rlArray; // scheduling information prior to open | |
63 | } rlInfo; // If fd > 0, cffd exists. Otherwise, rlArray. | |
64 | #else | |
65 | uint16_t scheduled; // ref count of how many times we've been scheduled | |
66 | #endif | |
67 | CFOptionFlags flags; | |
68 | off_t offset; | |
69 | } _CFFileStreamContext; | |
70 | ||
71 | ||
72 | CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset"); | |
73 | ||
74 | ||
75 | #ifdef REAL_FILE_SCHEDULING | |
76 | extern void _CFFileDescriptorInduceFakeReadCallBack(CFFileDescriptorRef); | |
77 | static void fileCallBack(CFFileDescriptorRef f, CFOptionFlags callBackTypes, void *info); | |
78 | ||
79 | static void constructCFFD(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) { | |
80 | CFFileDescriptorContext context = {0, stream, NULL, NULL, (void *)CFCopyDescription}; | |
81 | CFFileDescriptorRef cffd = CFFileDescriptorCreate(CFGetAllocator(stream), fileStream->fd, false, fileCallBack, &context); | |
82 | CFFileDescriptorEnableCallBacks(cffd, forRead ? kCFFileDescriptorReadCallBack : kCFFileDescriptorWriteCallBack); | |
83 | if (fileStream->rlInfo.rlArray) { | |
84 | CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray); | |
85 | CFRunLoopSourceRef src = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), cffd, 0); | |
86 | for (i = 0; i+1 < c; i += 2) { | |
87 | CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i); | |
88 | CFStringRef mode = CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1); | |
89 | CFRunLoopAddSource(rl, src, mode); | |
90 | } | |
91 | CFRelease(fileStream->rlInfo.rlArray); | |
92 | CFRelease(src); | |
93 | } | |
94 | fileStream->rlInfo.cffd = cffd; | |
95 | } | |
96 | #endif | |
97 | ||
98 | static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) { | |
99 | int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY); | |
100 | #if DEPLOYMENT_TARGET_WINDOWS | |
101 | wchar_t path[CFMaxPathSize]; | |
102 | flags |= (_O_BINARY|_O_NOINHERIT); | |
103 | if (_CFURLGetWideFileSystemRepresentation(fileStream->url, TRUE, path, CFMaxPathSize) == FALSE) | |
104 | #elif DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
105 | char path[CFMaxPathSize]; | |
106 | if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, (UInt8 *)path, CFMaxPathSize) == FALSE) | |
107 | #else | |
108 | #error Unknown or unspecified DEPLOYMENT_TARGET | |
109 | #endif | |
110 | { | |
111 | error->error = ENOENT; | |
112 | error->domain = kCFStreamErrorDomainPOSIX; | |
113 | return FALSE; | |
114 | } | |
115 | if (__CFBitIsSet(fileStream->flags, APPEND)) { | |
116 | flags |= O_APPEND; | |
117 | flags &= ~O_TRUNC; | |
118 | } | |
119 | ||
120 | do { | |
121 | #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED | |
122 | fileStream->fd = open((const char *)path, flags, 0666); | |
123 | #elif DEPLOYMENT_TARGET_WINDOWS | |
124 | fileStream->fd = _wopen(path, flags, 0666); | |
125 | #endif | |
126 | if (fileStream->fd < 0) | |
127 | break; | |
128 | ||
129 | if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) | |
130 | break; | |
131 | ||
132 | #ifdef REAL_FILE_SCHEDULING | |
133 | if (fileStream->rlInfo.rlArray != NULL) { | |
134 | constructCFFD(fileStream, forRead, stream); | |
135 | } | |
136 | #endif | |
137 | ||
138 | return TRUE; | |
139 | } while (1); | |
140 | ||
141 | __CFBitSet(fileStream->flags, USE_RUNLOOP_ARRAY); | |
142 | error->error = errno; | |
143 | error->domain = kCFStreamErrorDomainPOSIX; | |
144 | ||
145 | return FALSE; | |
146 | } | |
147 | ||
148 | static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { | |
149 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
150 | Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); | |
151 | *openComplete = TRUE; | |
152 | if (ctxt->url) { | |
153 | if (constructFD(ctxt, errorCode, forRead, stream)) { | |
154 | #ifndef REAL_FILE_SCHEDULING | |
155 | if (ctxt->scheduled > 0) { | |
156 | if (forRead) | |
157 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); | |
158 | else | |
159 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); | |
160 | } | |
161 | #endif | |
162 | return TRUE; | |
163 | } else { | |
164 | return FALSE; | |
165 | } | |
166 | #ifdef REAL_FILE_SCHEDULING | |
167 | } else if (ctxt->rlInfo.rlArray != NULL) { | |
168 | constructCFFD(ctxt, forRead, stream); | |
169 | #endif | |
170 | } | |
171 | return TRUE; | |
172 | } | |
173 | ||
174 | __private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) { | |
175 | CFIndex bytesRead = read(fd, buffer, bufferLength); | |
176 | if (bytesRead < 0) { | |
177 | errorCode->error = errno; | |
178 | errorCode->domain = kCFStreamErrorDomainPOSIX; | |
179 | return -1; | |
180 | } else { | |
181 | *atEOF = (bytesRead == 0) ? TRUE : FALSE; | |
182 | errorCode->error = 0; | |
183 | return bytesRead; | |
184 | } | |
185 | } | |
186 | ||
187 | static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) { | |
188 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
189 | CFIndex result; | |
190 | result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF); | |
191 | #ifdef REAL_FILE_SCHEDULING | |
192 | if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) { | |
193 | __CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ); | |
194 | if (!*atEOF && ctxt->rlInfo.cffd) { | |
195 | struct stat statbuf; | |
196 | int ret = fstat(ctxt->fd, &statbuf); | |
197 | if (0 <= ret && (S_IFREG == (statbuf.st_mode & S_IFMT))) { | |
198 | off_t offset = lseek(ctxt->fd, 0, SEEK_CUR); | |
199 | if (statbuf.st_size == offset) { | |
200 | _CFFileDescriptorInduceFakeReadCallBack(ctxt->rlInfo.cffd); | |
201 | } | |
202 | } | |
203 | } | |
204 | if (ctxt->rlInfo.cffd) { | |
205 | CFFileDescriptorEnableCallBacks(ctxt->rlInfo.cffd, kCFFileDescriptorReadCallBack); | |
206 | } | |
207 | } | |
208 | #else | |
209 | if (*atEOF) | |
210 | __CFBitSet(ctxt->flags, AT_EOF); | |
211 | if (ctxt->scheduled > 0 && !*atEOF) { | |
212 | CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); | |
213 | } | |
214 | #endif | |
215 | return result; | |
216 | } | |
217 | ||
218 | #ifdef REAL_FILE_SCHEDULING | |
219 | __private_extern__ Boolean fdCanRead(int fd) { | |
220 | struct timeval timeout = {0, 0}; | |
221 | fd_set *readSetPtr; | |
222 | fd_set readSet; | |
223 | Boolean result; | |
224 | // fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant | |
225 | if (fd < FD_SETSIZE) { | |
226 | FD_ZERO(&readSet); | |
227 | readSetPtr = &readSet; | |
228 | } else { | |
229 | int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); | |
230 | uint32_t *fds_bits = (uint32_t *)malloc(size); | |
231 | memset(fds_bits, 0, size); | |
232 | readSetPtr = (fd_set *)fds_bits; | |
233 | } | |
234 | FD_SET(fd, readSetPtr); | |
235 | result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE; | |
236 | if (readSetPtr != &readSet) { | |
237 | free(readSetPtr); | |
238 | } | |
239 | return result; | |
240 | } | |
241 | #endif | |
242 | ||
243 | static Boolean fileCanRead(CFReadStreamRef stream, void *info) { | |
244 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
245 | #ifdef REAL_FILE_SCHEDULING | |
246 | return fdCanRead(ctxt->fd); | |
247 | #else | |
248 | return !__CFBitIsSet(ctxt->flags, AT_EOF); | |
249 | #endif | |
250 | } | |
251 | ||
252 | __private_extern__ CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) { | |
253 | CFIndex bytesWritten = write(fd, buffer, bufferLength); | |
254 | if (bytesWritten < 0) { | |
255 | errorCode->error = errno; | |
256 | errorCode->domain = kCFStreamErrorDomainPOSIX; | |
257 | return -1; | |
258 | } else { | |
259 | errorCode->error = 0; | |
260 | return bytesWritten; | |
261 | } | |
262 | } | |
263 | ||
264 | static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { | |
265 | _CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info); | |
266 | CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode); | |
267 | #ifdef REAL_FILE_SCHEDULING | |
268 | if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) { | |
269 | __CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE); | |
270 | if (fileStream->rlInfo.cffd) { | |
271 | CFFileDescriptorEnableCallBacks(fileStream->rlInfo.cffd, kCFFileDescriptorWriteCallBack); | |
272 | } | |
273 | } | |
274 | #else | |
275 | if (fileStream->scheduled > 0) { | |
276 | CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); | |
277 | } | |
278 | #endif | |
279 | return result; | |
280 | } | |
281 | ||
282 | #ifdef REAL_FILE_SCHEDULING | |
283 | __private_extern__ Boolean fdCanWrite(int fd) { | |
284 | struct timeval timeout = {0, 0}; | |
285 | fd_set *writeSetPtr; | |
286 | fd_set writeSet; | |
287 | Boolean result; | |
288 | if (fd < FD_SETSIZE) { | |
289 | FD_ZERO(&writeSet); | |
290 | writeSetPtr = &writeSet; | |
291 | } else { | |
292 | int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t); | |
293 | uint32_t *fds_bits = (uint32_t *)malloc(size); | |
294 | memset(fds_bits, 0, size); | |
295 | writeSetPtr = (fd_set *)fds_bits; | |
296 | } | |
297 | FD_SET(fd, writeSetPtr); | |
298 | result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE; | |
299 | if (writeSetPtr != &writeSet) { | |
300 | free(writeSetPtr); | |
301 | } | |
302 | return result; | |
303 | } | |
304 | #endif | |
305 | ||
306 | static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) { | |
307 | #ifdef REAL_FILE_SCHEDULING | |
308 | return fdCanWrite(((_CFFileStreamContext *)info)->fd); | |
309 | #else | |
310 | return TRUE; | |
311 | #endif | |
312 | } | |
313 | ||
314 | static void fileClose(struct _CFStream *stream, void *info) { | |
315 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
316 | if (ctxt->fd >= 0) { | |
317 | close(ctxt->fd); | |
318 | ctxt->fd = -1; | |
319 | #ifdef REAL_FILE_SCHEDULING | |
320 | if (ctxt->rlInfo.cffd) { | |
321 | CFFileDescriptorInvalidate(ctxt->rlInfo.cffd); | |
322 | CFRelease(ctxt->rlInfo.cffd); | |
323 | ctxt->rlInfo.cffd = NULL; | |
324 | } | |
325 | } else if (ctxt->rlInfo.rlArray) { | |
326 | CFRelease(ctxt->rlInfo.rlArray); | |
327 | ctxt->rlInfo.rlArray = NULL; | |
328 | #endif | |
329 | } | |
330 | } | |
331 | ||
332 | #ifdef REAL_FILE_SCHEDULING | |
333 | static void fileCallBack(CFFileDescriptorRef f, CFOptionFlags type, void *info) { | |
334 | struct _CFStream *stream = (struct _CFStream *)info; | |
335 | Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); | |
336 | _CFFileStreamContext *fileStream = isReadStream ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream); | |
337 | if (type == kCFFileDescriptorWriteCallBack) { | |
338 | __CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE); | |
339 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); | |
340 | } else { | |
341 | __CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ); | |
342 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); | |
343 | } | |
344 | } | |
345 | #endif | |
346 | ||
347 | static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { | |
348 | _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; | |
349 | Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); | |
350 | CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); | |
351 | if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) { | |
352 | // Stream's already closed or error-ed out | |
353 | return; | |
354 | } | |
355 | #ifdef REAL_FILE_SCHEDULING | |
356 | if (status == kCFStreamStatusNotOpen) { | |
357 | if (!fileStream->rlInfo.rlArray) { | |
358 | fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks); | |
359 | } | |
360 | CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop); | |
361 | CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode); | |
362 | } else { | |
363 | CFRunLoopSourceRef rlSrc; | |
364 | if (!fileStream->rlInfo.cffd) { | |
365 | constructCFFD(fileStream, isReadStream, stream); | |
366 | } | |
367 | rlSrc = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.cffd, 0); | |
368 | CFRunLoopAddSource(runLoop, rlSrc, runLoopMode); | |
369 | CFRelease(rlSrc); | |
370 | } | |
371 | #else | |
372 | fileStream->scheduled++; | |
373 | if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) { | |
374 | if (isReadStream) | |
375 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); | |
376 | else | |
377 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); | |
378 | } | |
379 | #endif | |
380 | } | |
381 | ||
382 | static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) { | |
383 | _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; | |
384 | #ifdef REAL_FILE_SCHEDULING | |
385 | Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID()); | |
386 | CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream); | |
387 | if (status == kCFStreamStatusNotOpen) { | |
388 | // Not opened yet | |
389 | if (fileStream->rlInfo.rlArray) { | |
390 | CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; | |
391 | CFIndex i, c; | |
392 | for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { | |
393 | if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { | |
394 | CFArrayRemoveValueAtIndex(runloops, i); | |
395 | CFArrayRemoveValueAtIndex(runloops, i); | |
396 | break; | |
397 | } | |
398 | } | |
399 | } | |
400 | } else if (fileStream->rlInfo.cffd) { | |
401 | if (__CFBitIsSet(fileStream->flags, USE_RUNLOOP_ARRAY)) { | |
402 | // we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.cffd | |
403 | CFMutableArrayRef runloops = fileStream->rlInfo.rlArray; | |
404 | CFIndex i, c; | |
405 | for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) { | |
406 | if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) { | |
407 | CFArrayRemoveValueAtIndex(runloops, i); | |
408 | CFArrayRemoveValueAtIndex(runloops, i); | |
409 | break; | |
410 | } | |
411 | } | |
412 | } else { | |
413 | CFRunLoopSourceRef rlSrc = CFFileDescriptorCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.cffd, 0); | |
414 | CFRunLoopRemoveSource(runLoop, rlSrc, runLoopMode); | |
415 | CFRelease(rlSrc); | |
416 | } | |
417 | } | |
418 | #else | |
419 | if (fileStream->scheduled > 0) | |
420 | fileStream->scheduled--; | |
421 | #endif | |
422 | } | |
423 | ||
424 | static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { | |
425 | ||
426 | CFTypeRef result = NULL; | |
427 | _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; | |
428 | ||
429 | if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) { | |
430 | ||
431 | // NOTE that this does a lseek of 0 from the current location in | |
432 | // order to populate the offset field which will then be used to | |
433 | // create the resulting value. | |
434 | if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) { | |
435 | fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR); | |
436 | } | |
437 | ||
438 | if (fileStream->offset != -1) { | |
439 | result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset)); | |
440 | } | |
441 | } | |
442 | ||
443 | return result; | |
444 | } | |
445 | ||
446 | static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) { | |
447 | ||
448 | Boolean result = FALSE; | |
449 | _CFFileStreamContext *fileStream = (_CFFileStreamContext *)info; | |
450 | ||
451 | if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() && | |
452 | CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen) | |
453 | { | |
454 | if (val == kCFBooleanTrue) { | |
455 | __CFBitSet(fileStream->flags, APPEND); | |
456 | fileStream->offset = -1; // Can't offset and append on the stream | |
457 | } else { | |
458 | __CFBitClear(fileStream->flags, APPEND); | |
459 | } | |
460 | result = TRUE; | |
461 | } | |
462 | ||
463 | else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) { | |
464 | ||
465 | if (!__CFBitIsSet(fileStream->flags, APPEND)) | |
466 | { | |
467 | result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset)); | |
468 | } | |
469 | ||
470 | if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) { | |
471 | result = FALSE; | |
472 | } | |
473 | } | |
474 | ||
475 | return result; | |
476 | } | |
477 | ||
478 | static void *fileCreate(struct _CFStream *stream, void *info) { | |
479 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
480 | _CFFileStreamContext *newCtxt = (_CFFileStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0); | |
481 | if (!newCtxt) return NULL; | |
482 | newCtxt->url = ctxt->url; | |
483 | if (newCtxt->url) { | |
484 | CFRetain(newCtxt->url); | |
485 | } | |
486 | newCtxt->fd = ctxt->fd; | |
487 | #ifdef REAL_FILE_SCHEDULING | |
488 | newCtxt->rlInfo.cffd = NULL; | |
489 | #else | |
490 | newCtxt->scheduled = 0; | |
491 | #endif | |
492 | newCtxt->flags = 0; | |
493 | newCtxt->offset = -1; | |
494 | return newCtxt; | |
495 | } | |
496 | ||
497 | static void fileFinalize(struct _CFStream *stream, void *info) { | |
498 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
499 | if (ctxt->fd > 0) { | |
500 | #ifdef REAL_FILE_SCHEDULING | |
501 | if (ctxt->rlInfo.cffd) { | |
502 | CFFileDescriptorInvalidate(ctxt->rlInfo.cffd); | |
503 | CFRelease(ctxt->rlInfo.cffd); | |
504 | ctxt->rlInfo.cffd = NULL; | |
505 | } | |
506 | #endif | |
507 | close(ctxt->fd); | |
508 | #ifdef REAL_FILE_SCHEDULING | |
509 | } else if (ctxt->rlInfo.rlArray) { | |
510 | CFRelease(ctxt->rlInfo.rlArray); | |
511 | #endif | |
512 | } | |
513 | if (ctxt->url) { | |
514 | CFRelease(ctxt->url); | |
515 | } | |
516 | CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); | |
517 | } | |
518 | ||
519 | static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) { | |
520 | // This needs work | |
521 | _CFFileStreamContext *ctxt = (_CFFileStreamContext *)info; | |
522 | if (ctxt->url) { | |
523 | return CFCopyDescription(ctxt->url); | |
524 | } else { | |
525 | return CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("fd = %d"), ctxt->fd); | |
526 | } | |
527 | } | |
528 | ||
529 | /* CFData stream callbacks */ | |
530 | typedef struct { | |
531 | CFDataRef data; // Mutable if the stream was constructed writable | |
532 | const UInt8 *loc; // Current location in the file | |
533 | Boolean scheduled; | |
534 | char _padding[3]; | |
535 | } _CFReadDataStreamContext; | |
536 | ||
537 | #define BUF_SIZE 1024 | |
538 | typedef struct _CFStreamByteBuffer { | |
539 | UInt8 *bytes; | |
540 | CFIndex capacity, length; | |
541 | struct _CFStreamByteBuffer *next; | |
542 | } _CFStreamByteBuffer; | |
543 | ||
544 | typedef struct { | |
545 | _CFStreamByteBuffer *firstBuf, *currentBuf; | |
546 | CFAllocatorRef bufferAllocator; | |
547 | Boolean scheduled; | |
548 | char _padding[3]; | |
549 | } _CFWriteDataStreamContext; | |
550 | ||
551 | static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { | |
552 | _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; | |
553 | if (dataStream->scheduled) { | |
554 | if (CFDataGetLength(dataStream->data) != 0) { | |
555 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); | |
556 | } else { | |
557 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); | |
558 | } | |
559 | } | |
560 | errorCode->error = 0; | |
561 | *openComplete = TRUE; | |
562 | return TRUE; | |
563 | } | |
564 | ||
565 | static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { | |
566 | _CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info; | |
567 | if (dataStream->scheduled == FALSE) { | |
568 | dataStream->scheduled = TRUE; | |
569 | if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen) | |
570 | return; | |
571 | if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) { | |
572 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL); | |
573 | } else { | |
574 | CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL); | |
575 | } | |
576 | } | |
577 | } | |
578 | ||
579 | static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) { | |
580 | _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; | |
581 | const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data); | |
582 | CFIndex length = CFDataGetLength(dataCtxt->data); | |
583 | CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc; | |
584 | if (bytesToCopy > bufferLength) { | |
585 | bytesToCopy = bufferLength; | |
586 | } | |
587 | if (bytesToCopy < 0) { | |
588 | bytesToCopy = 0; | |
589 | } | |
590 | if (bytesToCopy != 0) { | |
591 | memmove(buffer, dataCtxt->loc, bytesToCopy); | |
592 | dataCtxt->loc += bytesToCopy; | |
593 | } | |
594 | error->error = 0; | |
595 | *atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE; | |
596 | if (dataCtxt->scheduled && !*atEOF) { | |
597 | CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); | |
598 | } | |
599 | return bytesToCopy; | |
600 | } | |
601 | ||
602 | static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) { | |
603 | _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; | |
604 | const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data); | |
605 | if (dataCtxt->loc - bytes > maxBytesToRead) { | |
606 | *numBytesRead = maxBytesToRead; | |
607 | *atEOF = FALSE; | |
608 | } else { | |
609 | *numBytesRead = dataCtxt->loc - bytes; | |
610 | *atEOF = TRUE; | |
611 | } | |
612 | error->error = 0; | |
613 | bytes = dataCtxt->loc; | |
614 | dataCtxt->loc += *numBytesRead; | |
615 | if (dataCtxt->scheduled && !*atEOF) { | |
616 | CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL); | |
617 | } | |
618 | return bytes; | |
619 | } | |
620 | ||
621 | static Boolean dataCanRead(CFReadStreamRef stream, void *info) { | |
622 | _CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info; | |
623 | return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE; | |
624 | } | |
625 | ||
626 | static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) { | |
627 | _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; | |
628 | if (dataStream->scheduled) { | |
629 | if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { | |
630 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); | |
631 | } else { | |
632 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); | |
633 | } | |
634 | } | |
635 | errorCode->error = 0; | |
636 | *openComplete = TRUE; | |
637 | return TRUE; | |
638 | } | |
639 | ||
640 | static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) { | |
641 | _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; | |
642 | if (dataStream->scheduled == FALSE) { | |
643 | dataStream->scheduled = TRUE; | |
644 | if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen) | |
645 | return; | |
646 | if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) { | |
647 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL); | |
648 | } else { | |
649 | CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL); | |
650 | } | |
651 | } | |
652 | } | |
653 | ||
654 | static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) { | |
655 | _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; | |
656 | CFIndex result; | |
657 | CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length; | |
658 | if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) { | |
659 | errorCode->error = ENOMEM; | |
660 | errorCode->domain = kCFStreamErrorDomainPOSIX; | |
661 | return -1; | |
662 | } else { | |
663 | result = bufferLength; | |
664 | while (bufferLength > 0) { | |
665 | CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength; | |
666 | if (freeSpace > 0) { | |
667 | memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy); | |
668 | buffer += amountToCopy; | |
669 | bufferLength -= amountToCopy; | |
670 | dataStream->currentBuf->length += amountToCopy; | |
671 | } | |
672 | if (bufferLength > 0) { | |
673 | CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength; | |
674 | _CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0); | |
675 | newBuf->bytes = (UInt8 *)(newBuf + 1); | |
676 | newBuf->capacity = bufSize; | |
677 | newBuf->length = 0; | |
678 | newBuf->next = NULL; | |
679 | dataStream->currentBuf->next = newBuf; | |
680 | dataStream->currentBuf = newBuf; | |
681 | freeSpace = bufSize; | |
682 | } | |
683 | } | |
684 | errorCode->error = 0; | |
685 | } | |
686 | if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) { | |
687 | CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL); | |
688 | } | |
689 | return result; | |
690 | } | |
691 | ||
692 | static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) { | |
693 | _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; | |
694 | if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE; | |
695 | if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE; | |
696 | return FALSE; | |
697 | } | |
698 | ||
699 | static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) { | |
700 | _CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info; | |
701 | CFIndex size = 0; | |
702 | _CFStreamByteBuffer *buf; | |
703 | CFAllocatorRef alloc; | |
704 | UInt8 *bytes, *currByte; | |
705 | if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL; | |
706 | if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL; | |
707 | alloc = dataStream->bufferAllocator; | |
708 | for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { | |
709 | size += buf->length; | |
710 | } | |
711 | if (size == 0) return NULL; | |
712 | bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0); | |
713 | currByte = bytes; | |
714 | for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) { | |
715 | memmove(currByte, buf->bytes, buf->length); | |
716 | currByte += buf->length; | |
717 | } | |
718 | return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc); | |
719 | } | |
720 | ||
721 | static void *readDataCreate(struct _CFStream *stream, void *info) { | |
722 | _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; | |
723 | _CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0); | |
724 | if (!newCtxt) return NULL; | |
725 | newCtxt->data = (CFDataRef)CFRetain(ctxt->data); | |
726 | newCtxt->loc = CFDataGetBytePtr(newCtxt->data); | |
727 | newCtxt->scheduled = FALSE; | |
728 | return (void *)newCtxt; | |
729 | } | |
730 | ||
731 | static void readDataFinalize(struct _CFStream *stream, void *info) { | |
732 | _CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info; | |
733 | CFRelease(ctxt->data); | |
734 | CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); | |
735 | } | |
736 | ||
737 | static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) { | |
738 | return CFCopyDescription(((_CFReadDataStreamContext *)info)->data); | |
739 | } | |
740 | ||
741 | static void *writeDataCreate(struct _CFStream *stream, void *info) { | |
742 | _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; | |
743 | _CFWriteDataStreamContext *newCtxt; | |
744 | if (ctxt->bufferAllocator != kCFAllocatorNull) { | |
745 | if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault(); | |
746 | CFRetain(ctxt->bufferAllocator); | |
747 | newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0); | |
748 | newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1); | |
749 | newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1); | |
750 | newCtxt->firstBuf->capacity = BUF_SIZE; | |
751 | newCtxt->firstBuf->length = 0; | |
752 | newCtxt->firstBuf->next = NULL; | |
753 | newCtxt->currentBuf = newCtxt->firstBuf; | |
754 | newCtxt->bufferAllocator = ctxt->bufferAllocator; | |
755 | newCtxt->scheduled = FALSE; | |
756 | } else { | |
757 | newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0); | |
758 | newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1); | |
759 | newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes; | |
760 | newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity; | |
761 | newCtxt->firstBuf->length = 0; | |
762 | newCtxt->firstBuf->next = NULL; | |
763 | newCtxt->currentBuf = newCtxt->firstBuf; | |
764 | newCtxt->bufferAllocator = kCFAllocatorNull; | |
765 | newCtxt->scheduled = FALSE; | |
766 | } | |
767 | return (void *)newCtxt; | |
768 | } | |
769 | ||
770 | static void writeDataFinalize(struct _CFStream *stream, void *info) { | |
771 | _CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info; | |
772 | if (ctxt->bufferAllocator != kCFAllocatorNull) { | |
773 | _CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next; | |
774 | while (buf != NULL) { | |
775 | next = buf->next; | |
776 | CFAllocatorDeallocate(ctxt->bufferAllocator, buf); | |
777 | buf = next; | |
778 | } | |
779 | CFRelease(ctxt->bufferAllocator); | |
780 | } | |
781 | CFAllocatorDeallocate(CFGetAllocator(stream), ctxt); | |
782 | } | |
783 | ||
784 | static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) { | |
785 | return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info); | |
786 | } | |
787 | ||
788 | static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule}; | |
789 | ||
790 | static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) { | |
791 | _CFFileStreamContext fileContext; | |
792 | CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL; | |
793 | if (!scheme || !CFEqual(scheme, CFSTR("file"))) { | |
794 | if (scheme) CFRelease(scheme); | |
795 | return NULL; | |
796 | } | |
797 | CFRelease(scheme); | |
798 | fileContext.url = fileURL; | |
799 | fileContext.fd = -1; | |
800 | return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading); | |
801 | } | |
802 | ||
803 | CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { | |
804 | return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE); | |
805 | } | |
806 | ||
807 | CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) { | |
808 | return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE); | |
809 | } | |
810 | ||
811 | // CFReadStreamRef takes ownership of the fd, and will close() it | |
812 | CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { | |
813 | _CFFileStreamContext fileContext; | |
814 | fileContext.url = NULL; | |
815 | fileContext.fd = fd; | |
816 | return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE); | |
817 | } | |
818 | ||
819 | // CFWriteStreamRef takes ownership of the fd, and will close() it | |
820 | CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) { | |
821 | _CFFileStreamContext fileContext; | |
822 | fileContext.url = NULL; | |
823 | fileContext.fd = fd; | |
824 | return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE); | |
825 | } | |
826 | ||
827 | ||
828 | ||
829 | static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL}; | |
830 | static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL}; | |
831 | ||
832 | CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) { | |
833 | _CFReadDataStreamContext ctxt; | |
834 | CFReadStreamRef result; | |
835 | ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator); | |
836 | result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); | |
837 | CFRelease(ctxt.data); | |
838 | return result; | |
839 | } | |
840 | ||
841 | /* This needs to be exported to make it callable from Foundation. */ | |
842 | CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) { | |
843 | _CFReadDataStreamContext ctxt; | |
844 | CFReadStreamRef result = NULL; | |
845 | ||
846 | ctxt.data = (CFDataRef)CFRetain(data); | |
847 | result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE); | |
848 | CFRelease(data); | |
849 | return result; | |
850 | } | |
851 | ||
852 | CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) { | |
853 | _CFStreamByteBuffer buf; | |
854 | _CFWriteDataStreamContext ctxt; | |
855 | buf.bytes = buffer; | |
856 | buf.capacity = bufferCapacity; | |
857 | buf.length = 0; | |
858 | buf.next = NULL; | |
859 | ctxt.firstBuf = &buf; | |
860 | ctxt.currentBuf = ctxt.firstBuf; | |
861 | ctxt.bufferAllocator = kCFAllocatorNull; | |
862 | return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); | |
863 | } | |
864 | ||
865 | CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) { | |
866 | if (!(bufferAllocator == NULL || bufferAllocator == kCFAllocatorNull)) { | |
867 | // If there is a real bufferAllocator, then we don't allow the | |
868 | // CFStream or the contents to be allocated with one of the special | |
869 | // *GCRefZero allocators for now. | |
870 | bufferAllocator = _CFConvertAllocatorToNonGCRefZeroEquivalent(bufferAllocator); | |
871 | alloc = _CFConvertAllocatorToNonGCRefZeroEquivalent(alloc); | |
872 | } | |
873 | _CFWriteDataStreamContext ctxt; | |
874 | ctxt.firstBuf = NULL; | |
875 | ctxt.currentBuf = NULL; | |
876 | ctxt.bufferAllocator = bufferAllocator; | |
877 | return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE); | |
878 | } | |
879 | ||
880 | #undef BUF_SIZE | |
881 |