/*	CFStream.h
	Copyright (c) 2000-2017, Apple Inc. and the Swift project authors
 
	Portions Copyright (c) 2014-2017, Apple Inc. and the Swift project authors
	Licensed under Apache License v2.0 with Runtime Library Exception
	See http://swift.org/LICENSE.txt for license information
	See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
*/

#if !defined(__COREFOUNDATION_CFSTREAM__)
#define __COREFOUNDATION_CFSTREAM__ 1

#include <CoreFoundation/CFBase.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFURL.h>
#include <CoreFoundation/CFRunLoop.h>
#include <CoreFoundation/CFSocket.h>
#include <CoreFoundation/CFError.h>
#include <dispatch/dispatch.h>

CF_IMPLICIT_BRIDGING_ENABLED
CF_EXTERN_C_BEGIN

typedef CFStringRef CFStreamPropertyKey CF_EXTENSIBLE_STRING_ENUM;

typedef CF_ENUM(CFIndex, CFStreamStatus) {
    kCFStreamStatusNotOpen = 0,
    kCFStreamStatusOpening,  /* open is in-progress */
    kCFStreamStatusOpen,
    kCFStreamStatusReading,
    kCFStreamStatusWriting,
    kCFStreamStatusAtEnd,    /* no further bytes can be read/written */
    kCFStreamStatusClosed,
    kCFStreamStatusError
};

typedef CF_OPTIONS(CFOptionFlags, CFStreamEventType) {
    kCFStreamEventNone = 0,
    kCFStreamEventOpenCompleted = 1,
    kCFStreamEventHasBytesAvailable = 2,
    kCFStreamEventCanAcceptBytes = 4, 
    kCFStreamEventErrorOccurred = 8,
    kCFStreamEventEndEncountered = 16
};

typedef struct {
    CFIndex version;
    void *info;
    void *(*retain)(void *info);
    void (*release)(void *info);
    CFStringRef (*copyDescription)(void *info);
} CFStreamClientContext;

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSInputStream) __CFReadStream * CFReadStreamRef;
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSOutputStream) __CFWriteStream * CFWriteStreamRef;

typedef void (*CFReadStreamClientCallBack)(CFReadStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);
typedef void (*CFWriteStreamClientCallBack)(CFWriteStreamRef stream, CFStreamEventType type, void *clientCallBackInfo);

CF_EXPORT
CFTypeID CFReadStreamGetTypeID(void);
CF_EXPORT
CFTypeID CFWriteStreamGetTypeID(void);

/* Memory streams */

/* Value will be a CFData containing all bytes thusfar written; used to recover the data written to a memory write stream. */
CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertyDataWritten;

/* Pass kCFAllocatorNull for bytesDeallocator to prevent CFReadStream from deallocating bytes; otherwise, CFReadStream will deallocate bytes when the stream is destroyed */
CF_EXPORT
CFReadStreamRef CFReadStreamCreateWithBytesNoCopy(CFAllocatorRef alloc, const UInt8 *bytes, CFIndex length, CFAllocatorRef bytesDeallocator);

/* The stream writes into the buffer given; when bufferCapacity is exhausted, the stream is exhausted (status becomes kCFStreamStatusAtEnd) */
CF_EXPORT
CFWriteStreamRef CFWriteStreamCreateWithBuffer(CFAllocatorRef alloc, UInt8 *buffer, CFIndex bufferCapacity);

/* New buffers are allocated from bufferAllocator as bytes are written to the stream.  At any point, you can recover the bytes thusfar written by asking for the property kCFStreamPropertyDataWritten, above */
CF_EXPORT
CFWriteStreamRef CFWriteStreamCreateWithAllocatedBuffers(CFAllocatorRef alloc, CFAllocatorRef bufferAllocator);

/* File streams */
CF_EXPORT
CFReadStreamRef CFReadStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL);
CF_EXPORT
CFWriteStreamRef CFWriteStreamCreateWithFile(CFAllocatorRef alloc, CFURLRef fileURL);
CF_IMPLICIT_BRIDGING_DISABLED
CF_EXPORT
void CFStreamCreateBoundPair(CFAllocatorRef alloc, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream, CFIndex transferBufferSize);
CF_IMPLICIT_BRIDGING_ENABLED

/* Property for file write streams; value should be a CFBoolean.  Set to TRUE to append to a file, rather than to replace its contents */
CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertyAppendToFile;

CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertyFileCurrentOffset;   // Value is a CFNumber


/* Socket stream properties */

/* Value will be a CFData containing the native handle */
CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertySocketNativeHandle;

/* Value will be a CFString, or NULL if unknown */
CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertySocketRemoteHostName;

/* Value will be a CFNumber, or NULL if unknown */
CF_EXPORT
const CFStreamPropertyKey kCFStreamPropertySocketRemotePortNumber;

CF_IMPLICIT_BRIDGING_DISABLED
/* Socket streams; the returned streams are paired such that they use the same socket; pass NULL if you want only the read stream or the write stream */
CF_EXPORT
void CFStreamCreatePairWithSocket(CFAllocatorRef alloc, CFSocketNativeHandle sock, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
CF_EXPORT
void CFStreamCreatePairWithSocketToHost(CFAllocatorRef alloc, CFStringRef host, UInt32 port, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
CF_EXPORT
void CFStreamCreatePairWithPeerSocketSignature(CFAllocatorRef alloc, const CFSocketSignature *signature, CFReadStreamRef *readStream, CFWriteStreamRef *writeStream);
CF_IMPLICIT_BRIDGING_ENABLED


/* Returns the current state of the stream */
CF_EXPORT
CFStreamStatus CFReadStreamGetStatus(CFReadStreamRef stream);
CF_EXPORT
CFStreamStatus CFWriteStreamGetStatus(CFWriteStreamRef stream);

/* Returns NULL if no error has occurred; otherwise returns the error. */
CF_EXPORT
CFErrorRef CFReadStreamCopyError(CFReadStreamRef stream) API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
CF_EXPORT
CFErrorRef CFWriteStreamCopyError(CFWriteStreamRef stream) API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));

/* Returns success/failure.  Opening a stream causes it to reserve all the system
   resources it requires.  If the stream can open non-blocking, this will always 
   return TRUE; listen to the run loop source to find out when the open completes
   and whether it was successful, or poll using CFRead/WriteStreamGetStatus(), waiting 
   for a status of kCFStreamStatusOpen or kCFStreamStatusError.  */
CF_EXPORT
Boolean CFReadStreamOpen(CFReadStreamRef stream);
CF_EXPORT
Boolean CFWriteStreamOpen(CFWriteStreamRef stream);

/* Terminates the flow of bytes; releases any system resources required by the 
   stream.  The stream may not fail to close.  You may call CFStreamClose() to 
   effectively abort a stream. */
CF_EXPORT
void CFReadStreamClose(CFReadStreamRef stream);
CF_EXPORT
void CFWriteStreamClose(CFWriteStreamRef stream);

/* Whether there is data currently available for reading; returns TRUE if it's 
   impossible to tell without trying */
CF_EXPORT
Boolean CFReadStreamHasBytesAvailable(CFReadStreamRef stream);

/* Returns the number of bytes read, or -1 if an error occurs preventing any 
   bytes from being read, or 0 if the stream's end was encountered.  
   It is an error to try and read from a stream that hasn't been opened first.  
   This call will block until at least one byte is available; it will NOT block
   until the entire buffer can be filled.  To avoid blocking, either poll using
   CFReadStreamHasBytesAvailable() or use the run loop and listen for the 
   kCFStreamEventHasBytesAvailable event for notification of data available. */
CF_EXPORT
CFIndex CFReadStreamRead(CFReadStreamRef stream, UInt8 *buffer, CFIndex bufferLength);

/* Returns a pointer to an internal buffer if possible (setting *numBytesRead
   to the length of the returned buffer), otherwise returns NULL; guaranteed
   to return in O(1).  Bytes returned in the buffer are considered read from
   the stream; if maxBytesToRead is greater than 0, not more than maxBytesToRead
   will be returned.  If maxBytesToRead is less than or equal to zero, as many bytes
   as are readily available will be returned.  The returned buffer is good only
   until the next stream operation called on the stream.  Caller should neither
   change the contents of the returned buffer nor attempt to deallocate the buffer;
   it is still owned by the stream. */
CF_EXPORT
const UInt8 *CFReadStreamGetBuffer(CFReadStreamRef stream, CFIndex maxBytesToRead, CFIndex *numBytesRead);

/* Whether the stream can currently be written to without blocking;
   returns TRUE if it's impossible to tell without trying */
CF_EXPORT
Boolean CFWriteStreamCanAcceptBytes(CFWriteStreamRef stream);

/* Returns the number of bytes successfully written, -1 if an error has
   occurred, or 0 if the stream has been filled to capacity (for fixed-length
   streams).  If the stream is not full, this call will block until at least
   one byte is written.  To avoid blocking, either poll via CFWriteStreamCanAcceptBytes
   or use the run loop and listen for the kCFStreamEventCanAcceptBytes event. */
CF_EXPORT
CFIndex CFWriteStreamWrite(CFWriteStreamRef stream, const UInt8 *buffer, CFIndex bufferLength);

/* Particular streams can name properties and assign meanings to them; you
   access these properties through the following calls.  A property is any interesting
   information about the stream other than the data being transmitted itself.
   Examples include the headers from an HTTP transmission, or the expected 
   number of bytes, or permission information, etc.  Properties that can be set
   configure the behavior of the stream, and may only be settable at particular times
   (like before the stream has been opened).  See the documentation for particular 
   properties to determine their get- and set-ability. */
CF_EXPORT
CFTypeRef CFReadStreamCopyProperty(CFReadStreamRef stream, CFStreamPropertyKey propertyName);
CF_EXPORT
CFTypeRef CFWriteStreamCopyProperty(CFWriteStreamRef stream, CFStreamPropertyKey propertyName);

/* Returns TRUE if the stream recognizes and accepts the given property-value pair; 
   FALSE otherwise. */
CF_EXPORT
Boolean CFReadStreamSetProperty(CFReadStreamRef stream, CFStreamPropertyKey propertyName, CFTypeRef propertyValue);
CF_EXPORT
Boolean CFWriteStreamSetProperty(CFWriteStreamRef stream, CFStreamPropertyKey propertyName, CFTypeRef propertyValue);

/* Asynchronous processing - If you wish to neither poll nor block, you may register 
   a client to hear about interesting events that occur on a stream.  Only one client
   per stream is allowed; registering a new client replaces the previous one.
 
   Once you have set a client, the stream must be scheduled to provide the context in
   which the client will be called.  Streams may be scheduled on a single dispatch queue
   or on one or more run loops.  If scheduled on a run loop, it is the caller's responsibility
   to ensure that at least one of the scheduled run loops is being run.

   NOTE: Unlike other CoreFoundation APIs, pasing a NULL clientContext here will remove
   the client.  If you do not care about the client context (i.e. your only concern
   is that your callback be called), you should pass in a valid context where every
   entry is 0 or NULL.

*/

CF_EXPORT
Boolean CFReadStreamSetClient(CFReadStreamRef stream, CFOptionFlags streamEvents, CFReadStreamClientCallBack clientCB, CFStreamClientContext *clientContext);
CF_EXPORT
Boolean CFWriteStreamSetClient(CFWriteStreamRef stream, CFOptionFlags streamEvents, CFWriteStreamClientCallBack clientCB, CFStreamClientContext *clientContext);

CF_EXPORT
void CFReadStreamScheduleWithRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFRunLoopMode runLoopMode);
CF_EXPORT
void CFWriteStreamScheduleWithRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFRunLoopMode runLoopMode);

CF_EXPORT
void CFReadStreamUnscheduleFromRunLoop(CFReadStreamRef stream, CFRunLoopRef runLoop, CFRunLoopMode runLoopMode);
CF_EXPORT
void CFWriteStreamUnscheduleFromRunLoop(CFWriteStreamRef stream, CFRunLoopRef runLoop, CFRunLoopMode runLoopMode);


/*
 * Specify the dispatch queue upon which the client callbacks will be invoked.
 * Passing NULL for the queue will prevent future callbacks from being invoked.
 * Specifying a dispatch queue using this API will unschedule the stream from
 * any run loops it had previously been scheduled upon - similarly, scheduling
 * with a runloop will disassociate the stream from any existing dispatch queue.
 */
CF_EXPORT
void CFReadStreamSetDispatchQueue(CFReadStreamRef stream, dispatch_queue_t q) API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));

CF_EXPORT
void CFWriteStreamSetDispatchQueue(CFWriteStreamRef stream, dispatch_queue_t q) API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));

/*
 * Returns the previously set dispatch queue with an incremented retain count.  
 * Note that the stream's queue may have been set to NULL if the stream was 
 * scheduled on a runloop subsequent to it having had a dispatch queue set.
 */
CF_EXPORT
dispatch_queue_t CFReadStreamCopyDispatchQueue(CFReadStreamRef stream) API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));

CF_EXPORT
dispatch_queue_t CFWriteStreamCopyDispatchQueue(CFWriteStreamRef stream) API_AVAILABLE(macos(10.9), ios(7.0), watchos(2.0), tvos(9.0));


/* The following API is deprecated starting in 10.5; please use CFRead/WriteStreamCopyError(), above, instead */
typedef CF_ENUM(CFIndex, CFStreamErrorDomain) {
    kCFStreamErrorDomainCustom = -1L,      /* custom to the kind of stream in question */
    kCFStreamErrorDomainPOSIX = 1,        /* POSIX errno; interpret using <sys/errno.h> */
    kCFStreamErrorDomainMacOSStatus      /* OSStatus type from Carbon APIs; interpret using <MacTypes.h> */
};

typedef struct {
    CFIndex domain; 
    SInt32 error;
} CFStreamError;
CF_EXPORT
CFStreamError CFReadStreamGetError(CFReadStreamRef stream);
CF_EXPORT
CFStreamError CFWriteStreamGetError(CFWriteStreamRef stream);


CF_EXTERN_C_END
CF_IMPLICIT_BRIDGING_DISABLED

#endif /* ! __COREFOUNDATION_CFSTREAM__ */
