/*++

Copyright (c) 1996 Microsoft Corporation

Copyright (c) 1996 Intel Corp

Module Name:

    perftest.cpp

Abstract:

    This  module  contains  demonstration code which illustrates how to install
    and  use  a  service provider.  This demonstration code is designed to work
    together only with the temporary sample skeleton "null" service provider.

--*/

#include <windows.h>
#include "nullpdef.h"
#include "ws2spi.h"



//
//  Unique GUID generated with UUIDGEN:
//   ba657007-d615-11cf-9f5c-08003e301875
//
//  WARNING:  If you use this sample code, you must generate your own
//   GUID.  Under no circumstances should you use this GUID.
//

GUID ProviderIDGuid = { 0xba657007, 0xd615, 0x11cf, 0x9f, 0x5c,
                     0x08, 0x00, 0x3e, 0x30, 0x18, 0x75 };


//
// Dummy name as unicode string
//

#define SAMPLE_NAME_WSTRING L"Null Provider protocol for sample"

// The  nominal  size  of  send  and  receive operations.  Since we expect this
// sample code to be used only with a "null" provider that does not really send
// or receive data, the value is arbitrary and has no real effect.

#define SEND_RECEIVE_BYTES  100




static
void
Test_Send(
    IN  SOCKET  SendSock,
    IN  DWORD   NumTries
    )
/*++
Routine Description:

    This  procedure  performs the indicated number of send operations through a
    socket.

Arguments:

    SendSock - Supplies the socket to be used for the "send" operations.

    NumTries - Supplies the number of tries.

Return Value:

    none

Implementation:

    We  use  an "unrolled" loop to perform a large number of calls back-to-back
    without  loop turnaround overhead.  Then we perform the remaining number of
    calls.   This  should  help  reduce  the  overall  contribution of the loop
    overhead,  which  might  be  relevant  if this were being used as part of a
    performance measurement.
--*/
{
    DWORD    unrolled_iterations;
    DWORD    remaining_iterations;
    DWORD    i;
    int      send_result;
    WSABUF   send_bufs;
    CHAR     data_space[SEND_RECEIVE_BYTES];
    DWORD    bytes_sent;

#define SEND_ONCE \
    send_result = WSASend(                       \
        SendSock,      /* s                   */ \
        & send_bufs,   /* lpBuffers           */ \
        1,             /* dwBufferCount       */ \
        & bytes_sent,  /* lpNumberOfBytesSent */ \
        0,             /* dwFlags             */ \
        NULL,          /* lpOverlapped        */ \
        NULL)          /* lpCompletionRoutine */

#define UNROLLED_LENGTH 50

    unrolled_iterations  = NumTries / UNROLLED_LENGTH;
    remaining_iterations = NumTries % UNROLLED_LENGTH;
    send_bufs.len = SEND_RECEIVE_BYTES;
    send_bufs.buf = data_space;

    for (i = 0; i < unrolled_iterations; i++) {
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
        SEND_ONCE;
    } // for i

    for (i = 0; i < remaining_iterations; i++) {
        SEND_ONCE;
    } // for i
    
} // Test_Send




static
SOCKET
Create_Test_Socket(
    void)
/*++
Routine Description:

    This  procedure  creates a socket to be used for the tests through the null
    provider.

Arguments:

    none
    
Return Value:

    The  procedure  returns  the  newly  created  socket  if  it is successful,
    otherwise it returns INVALID_SOCKET.
--*/
{
    SOCKET  new_socket;

    new_socket = WSASocket(
        AF_SPECIAL_NULL,   // af
        SOCK_STREAM,       // type
        0,                 // protocol
        NULL,              // lpProtocolInfo
        0,                 // g
        0);                // dwFlags
    return(new_socket);

} // Create_Test_Socket




static
void
Destroy_Test_Socket(
    IN  SOCKET  TestSock
    )
/*++
Routine Description:

    This  procedure  closes  a socket that was being used for tests through the
    null provider.

Arguments:

    TestSock - Supplies the socket to be closed.
    
Return Value:

    none
--*/
{
    int    close_result;
    DWORD  last_error;

    close_result = closesocket(
        TestSock);
    if (close_result != ERROR_SUCCESS) {
        last_error = GetLastError();
        last_error = GetLastError();  // Redundant for single-step convenience.
    }
    
} // Destroy_Test_Socket




static
DWORD
Establish_Provider(
    void
    )
/*++
Routine Description:

    This procedure makes sure the null provider is installed and installs it if
    it is not already installed.

    Normally  an  application  program  would  NOT actually install a provider.
    Installation  was  bundled into this application example simply to make the
    example compact.

Arguments:

    none
    
Return Value:


--*/
{
    LPWSAPROTOCOL_INFOW  proto_buf;
    DWORD                size_guess;
    BOOL                 still_trying;
    BOOL                 got_protos;
    int                  enum_result;
    int                  error_code;
    BOOL                 found_null_prov;

    size_guess = 0;
    got_protos = FALSE;
    still_trying = TRUE;
    proto_buf = NULL;

    // We  call  WSCEnumProtocols  twice.   The first call determines how big a
    // buffer  we will actually need for the results.  The second call actually
    // retrieves  the  results.  In a situation such as this, I generally write
    // the  sequence  of  two  calls  as  a  loop,  since this way the call and
    // error-check code has to be written only once.
    while (still_trying) {
        enum_result = WSCEnumProtocols(
            NULL,           // lpiProtocols
            proto_buf,      // lpProtocolBuffer
            & size_guess,   // lpdwBufferLength
            & error_code);  // lpErrno
        if (enum_result != SOCKET_ERROR) {
            got_protos = TRUE;
            still_trying = FALSE;
        }
        else {
            if (error_code == WSAENOBUFS) {
                delete proto_buf;
                proto_buf = (LPWSAPROTOCOL_INFOW) new char[size_guess];
                if (proto_buf == NULL) {
                    still_trying = FALSE;
                }
            }
            else {
                still_trying = FALSE;
            }
        }
    } // while still_trying

    found_null_prov = FALSE;
    if (got_protos) {
        int  i;

        for (i = 0; i < enum_result; i++) {
            if (proto_buf[i].iAddressFamily == AF_SPECIAL_NULL) {
                found_null_prov = TRUE;
                break;
            }
        }
    }
    delete proto_buf;

    if (! found_null_prov) {
        WSAPROTOCOL_INFOW proto_info;
        int               install_result;
        int               install_error;

        proto_info.dwServiceFlags1 = 0;
        proto_info.dwServiceFlags2 = 0;
        proto_info.dwServiceFlags3 = 0;
        proto_info.dwServiceFlags4 = 0;
        proto_info.dwProviderFlags = PFL_MATCHES_PROTOCOL_ZERO;
        proto_info.dwCatalogEntryId = 0;   // filled in by system
        proto_info.ProtocolChain.ChainLen = BASE_PROTOCOL;
        proto_info.iVersion = 0;
        proto_info.iAddressFamily = AF_SPECIAL_NULL;
        proto_info.iMaxSockAddr = 16;
        proto_info.iMinSockAddr = 16;
        proto_info.iSocketType = SOCK_STREAM;
        proto_info.iProtocol = 0;
        proto_info.iProtocolMaxOffset = 0;
        proto_info.iNetworkByteOrder = BIGENDIAN;
        proto_info.iSecurityScheme = SECURITY_PROTOCOL_NONE;
        proto_info.dwMessageSize = 0;  // stream-oriented
        proto_info.dwProviderReserved = 0;

        proto_info.ProviderId = ProviderIDGuid ; // filled in by system.

        // copy unicode string into name parameter
        memcpy(proto_info.szProtocol, SAMPLE_NAME_WSTRING, sizeof SAMPLE_NAME_WSTRING);


        install_result = WSCInstallProvider(
            &ProviderIDGuid,   // lpProviderID
            L"nullprov.dll",   // lpszProviderDllPath (unicode string)
            & proto_info,      // lpProtocolInfoList
            1,                 // dwNumberOfEntries
            & install_error);  // lpErrno
        if (install_result != ERROR_SUCCESS) {
            install_error = install_error;
            // assert(install_result == ERROR_SUCCESS);
        }
    } // if not found_null_prov

    return(TRUE);
    
} // Establish_Provider
    



void
main(
    void
    )
/*++
--*/
{
    SOCKET   test_sock;
    int      start_return;
    WSADATA  winsock_data;
    DWORD    last_error;
    int      clean_return;
    DWORD    num_tries;

    Establish_Provider();

    start_return = WSAStartup(
        MAKEWORD(2,0),    // wVersionRequested
        & winsock_data);  // lpWSAData

    if (start_return == ERROR_SUCCESS) {
        test_sock = Create_Test_Socket();
        // To  keep  this  sample  simple,  this  demonstration program and the
        // matching  null  provider  do  not  bother  binding or connecting the
        // socket  before  doing  "send" operations.  With a real provider, one
        // should  bind  and possibly also connect the socket before proceeding
        // with a "send".
        if (test_sock != INVALID_SOCKET) {
            num_tries = 10;
            Test_Send(
                test_sock,   // SendSock
                num_tries);  // NumTries
            Destroy_Test_Socket(
                test_sock);
        } // if test_sock != NULL
        else {
            last_error = GetLastError();
            // The  following  is  just  a  redundant executable statement that
            // allows   me   to   see   the   return  value  conveniently  when
            // single-stepping.
            last_error = GetLastError();
        }
    } // if start_return == ERROR_SUCCESS
    else {
        last_error = GetLastError();
        last_error = GetLastError();  // Redundant for single-step convenience.
    }

    clean_return = WSACleanup();
    clean_return = clean_return; // Redundant for single-step convenience.
    
} // main
