diff --git a/Headers/CoreFoundation/CFStreamPriv.h b/Headers/CoreFoundation/CFStreamPriv.h index 60ca0226..7db29aea 100644 --- a/Headers/CoreFoundation/CFStreamPriv.h +++ b/Headers/CoreFoundation/CFStreamPriv.h @@ -76,7 +76,7 @@ struct __CFReadStream CFRuntimeBase parent; struct CFReadStreamImpl impl; - Boolean open, closed, failed; + Boolean open, closed, failed, atEnd; CFErrorRef error; /* callbacks when used with a runloop */ diff --git a/Source/CFStream.c b/Source/CFStream.c index 39ef366b..f9dc4be2 100644 --- a/Source/CFStream.c +++ b/Source/CFStream.c @@ -45,6 +45,7 @@ # include # include #endif +#include #include CONST_STRING_DECL(kCFStreamPropertyDataWritten, "kCFStreamPropertyDataWritten"); @@ -557,7 +558,7 @@ CFStreamGetError (CFErrorRef inError) { if (!inError) { - CFStreamError error = { kCFStreamErrorDomainPOSIX, 0 }; + CFStreamError error = { 0, 0 }; return error; } else @@ -856,7 +857,11 @@ CFWriteStreamBufferWrite (CFWriteStreamRef s, const UInt8 *buffer, stream->bufferCapacity = cap; } else - bufferLength = bufSpace; + { + CFWriteStreamSetError(s, ENOMEM); + s->failed = true; + return -1; + } } GSMemoryCopy(stream->buffer + stream->position, buffer, bufferLength); @@ -1027,6 +1032,7 @@ CFReadStreamBufferGetBuffer (CFReadStreamRef s, CFIndex maxBytesToRead, retval = stream->buffer + stream->position; stream->position += *numBytesRead; + s->atEnd = (stream->position >= stream->bufferCapacity); return retval; } @@ -1068,6 +1074,8 @@ CFReadStreamGetStatus (CFReadStreamRef stream) return kCFStreamStatusClosed; else if (stream->failed) return kCFStreamStatusError; + else if (stream->atEnd) + return kCFStreamStatusAtEnd; else if (stream->open) return kCFStreamStatusOpen; else @@ -1089,6 +1097,8 @@ CFReadStreamHasBytesAvailable (CFReadStreamRef stream) CF_OBJC_FUNCDISPATCHV(_kCFReadStreamTypeID, Boolean, stream, "hasBytesAvailable"); + if (!stream->open || stream->closed) + return false; if (stream->impl.hasBytes != NULL) return stream->impl.hasBytes(stream); return true; @@ -1196,6 +1206,7 @@ CFReadStreamBufferRead (CFReadStreamRef s, UInt8 *buffer, CFIndex bufferLength) GSMemoryCopy(buffer, stream->buffer + stream->position, bufferLength); stream->position += bufferLength; + s->atEnd = (stream->position >= stream->bufferCapacity); return bufferLength; } diff --git a/Tests/CFStream/no_error.m b/Tests/CFStream/no_error.m new file mode 100644 index 00000000..f6c14aa1 --- /dev/null +++ b/Tests/CFStream/no_error.m @@ -0,0 +1,30 @@ +#include + +#include "../CFTesting.h" + +int main (void) +{ + const char *data = "ABC"; + CFReadStreamRef r; + CFWriteStreamRef w; + CFStreamError e; + + r = CFReadStreamCreateWithBytesNoCopy (NULL, (const UInt8 *)data, 3, + kCFAllocatorNull); + CFReadStreamOpen (r); + e = CFReadStreamGetError (r); + PASS_CF(e.domain == 0 && e.error == 0, + "A read stream with no error reports domain 0."); + CFReadStreamClose (r); + CFRelease (r); + + w = CFWriteStreamCreateWithAllocatedBuffers (NULL, NULL); + CFWriteStreamOpen (w); + e = CFWriteStreamGetError (w); + PASS_CF(e.domain == 0 && e.error == 0, + "A write stream with no error reports domain 0."); + CFWriteStreamClose (w); + CFRelease (w); + + return 0; +} diff --git a/Tests/CFStream/read_atend.m b/Tests/CFStream/read_atend.m new file mode 100644 index 00000000..12730738 --- /dev/null +++ b/Tests/CFStream/read_atend.m @@ -0,0 +1,27 @@ +#include + +#include "../CFTesting.h" + +int main (void) +{ + const char *data = "ABCDEFGHIJ"; + UInt8 buf[64]; + CFReadStreamRef r; + + r = CFReadStreamCreateWithBytesNoCopy (NULL, (const UInt8 *)data, 10, + kCFAllocatorNull); + CFReadStreamOpen (r); + CFReadStreamRead (r, buf, 10); + PASS_CF(CFReadStreamGetStatus (r) == kCFStreamStatusAtEnd, + "Status is AtEnd once all bytes are consumed."); + PASS_CF(CFReadStreamHasBytesAvailable (r) == false, + "No bytes are available at the end."); + PASS_CF(CFReadStreamRead (r, buf, 64) == 0, + "Reading past the end returns zero."); + PASS_CF(CFReadStreamGetStatus (r) == kCFStreamStatusAtEnd, + "Status remains AtEnd past the end."); + CFReadStreamClose (r); + CFRelease (r); + + return 0; +} diff --git a/Tests/CFStream/read_unopened.m b/Tests/CFStream/read_unopened.m new file mode 100644 index 00000000..0c9de7c8 --- /dev/null +++ b/Tests/CFStream/read_unopened.m @@ -0,0 +1,19 @@ +#include + +#include "../CFTesting.h" + +int main (void) +{ + const char *data = "ABCDEFGHIJ"; + CFReadStreamRef r; + + r = CFReadStreamCreateWithBytesNoCopy (NULL, (const UInt8 *)data, 10, + kCFAllocatorNull); + PASS_CF(CFReadStreamGetStatus (r) == kCFStreamStatusNotOpen, + "Status is NotOpen before opening."); + PASS_CF(CFReadStreamHasBytesAvailable (r) == false, + "No bytes are available before the stream is opened."); + CFRelease (r); + + return 0; +} diff --git a/Tests/CFStream/write_overflow.m b/Tests/CFStream/write_overflow.m new file mode 100644 index 00000000..9fb60d0a --- /dev/null +++ b/Tests/CFStream/write_overflow.m @@ -0,0 +1,27 @@ +#include + +#include "../CFTesting.h" +#include +#include + +int main (void) +{ + UInt8 fb[8]; + CFWriteStreamRef w; + CFStreamError e; + + w = CFWriteStreamCreateWithBuffer (NULL, fb, 8); + CFWriteStreamOpen (w); + PASS_CF(CFWriteStreamWrite (w, (const UInt8 *)"ABCDEFGH", 8) == 8, + "Filling the fixed buffer exactly succeeds."); + PASS_CF(CFWriteStreamWrite (w, (const UInt8 *)"X", 1) == -1, + "Writing past the fixed buffer returns -1."); + PASS_CF(CFWriteStreamGetStatus (w) == kCFStreamStatusError, + "Status is Error after overflowing the fixed buffer."); + e = CFWriteStreamGetError (w); + PASS_CF(e.domain == kCFStreamErrorDomainPOSIX && e.error == ENOMEM, + "Overflowing the fixed buffer reports POSIX ENOMEM."); + CFRelease (w); + + return 0; +}