NoCopyReceives.c

/*
    File:       NoCopyReceives.c
 
    Contains:   Minimal sample to demo no-copy receives under OT.
 
    Written by: Quinn "The Eskimo!" 
 
    Copyright:  Copyright © 1997-2001 by Apple Computer, Inc., All Rights Reserved.
 
                You may incorporate this Apple sample source code into your program(s) without
                restriction. This Apple sample source code has been provided "AS IS" and the
                responsibility for its operation is yours. You are not permitted to redistribute
                this Apple sample source code as "Apple sample source code" after having made
                changes. If you're going to re-distribute the source, we require that you make
                it clear in the source that the code was descended from Apple sample source
                code, but that you've made changes.
 
    Change History (most recent first):
                2/18/2001   Chad Jones      Updated for Codewarrior IDE 4.1 and Carbonized for OSX
                7/22/1999   Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
                
 
*/
 
/////////////////////////////////////////////////////////////////////
// Need to include to be carbonized 
 
#if defined(__MWERKS__)
#   include <carbon.h>
 
/////////////////////////////////////////////////////////////////////
// Pick up all the standard OT stuff.
#   include <Events.h>
#   include <Files.h>
#   include <OpenTransport.h>
#   include <OpenTptInternet.h>
#   include <OpenTptClient.h>
#   include <SIOUX.h>
 
#else
#   include <CoreServices/CoreServices.h>
#endif
 
#   include <stdio.h>
 
/////////////////////////////////////////////////////////////////////
// OTDebugStr is not defined in any OT header files, but it is
// exported by the libraries, so we define the prototype here.
 
extern pascal void OTDebugStr(const char* str);
 
/////////////////////////////////////////////////////////////////////
// More Assert replaces OTAssert in carbonized code because OTAssert 
// does not exist
    //
    //  We usually want asserions and other debugging code
    //  turned on, but you can turn it all off if you like
    //  by setting MORE_DEBUG to 0.
    //
 
#ifndef MORE_DEBUG
#   define MORE_DEBUG 1
#endif
 
    //
    //  Our assertion macros compile down to nothing if
    //  MORE_DEBUG is not true. MoreAssert produces a
    //  value indicating whether the assertion succeeded
    //  or failed. MoreAssertQ is Quinn's flavor of
    //  MoreAssert which does not produce a value.
    //
 
#if MORE_DEBUG
#   define MoreAssert(x) \
        ((x) ? true : (DebugStr ("\pMoreAssert failure: " #x), false))
#   define MoreAssertQ(x) \
        do { if (!(x)) DebugStr ("\pMoreAssertQ failure: " #x); } while (false)
#else
#   define MoreAssert(x) (true)
#   define MoreAssertQ(x)
#endif
 
 
/////////////////////////////////////////////////////////////////////
 
static UInt32 gLastPrinted = 0;
 
static pascal void YieldingNotifier(void* contextPtr, OTEventCode code, 
                                       OTResult result, void* cookie)
{
    #pragma unused(contextPtr)
    #pragma unused(result)
    #pragma unused(cookie)
    
    switch (code) {
        case kOTSyncIdleEvent:
            if ( TickCount() > gLastPrinted + 60 ) {
                printf(".");
                fflush(stdout);
                gLastPrinted = TickCount();
            }
            break;
        default:
            // do nothing
            break;
    }
}
 
/////////////////////////////////////////////////////////////////////
 
enum {
    kTransferBufferSize = 1024
};
 
static char gTransferBuffer[kTransferBufferSize];
 
static OSStatus NoCopyReceiveUsingOTReadBuffer(EndpointRef ep, SInt16 destFileRefNum)
    // Reads data from the endpoint using no-copy receive.  The data
    // is then copied out of the OTBuffer chain using the OTReadBuffer utility
    // function.  This method is useful if you need to look at a small chunk of
    // data (which you can copy out using OTReadBuffer) to decide what to do with
    // the rest.
{
    OSStatus err;
    OTResult result;
    OTBuffer *receivedBuffer;
    OTBufferInfo bufferInfo;
    OTFlags junkFlags;
    UInt32 bytesRemaining;
    OTByteCount bytesThisTime; //This is a UInt32
    SInt32 count;
    
    // Prepare for failure.
    
    err = noErr;
    receivedBuffer = nil;
        
    // Read the data.  Use the constant kOTNetbufDataIsOTBufferStar to
    // indicate that you want to do a no-copy receive.
    
    result = OTRcv(ep, &receivedBuffer, kOTNetbufDataIsOTBufferStar, &junkFlags);
    if (result >= 0) {
 
        // Use the OT utility function OTBufferDataSize to calculate
        // how much data OT returned.
                
        bytesRemaining = OTBufferDataSize(receivedBuffer);
        
        // Initialise the bufferInfo data structure.
 
        bufferInfo.fOffset = 0;
        bufferInfo.fBuffer = receivedBuffer;
        
        // Write that data to the file.  We do this in chunks,
        // copying each chunk of data out of the OTBuffer chain
        // and into our transfer buffer using the OTReadBuffer function,
        // then writing each chunk of data, until there is no
        // more data left in the buffer chain.  This is not a
        // particularly efficient method (see below for something
        // better) but it does demonstrate the use of OTReadBuffer.
        
        while (err == noErr && bytesRemaining > 0) {
            if (bytesRemaining > kTransferBufferSize) {
                bytesThisTime = kTransferBufferSize;
            } else {
                bytesThisTime = bytesRemaining;
            }
            (void) OTReadBuffer(&bufferInfo, gTransferBuffer, &bytesThisTime);
            count = bytesThisTime;
            err = FSWrite(destFileRefNum, &count, gTransferBuffer);
            bytesRemaining -= bytesThisTime;
        }
        
        err = noErr;
    } else {
        err = result;
    }
    
    // Clean up.  We *must* release the OTBuffer chain back to OT
    // so that it can reuse it.  Also, OTReleaseBuffer is not tolerant of
    // the parameter being nil, so we check for that case first.
    
    if (receivedBuffer != nil) {
        OTReleaseBuffer(receivedBuffer);
    }
    
    return err;
}
 
/////////////////////////////////////////////////////////////////////
 
static OSStatus NoCopyReceiveWalkingBufferChain(EndpointRef ep, SInt16 destFileRefNum)
    // Reads data from the endpoint using no-copy receive.  We walk
    // the resulting buffer chain, writing out chunks of data
    // directly to the file from the buffers returned to us by OT.
{
    OSStatus err;
    OTResult result;
    OTBufferInfo bufferInfo;
    OTBuffer *thisBuffer;
    OTFlags junkFlags;
    SInt32 count;
    
    err = noErr;
 
    // Initialise the bufferInfo data structure.
 
    bufferInfo.fOffset = 0;
    bufferInfo.fBuffer = nil;
    
    // Read the data.  Use the constant kOTNetbufDataIsOTBufferStar to
    // indicate that you want to do a no-copy receive.
 
    result = OTRcv(ep, &bufferInfo.fBuffer, kOTNetbufDataIsOTBufferStar, &junkFlags);
    if (result >= 0) {
    
        // Now walk the returned buffer chain, writing out each
        // chunk of data to the file.
        
        thisBuffer = bufferInfo.fBuffer;
        while (err == noErr && thisBuffer != nil) {
            
            count = thisBuffer->fLen;
            err = FSWrite(destFileRefNum, &count, thisBuffer->fData);
            
            thisBuffer = thisBuffer->fNext;
        }
 
    } else {
        err = result;
    }
 
    // Clean up.  We *must* release the OTBuffer chain back to OT
    // so that it can reuse it.  Also, OTReleaseBuffer is not tolerant of
    // the parameter being nil, so we check for that case first.
 
    if (bufferInfo.fBuffer != nil) {
        OTReleaseBuffer(bufferInfo.fBuffer);
    }
 
    return err;
}
 
/////////////////////////////////////////////////////////////////////
 
enum {
    kUsingOTReadBuffer = 0,
    kUseWalkingBufferChain
};
 
static OSStatus TestNoCopyReceive(UInt8 method, SInt16 destFileRefNum)
    // Test the above two no-copy receive functions by connecting
    // to "www.apple.com" and downloading the root HTTP object.
{
    OSStatus err;
    OSStatus junk;
    EndpointRef ep;
    DNSAddress hostDNSAddress;
    TCall sndCall;
    OTResult bytesSent;
    char httpGetCommand[256];
    
    // Create a TCP endpoint.
    
    ep = OTOpenEndpointInContext(OTCreateConfiguration(kTCPName), 0, nil, &err,nil);
    
    // Set up the endpoint.
    
    if (err == noErr) {
 
        // Establish the modes of operation.  This sample uses
        // sync/blocking mode, with sync idle events that yield
        // time using the Thread Manager.
 
        junk = OTSetSynchronous(ep);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetSynchronous failed 
        
        junk = OTSetBlocking(ep);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetBlocking failed
        
        
        junk = OTInstallNotifier(ep, NewOTNotifyUPP(YieldingNotifier), nil);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTInstallNotifier failed 
        
        junk = OTUseSyncIdleEvents(ep, true);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTUseSyncIdleEvents failed 
 
        // Bind the endpoint.  Because we're an outgoing connection,
        // we don't have to bind it to a specific address.
                
        err = OTBind(ep, nil, nil);
    }
        
    // Initialise the sndCall structure and call OTConnect.  We nil
    // out most of the fields in the sndCall structure because
    // we don't want any special options or connection data.
    // The important field of the sndCall is the addr TNetBuf,
    // which we initialise to the address "www.apple.com:80"
    // (port 80 is the HTTP port).
    
    if (err == noErr) {
        OTMemzero(&sndCall, sizeof(TCall));
        sndCall.addr.buf    = (UInt8 *) &hostDNSAddress;
        sndCall.addr.len    = OTInitDNSAddress(&hostDNSAddress, "www.apple.com:80");
        
        err = OTConnect(ep, &sndCall, nil);
    }
    
    // Send the HTTP command to the web server.
    
    if (err == noErr) {
        (void) sprintf(httpGetCommand, "GET / HTTP/1.0%c%c%c%c", 13, 10, 13, 10);
 
        bytesSent = OTSnd(ep, (void *) httpGetCommand, OTStrLength(httpGetCommand), 0);
        
        // OTSnd returns the number of bytes sent.  Because we're in
        // synchronous mode, it won't return until it's sent all the
        // bytes, or it gets an error.
        
        if (bytesSent > 0) {
            err = noErr;
        } else {
            err = bytesSent;
        }
    }
    
    // Now receive the response from the server and write it to the
    // destination file.
    
    if (err == noErr) {
 
        // No-copy receive does not really make sense in sync/blocking
        // mode, so we switch the endpoint to sync/non-blocking before.
        // proceeding.  This is reasonable because you should not be using
        // no-copy receives unless you're looking for maximum speed,
        // and if you're looking for maximum speed you should be using
        // async/blocking mode, and doing everything in your notifier.
        // In this case, I'm just trying to demonstrate it's use, so
        // I make do with sync/non-blocking mode.
        
        junk = OTSetNonBlocking(ep);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTSetNonBlocking failed 
 
        do {
        
            // Depending on which method we were asked to use,
            // call the relevant receive function.
            
            switch (method) {
                case kUsingOTReadBuffer:
                    err = NoCopyReceiveUsingOTReadBuffer(ep, destFileRefNum);
                    break;
                case kUseWalkingBufferChain:
                    err = NoCopyReceiveWalkingBufferChain(ep, destFileRefNum);
                    break;
                default:
                    //OTDebugBreak("TestNoCopyReceive: What method?");
                    MoreAssert(false);//Triggers if: Get to this error path
                    err = -1;
                    break;
            }
            
            // If we get kOTNoDataErr, that means we're still waiting for data,
            // so we just clear the error and continue looping.
            
            if (err == kOTNoDataErr) {
                YieldingNotifier(nil, kOTSyncIdleEvent, noErr, nil);
                err = noErr;
            }
        } while (err == noErr);
        
        // We will eventually leave the above loop with a kOTLookErr
        // because the endpoint received either a T_ORDREL or T_DISCONNECT
        // event.  Either way, the data transfer is finished, so
        // we can just clear the error code and continue.
        
        if (err == kOTLookErr) {
            err = noErr;
        }
    }
    
    // I'm not going to bother cleaning up the endpoint cleanly.
    // This is the subject of another sample.  For this sample, force
    // closing the endpoint is good enough.
    
    // ‚lean up.
    
    if (ep != kOTInvalidEndpointRef) {
        junk = OTCloseProvider(ep);
        MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: OTCloseProvider failed
    }
    return err;
}
 
/////////////////////////////////////////////////////////////////////
 
int main(void)
    // The main line of the sample program.  It creates
    // a file in the same directory as the sample called
    // "NoCopyReceive Test Output" and puts two copies
    // of the root HTTP object from "www.apple.com" into
    // that file.
{
    OSStatus err;
    OSStatus junk;
    FSSpec fss;
    SInt16 destFileRefNum;
    
#if defined(__MWERKS__)
    SIOUXSettings.autocloseonquit = FALSE;  // don't close the SIOUX window on program termination
    SIOUXSettings.asktosaveonclose = FALSE; // don't offer to save on a close
#endif
 
    printf("NoCopyReceives\n");
    printf("-- Download a URL using no-copy receives\n");
    printf("\n");
    
    err = InitOpenTransportInContext(kInitOTForApplicationMask, nil);
    
    if (err == noErr) {
    
        // Create and open the output file.
        
        (void) FSMakeFSSpec(0, 0, "\pNoCopyReceives_Output.txt", &fss);
                 
        (void) FSpCreate(&fss, 'R*ch', 'TEXT', 0);
                
        err = FSpOpenDF(&fss, fsRdWrPerm, &destFileRefNum);
 
        // Download two copies of the URL, one using the OTReadBuffer
        // method, the other by the buffer walking method.
 
        if (err == noErr) {
        
            printf("Downloading using OTReadBuffer method");
            err = TestNoCopyReceive(kUsingOTReadBuffer, destFileRefNum);
            printf("\n\n");
 
            if (err == noErr) {
                printf("Downloading using 'walk the buffer chain' method");
                err = TestNoCopyReceive(kUseWalkingBufferChain, destFileRefNum);
                printf("\n\n");
            }
 
            // Close the file.
            
            junk = FSClose(destFileRefNum);
            MoreAssert(junk == noErr);//Triggers if: TestNoCopyReceive: FSClose failed
        }
        
        CloseOpenTransportInContext(nil);
    }
    
    if (err == noErr) {
        printf("Success.\n");
    } else {
        printf("Failed with error %ld.\n", err);
    }
    printf("Done.\n");
        return(0);
}