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