blob: 62184465a2aa49cd97f3eb2e64a536c0158e7dbc [file] [log] [blame]
/*
* Copyright (c) 2008-2009 Brent Fulgham <bfulgham@gmail.org>. All rights reserved.
*
* This source code is a modified version of the CoreFoundation sources released by Apple Inc. under
* the terms of the APSL version 2.0 (see below).
*
* For information about changes from the original Apple source release can be found by reviewing the
* source control system for the project at https://sourceforge.net/svn/?group_id=246198.
*
* The original license information is as follows:
*
* Copyright (c) 2008 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/* CFConcreteStreams.c
Copyright 2000-2002, Apple, Inc. All rights reserved.
Responsibility: Becky Willrich
*/
#define _DARWIN_UNLIMITED_SELECT 1
#include "CFStreamInternal.h"
#include "CFInternal.h"
#include "CFPriv.h"
#include <CoreFoundation/CFNumber.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_LINUX
#include <sys/time.h>
#include <unistd.h>
#elif DEPLOYMENT_TARGET_WINDOWS
#include <io.h>
#define lseek _lseek
#define open _open
#define read _read
#define write _write
#define close _close
#endif
// On Unix, you can schedule an fd with the RunLoop by creating a CFSocket around it. On Win32
// files and sockets are not interchangeable, and we do cheapo scheduling, where the file is
// always readable and writable until we hit EOF (similar to the way CFData streams are scheduled).
#if DEPLOYMENT_TARGET_MACOSX
#define REAL_FILE_SCHEDULING (1)
#endif
#define SCHEDULE_AFTER_WRITE (0)
#define SCHEDULE_AFTER_READ (1)
#define APPEND (3)
#define AT_EOF (4)
#define USE_RUNLOOP_ARRAY (5)
/* File callbacks */
typedef struct {
CFURLRef url;
int fd;
#ifdef REAL_FILE_SCHEDULING
union {
CFSocketRef sock; // socket created once we open and have an fd
CFMutableArrayRef rlArray; // scheduling information prior to open
} rlInfo; // If fd > 0, sock exists. Otherwise, rlArray.
#else
uint16_t scheduled; // ref count of how many times we've been scheduled
#endif
CFOptionFlags flags;
off_t offset;
} _CFFileStreamContext;
CONST_STRING_DECL(kCFStreamPropertyFileCurrentOffset, "kCFStreamPropertyFileCurrentOffset");
#ifdef REAL_FILE_SCHEDULING
static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info);
static void constructCFSocket(_CFFileStreamContext *fileStream, Boolean forRead, struct _CFStream *stream) {
CFSocketContext context = {0, stream, NULL, NULL, CFCopyDescription};
CFSocketRef sock = CFSocketCreateWithNative(CFGetAllocator(stream), fileStream->fd, forRead ? kCFSocketReadCallBack : kCFSocketWriteCallBack, fileCallBack, &context);
CFSocketSetSocketFlags(sock, 0);
if (fileStream->rlInfo.rlArray) {
CFIndex i, c = CFArrayGetCount(fileStream->rlInfo.rlArray);
CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(CFGetAllocator(stream), sock, 0);
for (i = 0; i+1 < c; i += 2) {
CFRunLoopRef rl = (CFRunLoopRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i);
CFStringRef mode = (CFStringRef)CFArrayGetValueAtIndex(fileStream->rlInfo.rlArray, i+1);
CFRunLoopAddSource(rl, src, mode);
}
CFRelease(fileStream->rlInfo.rlArray);
CFRelease(src);
}
fileStream->rlInfo.sock = sock;
}
#endif
static Boolean constructFD(_CFFileStreamContext *fileStream, CFStreamError *error, Boolean forRead, struct _CFStream *stream) {
UInt8 path[1024];
int flags = forRead ? O_RDONLY : (O_CREAT | O_TRUNC | O_WRONLY);
if (CFURLGetFileSystemRepresentation(fileStream->url, TRUE, path, 1024) == FALSE) {
error->error = ENOENT;
error->domain = kCFStreamErrorDomainPOSIX;
return FALSE;
}
if (__CFBitIsSet(fileStream->flags, APPEND)) {
flags |= O_APPEND;
if(_CFExecutableLinkedOnOrAfter(CFSystemVersionPanther)) flags &= ~O_TRUNC;
}
do {
fileStream->fd = open((const char *)path, flags, 0666);
if (fileStream->fd < 0)
break;
if ((fileStream->offset != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1))
break;
#ifdef REAL_FILE_SCHEDULING
if (fileStream->rlInfo.rlArray != NULL) {
constructCFSocket(fileStream, forRead, stream);
}
#endif
return TRUE;
} while (1);
__CFBitSet(fileStream->flags, USE_RUNLOOP_ARRAY);
error->error = errno;
error->domain = kCFStreamErrorDomainPOSIX;
return FALSE;
}
static Boolean fileOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
Boolean forRead = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
*openComplete = TRUE;
if (ctxt->url) {
if (constructFD(ctxt, errorCode, forRead, stream)) {
#ifndef REAL_FILE_SCHEDULING
if (ctxt->scheduled > 0) {
if (forRead)
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
}
#endif
return TRUE;
} else {
return FALSE;
}
#ifdef REAL_FILE_SCHEDULING
} else if (ctxt->rlInfo.rlArray != NULL) {
constructCFSocket(ctxt, forRead, stream);
#endif
}
return TRUE;
}
__private_extern__ CFIndex fdRead(int fd, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF) {
CFIndex bytesRead = read(fd, buffer, bufferLength);
if (bytesRead < 0) {
errorCode->error = errno;
errorCode->domain = kCFStreamErrorDomainPOSIX;
return -1;
} else {
*atEOF = (bytesRead == 0) ? TRUE : FALSE;
errorCode->error = 0;
return bytesRead;
}
}
static CFIndex fileRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, Boolean *atEOF, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
CFIndex result;
result = fdRead(ctxt->fd, buffer, bufferLength, errorCode, atEOF);
#ifdef REAL_FILE_SCHEDULING
if (__CFBitIsSet(ctxt->flags, SCHEDULE_AFTER_READ)) {
__CFBitClear(ctxt->flags, SCHEDULE_AFTER_READ);
if (ctxt->rlInfo.sock) {
CFSocketEnableCallBacks(ctxt->rlInfo.sock, kCFSocketReadCallBack);
}
}
#else
if (*atEOF)
__CFBitSet(ctxt->flags, AT_EOF);
if (ctxt->scheduled > 0 && !*atEOF) {
CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
}
#endif
return result;
}
#ifdef REAL_FILE_SCHEDULING
__private_extern__ Boolean fdCanRead(int fd) {
struct timeval timeout = {0, 0};
fd_set *readSetPtr;
fd_set readSet;
Boolean result;
// fd_set is not a mask in Win32, so checking for an fd that's too big is not relevant
if (fd < FD_SETSIZE) {
FD_ZERO(&readSet);
readSetPtr = &readSet;
} else {
int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t);
uint32_t *fds_bits = (uint32_t *)malloc(size);
memset(fds_bits, 0, size);
readSetPtr = (fd_set *)fds_bits;
}
FD_SET(fd, readSetPtr);
result = (select(fd + 1, readSetPtr, NULL, NULL, &timeout) == 1) ? TRUE : FALSE;
if (readSetPtr != &readSet) {
free(readSetPtr);
}
return result;
}
#endif
static Boolean fileCanRead(CFReadStreamRef stream, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
#ifdef REAL_FILE_SCHEDULING
return fdCanRead(ctxt->fd);
#else
return !__CFBitIsSet(ctxt->flags, AT_EOF);
#endif
}
__private_extern__ CFIndex fdWrite(int fd, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode) {
CFIndex bytesWritten = write(fd, buffer, bufferLength);
if (bytesWritten < 0) {
errorCode->error = errno;
errorCode->domain = kCFStreamErrorDomainPOSIX;
return -1;
} else {
errorCode->error = 0;
return bytesWritten;
}
}
static CFIndex fileWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) {
_CFFileStreamContext *fileStream = ((_CFFileStreamContext *)info);
CFIndex result = fdWrite(fileStream->fd, buffer, bufferLength, errorCode);
#ifdef REAL_FILE_SCHEDULING
if (__CFBitIsSet(fileStream->flags, SCHEDULE_AFTER_WRITE)) {
__CFBitClear(fileStream->flags, SCHEDULE_AFTER_WRITE);
if (fileStream->rlInfo.sock) {
CFSocketEnableCallBacks(fileStream->rlInfo.sock, kCFSocketWriteCallBack);
}
}
#else
if (fileStream->scheduled > 0) {
CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
}
#endif
return result;
}
#ifdef REAL_FILE_SCHEDULING
__private_extern__ Boolean fdCanWrite(int fd) {
struct timeval timeout = {0, 0};
fd_set *writeSetPtr;
fd_set writeSet;
Boolean result;
if (fd < FD_SETSIZE) {
FD_ZERO(&writeSet);
writeSetPtr = &writeSet;
} else {
int size = howmany(fd+1, NFDBITS) * sizeof(uint32_t);
uint32_t *fds_bits = (uint32_t *)malloc(size);
memset(fds_bits, 0, size);
writeSetPtr = (fd_set *)fds_bits;
}
FD_SET(fd, writeSetPtr);
result = (select(fd + 1, NULL, writeSetPtr, NULL, &timeout) == 1) ? TRUE : FALSE;
if (writeSetPtr != &writeSet) {
free(writeSetPtr);
}
return result;
}
#endif
static Boolean fileCanWrite(CFWriteStreamRef stream, void *info) {
#ifdef REAL_FILE_SCHEDULING
return fdCanWrite(((_CFFileStreamContext *)info)->fd);
#else
return TRUE;
#endif
}
static void fileClose(struct _CFStream *stream, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
if (ctxt->fd >= 0) {
close(ctxt->fd);
ctxt->fd = -1;
#ifdef REAL_FILE_SCHEDULING
if (ctxt->rlInfo.sock) {
CFSocketInvalidate(ctxt->rlInfo.sock);
CFRelease(ctxt->rlInfo.sock);
ctxt->rlInfo.sock = NULL;
}
} else if (ctxt->rlInfo.rlArray) {
CFRelease(ctxt->rlInfo.rlArray);
ctxt->rlInfo.rlArray = NULL;
#endif
}
}
#ifdef REAL_FILE_SCHEDULING
static void fileCallBack(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
struct _CFStream *stream = (struct _CFStream *)info;
Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
_CFFileStreamContext *fileStream = (_CFFileStreamContext *)((isReadStream) ? CFReadStreamGetInfoPointer((CFReadStreamRef)stream) : CFWriteStreamGetInfoPointer((CFWriteStreamRef)stream));
if (type == kCFSocketWriteCallBack) {
__CFBitSet(fileStream->flags, SCHEDULE_AFTER_WRITE);
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
} else {
// type == kCFSocketReadCallBack
__CFBitSet(fileStream->flags, SCHEDULE_AFTER_READ);
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
}
}
#endif
static void fileSchedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream);
if (fileStream->fd < 0 && status != kCFStreamStatusNotOpen) {
// Stream's already closed or error-ed out
return;
}
#ifdef REAL_FILE_SCHEDULING
if (status == kCFStreamStatusNotOpen) {
if (!fileStream->rlInfo.rlArray) {
fileStream->rlInfo.rlArray = CFArrayCreateMutable(CFGetAllocator(stream), 0, &kCFTypeArrayCallBacks);
}
CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoop);
CFArrayAppendValue(fileStream->rlInfo.rlArray, runLoopMode);
} else {
CFRunLoopSourceRef rlSrc;
if (!fileStream->rlInfo.sock) {
constructCFSocket(fileStream, isReadStream, stream);
}
rlSrc = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
CFRunLoopAddSource(runLoop, rlSrc, runLoopMode);
CFRelease(rlSrc);
}
#else
fileStream->scheduled++;
if (fileStream->scheduled == 1 && fileStream->fd > 0 && status == kCFStreamStatusOpen) {
if (isReadStream)
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
else
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
}
#endif
}
static void fileUnschedule(struct _CFStream *stream, CFRunLoopRef runLoop, CFStringRef runLoopMode, void *info) {
_CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
#ifdef REAL_FILE_SCHEDULING
Boolean isReadStream = (CFGetTypeID(stream) == CFReadStreamGetTypeID());
CFStreamStatus status = isReadStream ? CFReadStreamGetStatus((CFReadStreamRef)stream) : CFWriteStreamGetStatus((CFWriteStreamRef)stream);
if (status == kCFStreamStatusNotOpen) {
// Not opened yet
if (fileStream->rlInfo.rlArray) {
CFMutableArrayRef runloops = fileStream->rlInfo.rlArray;
CFIndex i, c;
for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) {
if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) {
CFArrayRemoveValueAtIndex(runloops, i);
CFArrayRemoveValueAtIndex(runloops, i);
break;
}
}
}
} else if (fileStream->rlInfo.sock) {
if (__CFBitIsSet(fileStream->flags, USE_RUNLOOP_ARRAY)) {
// we know that fileStream->rlInfo.rlArray is non-NULL because it is in a union with fileStream->rlInfo.sock
CFMutableArrayRef runloops = fileStream->rlInfo.rlArray;
CFIndex i, c;
for (i = 0, c = CFArrayGetCount(runloops); i+1 < c; i += 2) {
if (CFEqual(CFArrayGetValueAtIndex(runloops, i), runLoop) && CFEqual(CFArrayGetValueAtIndex(runloops, i+1), runLoopMode)) {
CFArrayRemoveValueAtIndex(runloops, i);
CFArrayRemoveValueAtIndex(runloops, i);
break;
}
}
} else {
CFRunLoopSourceRef sockSource = CFSocketCreateRunLoopSource(CFGetAllocator(stream), fileStream->rlInfo.sock, 0);
CFRunLoopRemoveSource(runLoop, sockSource, runLoopMode);
CFRelease(sockSource);
}
}
#else
if (fileStream->scheduled > 0)
fileStream->scheduled--;
#endif
}
static CFTypeRef fileCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
CFTypeRef result = NULL;
_CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
if (CFEqual(propertyName, kCFStreamPropertyFileCurrentOffset)) {
// NOTE that this does a lseek of 0 from the current location in
// order to populate the offset field which will then be used to
// create the resulting value.
if (!__CFBitIsSet(fileStream->flags, APPEND) && fileStream->fd != -1) {
fileStream->offset = lseek(fileStream->fd, 0, SEEK_CUR);
}
if (fileStream->offset != -1) {
result = CFNumberCreate(CFGetAllocator((CFTypeRef)stream), kCFNumberSInt64Type, &(fileStream->offset));
}
}
return result;
}
static Boolean fileSetProperty(struct _CFStream *stream, CFStringRef prop, CFTypeRef val, void *info) {
Boolean result = FALSE;
_CFFileStreamContext *fileStream = (_CFFileStreamContext *)info;
if (CFEqual(prop, kCFStreamPropertyAppendToFile) && CFGetTypeID(stream) == CFWriteStreamGetTypeID() &&
CFWriteStreamGetStatus((CFWriteStreamRef)stream) == kCFStreamStatusNotOpen)
{
if (val == kCFBooleanTrue) {
__CFBitSet(fileStream->flags, APPEND);
fileStream->offset = -1; // Can't offset and append on the stream
} else {
__CFBitClear(fileStream->flags, APPEND);
}
result = TRUE;
}
else if (CFEqual(prop, kCFStreamPropertyFileCurrentOffset)) {
if (!__CFBitIsSet(fileStream->flags, APPEND))
{
result = CFNumberGetValue((CFNumberRef)val, kCFNumberSInt64Type, &(fileStream->offset));
}
if ((fileStream->fd != -1) && (lseek(fileStream->fd, fileStream->offset, SEEK_SET) == -1)) {
result = FALSE;
}
}
return result;
}
static void *fileCreate(struct _CFStream *stream, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
_CFFileStreamContext *newCtxt = (_CFFileStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFFileStreamContext), 0);
if (!newCtxt) return NULL;
newCtxt->url = ctxt->url;
if (newCtxt->url) {
CFRetain(newCtxt->url);
}
newCtxt->fd = ctxt->fd;
#ifdef REAL_FILE_SCHEDULING
newCtxt->rlInfo.sock = NULL;
#else
newCtxt->scheduled = 0;
#endif
newCtxt->flags = 0;
newCtxt->offset = -1;
return newCtxt;
}
static void fileFinalize(struct _CFStream *stream, void *info) {
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
if (ctxt->fd > 0) {
#ifdef REAL_FILE_SCHEDULING
if (ctxt->rlInfo.sock) {
CFSocketInvalidate(ctxt->rlInfo.sock);
CFRelease(ctxt->rlInfo.sock);
}
#endif
close(ctxt->fd);
#ifdef REAL_FILE_SCHEDULING
} else if (ctxt->rlInfo.rlArray) {
CFRelease(ctxt->rlInfo.rlArray);
#endif
}
if (ctxt->url) {
CFRelease(ctxt->url);
}
CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
}
static CFStringRef fileCopyDescription(struct _CFStream *stream, void *info) {
// This needs work
_CFFileStreamContext *ctxt = (_CFFileStreamContext *)info;
if (ctxt->url) {
return CFCopyDescription(ctxt->url);
} else {
return CFStringCreateWithFormat(CFGetAllocator(stream), NULL, CFSTR("fd = %d"), ctxt->fd);
}
}
/* CFData stream callbacks */
typedef struct {
CFDataRef data; // Mutable if the stream was constructed writable
const UInt8 *loc; // Current location in the file
Boolean scheduled;
char _padding[3];
} _CFReadDataStreamContext;
#define BUF_SIZE 1024
typedef struct _CFStreamByteBuffer {
UInt8 *bytes;
CFIndex capacity, length;
struct _CFStreamByteBuffer *next;
} _CFStreamByteBuffer;
typedef struct {
_CFStreamByteBuffer *firstBuf, *currentBuf;
CFAllocatorRef bufferAllocator;
Boolean scheduled;
char _padding[3];
} _CFWriteDataStreamContext;
static Boolean readDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
_CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info;
if (dataStream->scheduled) {
if (CFDataGetLength(dataStream->data) != 0) {
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
} else {
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
}
}
errorCode->error = 0;
*openComplete = TRUE;
return TRUE;
}
static void readDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) {
_CFReadDataStreamContext *dataStream = (_CFReadDataStreamContext *)info;
if (dataStream->scheduled == FALSE) {
dataStream->scheduled = TRUE;
if (CFReadStreamGetStatus((CFReadStreamRef)stream) != kCFStreamStatusOpen)
return;
if (CFDataGetBytePtr(dataStream->data) + CFDataGetLength(dataStream->data) > dataStream->loc) {
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventHasBytesAvailable, NULL);
} else {
CFReadStreamSignalEvent((CFReadStreamRef)stream, kCFStreamEventEndEncountered, NULL);
}
}
}
static CFIndex dataRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength, CFStreamError *error, Boolean *atEOF, void *info) {
_CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
const UInt8 *bytePtr = CFDataGetBytePtr(dataCtxt->data);
CFIndex length = CFDataGetLength(dataCtxt->data);
CFIndex bytesToCopy = bytePtr + length - dataCtxt->loc;
if (bytesToCopy > bufferLength) {
bytesToCopy = bufferLength;
}
if (bytesToCopy < 0) {
bytesToCopy = 0;
}
if (bytesToCopy != 0) {
memmove(buffer, dataCtxt->loc, bytesToCopy);
dataCtxt->loc += bytesToCopy;
}
error->error = 0;
*atEOF = (dataCtxt->loc < bytePtr + length) ? FALSE : TRUE;
if (dataCtxt->scheduled && !*atEOF) {
CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
}
return bytesToCopy;
}
static const UInt8 *dataGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead, CFStreamError *error, Boolean *atEOF, void *info) {
_CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
const UInt8 *bytes = CFDataGetBytePtr(dataCtxt->data);
if (dataCtxt->loc - bytes > maxBytesToRead) {
*numBytesRead = maxBytesToRead;
*atEOF = FALSE;
} else {
*numBytesRead = dataCtxt->loc - bytes;
*atEOF = TRUE;
}
error->error = 0;
bytes = dataCtxt->loc;
dataCtxt->loc += *numBytesRead;
if (dataCtxt->scheduled && !*atEOF) {
CFReadStreamSignalEvent(stream, kCFStreamEventHasBytesAvailable, NULL);
}
return bytes;
}
static Boolean dataCanRead(CFReadStreamRef stream, void *info) {
_CFReadDataStreamContext *dataCtxt = (_CFReadDataStreamContext *)info;
return (CFDataGetBytePtr(dataCtxt->data) + CFDataGetLength(dataCtxt->data) > dataCtxt->loc) ? TRUE : FALSE;
}
static Boolean writeDataOpen(struct _CFStream *stream, CFStreamError *errorCode, Boolean *openComplete, void *info) {
_CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
if (dataStream->scheduled) {
if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) {
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
} else {
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
}
}
errorCode->error = 0;
*openComplete = TRUE;
return TRUE;
}
static void writeDataSchedule(struct _CFStream *stream, CFRunLoopRef rl, CFStringRef rlMode, void *info) {
_CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
if (dataStream->scheduled == FALSE) {
dataStream->scheduled = TRUE;
if (CFWriteStreamGetStatus((CFWriteStreamRef)stream) != kCFStreamStatusOpen)
return;
if (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length) {
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventCanAcceptBytes, NULL);
} else {
CFWriteStreamSignalEvent((CFWriteStreamRef)stream, kCFStreamEventEndEncountered, NULL);
}
}
}
static CFIndex dataWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength, CFStreamError *errorCode, void *info) {
_CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
CFIndex result;
CFIndex freeSpace = dataStream->currentBuf->capacity - dataStream->currentBuf->length;
if (dataStream->bufferAllocator == kCFAllocatorNull && bufferLength > freeSpace) {
errorCode->error = ENOMEM;
errorCode->domain = kCFStreamErrorDomainPOSIX;
return -1;
} else {
result = bufferLength;
while (bufferLength > 0) {
CFIndex amountToCopy = (bufferLength > freeSpace) ? freeSpace : bufferLength;
if (freeSpace > 0) {
memmove(dataStream->currentBuf->bytes + dataStream->currentBuf->length, buffer, amountToCopy);
buffer += amountToCopy;
bufferLength -= amountToCopy;
dataStream->currentBuf->length += amountToCopy;
}
if (bufferLength > 0) {
CFIndex bufSize = BUF_SIZE > bufferLength ? BUF_SIZE : bufferLength;
_CFStreamByteBuffer *newBuf = (_CFStreamByteBuffer *)CFAllocatorAllocate(dataStream->bufferAllocator, sizeof(_CFStreamByteBuffer) + bufSize, 0);
newBuf->bytes = (UInt8 *)(newBuf + 1);
newBuf->capacity = bufSize;
newBuf->length = 0;
newBuf->next = NULL;
dataStream->currentBuf->next = newBuf;
dataStream->currentBuf = newBuf;
freeSpace = bufSize;
}
}
errorCode->error = 0;
}
if (dataStream->scheduled && (dataStream->bufferAllocator != kCFAllocatorNull || dataStream->currentBuf->capacity > dataStream->currentBuf->length)) {
CFWriteStreamSignalEvent(stream, kCFStreamEventCanAcceptBytes, NULL);
}
return result;
}
static Boolean dataCanWrite(CFWriteStreamRef stream, void *info) {
_CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
if (dataStream->bufferAllocator != kCFAllocatorNull) return TRUE;
if (dataStream->currentBuf->capacity > dataStream->currentBuf->length) return TRUE;
return FALSE;
}
static CFPropertyListRef dataCopyProperty(struct _CFStream *stream, CFStringRef propertyName, void *info) {
_CFWriteDataStreamContext *dataStream = (_CFWriteDataStreamContext *)info;
CFIndex size = 0;
_CFStreamByteBuffer *buf;
CFAllocatorRef alloc;
UInt8 *bytes, *currByte;
if (!CFEqual(propertyName, kCFStreamPropertyDataWritten)) return NULL;
if (dataStream->bufferAllocator == kCFAllocatorNull) return NULL;
alloc = dataStream->bufferAllocator;
for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
size += buf->length;
}
if (size == 0) return NULL;
bytes = (UInt8 *)CFAllocatorAllocate(alloc, size, 0);
currByte = bytes;
for (buf = dataStream->firstBuf; buf != NULL; buf = buf->next) {
memmove(currByte, buf->bytes, buf->length);
currByte += buf->length;
}
return CFDataCreateWithBytesNoCopy(alloc, bytes, size, alloc);
}
static void *readDataCreate(struct _CFStream *stream, void *info) {
_CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
_CFReadDataStreamContext *newCtxt = (_CFReadDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFReadDataStreamContext), 0);
if (!newCtxt) return NULL;
newCtxt->data = (CFDataRef)CFRetain(ctxt->data);
newCtxt->loc = CFDataGetBytePtr(newCtxt->data);
newCtxt->scheduled = FALSE;
return (void *)newCtxt;
}
static void readDataFinalize(struct _CFStream *stream, void *info) {
_CFReadDataStreamContext *ctxt = (_CFReadDataStreamContext *)info;
CFRelease(ctxt->data);
CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
}
static CFStringRef readDataCopyDescription(struct _CFStream *stream, void *info) {
return CFCopyDescription(((_CFReadDataStreamContext *)info)->data);
}
static void *writeDataCreate(struct _CFStream *stream, void *info) {
_CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
_CFWriteDataStreamContext *newCtxt;
if (ctxt->bufferAllocator != kCFAllocatorNull) {
if (ctxt->bufferAllocator == NULL) ctxt->bufferAllocator = CFAllocatorGetDefault();
CFRetain(ctxt->bufferAllocator);
newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer) + BUF_SIZE, 0);
newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt + 1);
newCtxt->firstBuf->bytes = (UInt8 *)(newCtxt->firstBuf + 1);
newCtxt->firstBuf->capacity = BUF_SIZE;
newCtxt->firstBuf->length = 0;
newCtxt->firstBuf->next = NULL;
newCtxt->currentBuf = newCtxt->firstBuf;
newCtxt->bufferAllocator = ctxt->bufferAllocator;
newCtxt->scheduled = FALSE;
} else {
newCtxt = (_CFWriteDataStreamContext *)CFAllocatorAllocate(CFGetAllocator(stream), sizeof(_CFWriteDataStreamContext) + sizeof(_CFStreamByteBuffer), 0);
newCtxt->firstBuf = (_CFStreamByteBuffer *)(newCtxt+1);
newCtxt->firstBuf->bytes = ctxt->firstBuf->bytes;
newCtxt->firstBuf->capacity = ctxt->firstBuf->capacity;
newCtxt->firstBuf->length = 0;
newCtxt->firstBuf->next = NULL;
newCtxt->currentBuf = newCtxt->firstBuf;
newCtxt->bufferAllocator = kCFAllocatorNull;
newCtxt->scheduled = FALSE;
}
return (void *)newCtxt;
}
static void writeDataFinalize(struct _CFStream *stream, void *info) {
_CFWriteDataStreamContext *ctxt = (_CFWriteDataStreamContext *)info;
if (ctxt->bufferAllocator != kCFAllocatorNull) {
_CFStreamByteBuffer *buf = ctxt->firstBuf->next, *next;
while (buf != NULL) {
next = buf->next;
CFAllocatorDeallocate(ctxt->bufferAllocator, buf);
buf = next;
}
CFRelease(ctxt->bufferAllocator);
}
CFAllocatorDeallocate(CFGetAllocator(stream), ctxt);
}
static CFStringRef writeDataCopyDescription(struct _CFStream *stream, void *info) {
return CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFWriteDataContext %p>"), info);
}
static const struct _CFStreamCallBacksV1 fileCallBacks = {1, fileCreate, fileFinalize, fileCopyDescription, fileOpen, NULL, fileRead, NULL, fileCanRead, fileWrite, fileCanWrite, fileClose, fileCopyProperty, fileSetProperty, NULL, fileSchedule, fileUnschedule};
static struct _CFStream *_CFStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL, Boolean forReading) {
_CFFileStreamContext fileContext;
CFStringRef scheme = fileURL ? CFURLCopyScheme(fileURL) : NULL;
if (!scheme || !CFEqual(scheme, CFSTR("file"))) {
if (scheme) CFRelease(scheme);
return NULL;
}
CFRelease(scheme);
fileContext.url = fileURL;
fileContext.fd = -1;
return _CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), forReading);
}
CF_EXPORT CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
return (CFReadStreamRef)_CFStreamCreateWithFile(alloc, fileURL, TRUE);
}
CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL) {
return (CFWriteStreamRef)_CFStreamCreateWithFile(alloc, fileURL, FALSE);
}
CFReadStreamRef _CFReadStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
_CFFileStreamContext fileContext;
fileContext.url = NULL;
fileContext.fd = fd;
return (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), TRUE);
}
CFWriteStreamRef _CFWriteStreamCreateFromFileDescriptor(CFAllocatorRef alloc, int fd) {
_CFFileStreamContext fileContext;
fileContext.url = NULL;
fileContext.fd = fd;
return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &fileContext, (struct _CFStreamCallBacks *)(&fileCallBacks), FALSE);
}
static const struct _CFStreamCallBacksV1 readDataCallBacks = {1, readDataCreate, readDataFinalize, readDataCopyDescription, readDataOpen, NULL, dataRead, dataGetBuffer, dataCanRead, NULL, NULL, NULL, NULL, NULL, NULL, readDataSchedule, NULL};
static const struct _CFStreamCallBacksV1 writeDataCallBacks = {1, writeDataCreate, writeDataFinalize, writeDataCopyDescription, writeDataOpen, NULL, NULL, NULL, NULL, dataWrite, dataCanWrite, NULL, dataCopyProperty, NULL, NULL, writeDataSchedule, NULL};
CF_EXPORT CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator) {
_CFReadDataStreamContext ctxt;
CFReadStreamRef result;
ctxt.data = CFDataCreateWithBytesNoCopy(alloc, bytes, length, bytesDeallocator);
result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
CFRelease(ctxt.data);
return result;
}
/* This needs to be exported to make it callable from Foundation. */
CF_EXPORT CFReadStreamRef CFReadStreamCreateWithData(CFAllocatorRef alloc, CFDataRef data) {
_CFReadDataStreamContext ctxt;
CFReadStreamRef result = NULL;
ctxt.data = (CFDataRef)CFRetain(data);
result = (CFReadStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&readDataCallBacks), TRUE);
CFRelease(data);
return result;
}
CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity) {
_CFStreamByteBuffer buf;
_CFWriteDataStreamContext ctxt;
buf.bytes = buffer;
buf.capacity = bufferCapacity;
buf.length = 0;
buf.next = NULL;
ctxt.firstBuf = &buf;
ctxt.currentBuf = ctxt.firstBuf;
ctxt.bufferAllocator = kCFAllocatorNull;
return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
}
CF_EXPORT CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator) {
_CFWriteDataStreamContext ctxt;
ctxt.firstBuf = NULL;
ctxt.currentBuf = NULL;
ctxt.bufferAllocator = bufferAllocator;
return (CFWriteStreamRef)_CFStreamCreateWithConstantCallbacks(alloc, &ctxt, (struct _CFStreamCallBacks *)(&writeDataCallBacks), FALSE);
}
#undef BUF_SIZE