1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-30 05:39:12 +02:00

Refactor remaining common/io modules with inline getters/setters.

Extend the pattern introduced in 79a2d02c to the remaining common/io modules.
This commit is contained in:
David Steele 2021-04-13 14:37:02 -04:00
parent 5bf160643b
commit 9fec4ce98c
30 changed files with 263 additions and 328 deletions

View File

@ -17,8 +17,8 @@ Execute Process
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/wait.h" #include "common/wait.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************

View File

@ -5,7 +5,7 @@ Buffer IO Read
#include "common/debug.h" #include "common/debug.h"
#include "common/io/bufferRead.h" #include "common/io/bufferRead.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -5,7 +5,7 @@ Buffer IO Write
#include "common/debug.h" #include "common/debug.h"
#include "common/io/bufferWrite.h" #include "common/io/bufferWrite.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -4,7 +4,7 @@ Io Client Interface
#include "build.auto.h" #include "build.auto.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/io/client.intern.h" #include "common/io/client.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -13,9 +13,7 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct IoClient struct IoClient
{ {
MemContext *memContext; // Mem context IoClientPub pub; // Publicly accessible variables
void *driver; // Driver object
const IoClientInterface *interface; // Driver interface
}; };
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -38,43 +36,21 @@ ioClientNew(void *driver, const IoClientInterface *interface)
*this = (IoClient) *this = (IoClient)
{ {
.memContext = memContextCurrent(), .pub =
.driver = driver, {
.interface = interface, .memContext = memContextCurrent(),
.driver = driver,
.interface = interface,
},
}; };
FUNCTION_LOG_RETURN(IO_CLIENT, this); FUNCTION_LOG_RETURN(IO_CLIENT, this);
} }
/**********************************************************************************************************************************/
IoSession *
ioClientOpen(IoClient *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
FUNCTION_LOG_RETURN(IO_SESSION, this->interface->open(this->driver));
}
/**********************************************************************************************************************************/
const String *
ioClientName(IoClient *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_CLIENT, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
FUNCTION_LOG_RETURN_CONST(STRING, this->interface->name(this->driver));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
String * String *
ioClientToLog(const IoClient *this) ioClientToLog(const IoClient *this)
{ {
return strNewFmt("{type: %s, driver: %s}", strZ(*this->interface->type), strZ(this->interface->toLog(this->driver))); return strNewFmt(
"{type: %s, driver: %s}", strZ(*this->pub.interface->type), strZ(this->pub.interface->toLog(this->pub.driver)));
} }

View File

@ -12,9 +12,28 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
typedef struct IoClient IoClient; typedef struct IoClient IoClient;
#include "common/io/client.intern.h"
#include "common/io/session.h" #include "common/io/session.h"
#include "common/type/object.h" #include "common/type/object.h"
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct IoClientPub
{
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoClientInterface *interface; // Driver interface
} IoClientPub;
// Name that identifies the client
__attribute__((always_inline)) static inline const String *
ioClientName(const IoClient *const this)
{
ASSERT_INLINE(this != NULL);
return ((const IoClientPub *)this)->interface->name(((const IoClientPub *)this)->driver);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -26,13 +45,12 @@ ioClientMove(IoClient *this, MemContext *parentNew)
} }
// Open session // Open session
IoSession *ioClientOpen(IoClient *this); __attribute__((always_inline)) static inline IoSession *
ioClientOpen(IoClient *const this)
/*********************************************************************************************************************************** {
Getters/Setters ASSERT_INLINE(this != NULL);
***********************************************************************************************************************************/ return ((const IoClientPub *)this)->interface->open(((const IoClientPub *)this)->driver);
// Name that identifies the client }
const String *ioClientName(IoClient *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor

View File

@ -5,6 +5,8 @@ Io Client Interface Internal
#define COMMON_IO_CLIENT_INTERN_H #define COMMON_IO_CLIENT_INTERN_H
#include "common/io/client.h" #include "common/io/client.h"
#include "common/io/session.h"
#include "common/type/string.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Interface Interface

View File

@ -8,7 +8,7 @@ File Descriptor Io Read
#include "common/debug.h" #include "common/debug.h"
#include "common/io/fd.h" #include "common/io/fd.h"
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -8,7 +8,7 @@ File Descriptor Io Write
#include "common/debug.h" #include "common/debug.h"
#include "common/io/fd.h" #include "common/io/fd.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -9,7 +9,7 @@ HTTP Response
#include "common/io/http/request.h" #include "common/io/http/request.h"
#include "common/io/http/response.h" #include "common/io/http/response.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/stat.h" #include "common/stat.h"
#include "common/wait.h" #include "common/wait.h"

View File

@ -7,7 +7,7 @@ IO Read Interface
#include "common/debug.h" #include "common/debug.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -16,20 +16,10 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct IoRead struct IoRead
{ {
MemContext *memContext; // Mem context IoReadPub pub; // Publicly accessible variables
void *driver; // Driver object
IoReadInterface interface; // Driver interface
IoFilterGroup *filterGroup; // IO filters
Buffer *input; // Input buffer Buffer *input; // Input buffer
Buffer *output; // Internal output buffer (extra output from buffered reads) Buffer *output; // Internal output buffer (extra output from buffered reads)
size_t outputPos; // Current position in the internal output buffer size_t outputPos; // Current position in the internal output buffer
bool eofAll; // Is the read done (read and filters complete)?
#ifdef DEBUG
bool opened; // Has the io been opened?
bool closed; // Has the io been closed?
#endif
}; };
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -52,10 +42,13 @@ ioReadNew(void *driver, IoReadInterface interface)
*this = (IoRead) *this = (IoRead)
{ {
.memContext = memContextCurrent(), .pub =
.driver = driver, {
.interface = interface, .memContext = memContextCurrent(),
.filterGroup = ioFilterGroupNew(), .driver = driver,
.interface = interface,
.filterGroup = ioFilterGroupNew(),
},
.input = bufNew(ioBufferSize()), .input = bufNew(ioBufferSize()),
}; };
} }
@ -73,18 +66,18 @@ ioReadOpen(IoRead *this)
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(!this->opened && !this->closed); ASSERT(!this->pub.opened && !this->pub.closed);
ASSERT(ioFilterGroupSize(this->filterGroup) == 0 || !ioReadBlock(this)); ASSERT(ioFilterGroupSize(this->pub.filterGroup) == 0 || !ioReadBlock(this));
// Open if the driver has an open function // Open if the driver has an open function
bool result = this->interface.open != NULL ? this->interface.open(this->driver) : true; bool result = this->pub.interface.open != NULL ? this->pub.interface.open(this->pub.driver) : true;
// Only open the filter group if the read was opened // Only open the filter group if the read was opened
if (result) if (result)
ioFilterGroupOpen(this->filterGroup); ioFilterGroupOpen(this->pub.filterGroup);
#ifdef DEBUG #ifdef DEBUG
this->opened = result; this->pub.opened = result;
#endif #endif
FUNCTION_LOG_RETURN(BOOL, result); FUNCTION_LOG_RETURN(BOOL, result);
@ -103,9 +96,9 @@ ioReadEofDriver(const IoRead *this)
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
FUNCTION_LOG_RETURN(BOOL, this->interface.eof != NULL ? this->interface.eof(this->driver) : false); FUNCTION_LOG_RETURN(BOOL, this->pub.interface.eof != NULL ? this->pub.interface.eof(this->pub.driver) : false);
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -120,17 +113,17 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool block)
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
// Loop until EOF or the output buffer is full // Loop until EOF or the output buffer is full
size_t bufferUsedBegin = bufUsed(buffer); size_t bufferUsedBegin = bufUsed(buffer);
while (!this->eofAll && bufRemains(buffer) > 0) while (!ioReadEof(this) && bufRemains(buffer) > 0)
{ {
// Process input buffer again to get more output // Process input buffer again to get more output
if (ioFilterGroupInputSame(this->filterGroup)) if (ioFilterGroupInputSame(this->pub.filterGroup))
{ {
ioFilterGroupProcess(this->filterGroup, this->input, buffer); ioFilterGroupProcess(this->pub.filterGroup, this->input, buffer);
} }
// Else new input can be accepted // Else new input can be accepted
else else
@ -146,7 +139,7 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool block)
if (ioReadBlock(this) && bufRemains(this->input) > bufRemains(buffer)) if (ioReadBlock(this) && bufRemains(this->input) > bufRemains(buffer))
bufLimitSet(this->input, bufRemains(buffer)); bufLimitSet(this->input, bufRemains(buffer));
this->interface.read(this->driver, this->input, block); this->pub.interface.read(this->pub.driver, this->input, block);
bufLimitClear(this->input); bufLimitClear(this->input);
} }
// Set input to NULL and flush (no need to actually free the buffer here as it will be freed with the mem context) // Set input to NULL and flush (no need to actually free the buffer here as it will be freed with the mem context)
@ -156,7 +149,7 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool block)
// Process the input buffer (or flush if NULL) // Process the input buffer (or flush if NULL)
if (this->input == NULL || !bufEmpty(this->input)) if (this->input == NULL || !bufEmpty(this->input))
ioFilterGroupProcess(this->filterGroup, this->input, buffer); ioFilterGroupProcess(this->pub.filterGroup, this->input, buffer);
// Stop if not blocking -- we don't need to fill the buffer as long as we got some data // Stop if not blocking -- we don't need to fill the buffer as long as we got some data
if (!block && bufUsed(buffer) > bufferUsedBegin) if (!block && bufUsed(buffer) > bufferUsedBegin)
@ -164,7 +157,7 @@ ioReadInternal(IoRead *this, Buffer *buffer, bool block)
} }
// Eof when no more input and the filter group is done // Eof when no more input and the filter group is done
this->eofAll = ioReadEofDriver(this) && ioFilterGroupDone(this->filterGroup); this->pub.eofAll = ioReadEofDriver(this) && ioFilterGroupDone(this->pub.filterGroup);
} }
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
@ -184,7 +177,7 @@ ioRead(IoRead *this, Buffer *buffer)
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
// Store size of remaining portion of buffer to calculate total read at the end // Store size of remaining portion of buffer to calculate total read at the end
size_t outputRemains = bufRemains(buffer); size_t outputRemains = bufRemains(buffer);
@ -220,12 +213,12 @@ ioReadSmall(IoRead *this, Buffer *buffer)
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(buffer != NULL); ASSERT(buffer != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
// Allocate the internal output buffer if it has not already been allocated // Allocate the internal output buffer if it has not already been allocated
if (this->output == NULL) if (this->output == NULL)
{ {
MEM_CONTEXT_BEGIN(this->memContext) MEM_CONTEXT_BEGIN(this->pub.memContext)
{ {
this->output = bufNew(ioBufferSize()); this->output = bufNew(ioBufferSize());
} }
@ -287,13 +280,13 @@ ioReadLineParam(IoRead *this, bool allowEof)
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
// Allocate the output buffer if it has not already been allocated. This buffer is not allocated at object creation because it // Allocate the output buffer if it has not already been allocated. This buffer is not allocated at object creation because it
// is not always used. // is not always used.
if (this->output == NULL) if (this->output == NULL)
{ {
MEM_CONTEXT_BEGIN(this->memContext) MEM_CONTEXT_BEGIN(this->pub.memContext)
{ {
this->output = bufNew(ioBufferSize()); this->output = bufNew(ioBufferSize());
} }
@ -363,17 +356,6 @@ ioReadLineParam(IoRead *this, bool allowEof)
FUNCTION_LOG_RETURN(STRING, result); FUNCTION_LOG_RETURN(STRING, result);
} }
/**********************************************************************************************************************************/
String *
ioReadLine(IoRead *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_READ, this);
FUNCTION_LOG_END();
FUNCTION_LOG_RETURN(STRING, ioReadLineParam(this, false));
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
bool bool
ioReadReady(IoRead *this, IoReadReadyParam param) ioReadReady(IoRead *this, IoReadReadyParam param)
@ -387,8 +369,8 @@ ioReadReady(IoRead *this, IoReadReadyParam param)
bool result = true; bool result = true;
if (this->interface.ready != NULL) if (this->pub.interface.ready != NULL)
result = this->interface.ready(this->driver, param.error); result = this->pub.interface.ready(this->pub.driver, param.error);
FUNCTION_LOG_RETURN(BOOL, result); FUNCTION_LOG_RETURN(BOOL, result);
} }
@ -402,75 +384,22 @@ ioReadClose(IoRead *this)
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(this != NULL); ASSERT(this != NULL);
ASSERT(this->opened && !this->closed); ASSERT(this->pub.opened && !this->pub.closed);
// Close the filter group and gather results // Close the filter group and gather results
ioFilterGroupClose(this->filterGroup); ioFilterGroupClose(this->pub.filterGroup);
// Close the driver if there is a close function // Close the driver if there is a close function
if (this->interface.close != NULL) if (this->pub.interface.close != NULL)
this->interface.close(this->driver); this->pub.interface.close(this->pub.driver);
#ifdef DEBUG #ifdef DEBUG
this->closed = true; this->pub.closed = true;
#endif #endif
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
} }
/**********************************************************************************************************************************/
bool
ioReadBlock(const IoRead *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_READ, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface.block);
}
/**********************************************************************************************************************************/
void *
ioReadDriver(IoRead *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_READ, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->driver);
}
/**********************************************************************************************************************************/
bool
ioReadEof(const IoRead *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_READ, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
ASSERT(this->opened && !this->closed);
FUNCTION_LOG_RETURN(BOOL, this->eofAll);
}
/**********************************************************************************************************************************/
IoFilterGroup *
ioReadFilterGroup(const IoRead *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_READ, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->filterGroup);
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
int int
ioReadFd(const IoRead *this) ioReadFd(const IoRead *this)
@ -481,18 +410,5 @@ ioReadFd(const IoRead *this)
ASSERT(this != NULL); ASSERT(this != NULL);
FUNCTION_LOG_RETURN(INT, this->interface.fd == NULL ? -1 : this->interface.fd(this->driver)); FUNCTION_LOG_RETURN(INT, this->pub.interface.fd == NULL ? -1 : this->pub.interface.fd(this->pub.driver));
}
/**********************************************************************************************************************************/
const IoReadInterface *
ioReadInterface(const IoRead *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_READ, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(&this->interface);
} }

View File

@ -15,9 +15,41 @@ Object type
typedef struct IoRead IoRead; typedef struct IoRead IoRead;
#include "common/io/filter/group.h" #include "common/io/filter/group.h"
#include "common/io/read.intern.h"
#include "common/type/buffer.h" #include "common/type/buffer.h"
#include "common/type/object.h" #include "common/type/object.h"
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Do reads block when more bytes are requested than are available to read?
__attribute__((always_inline)) static inline bool
ioReadBlock(const IoRead *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoReadPub *)this)->interface.block;
}
// Is IO at EOF? All driver reads are complete and all data has been flushed from the filters (if any).
__attribute__((always_inline)) static inline bool
ioReadEof(const IoRead *const this)
{
ASSERT_INLINE(this != NULL);
ASSERT_INLINE(((IoReadPub *)this)->opened && !((IoReadPub *)this)->closed);
return ((const IoReadPub *)this)->eofAll;
}
// Get filter group if filters need to be added
__attribute__((always_inline)) static inline IoFilterGroup *
ioReadFilterGroup(IoRead *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoReadPub *)this)->filterGroup;
}
// File descriptor for the read object. Not all read objects have a file descriptor and -1 will be returned in that case.
int ioReadFd(const IoRead *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -30,12 +62,17 @@ size_t ioRead(IoRead *this, Buffer *buffer);
// Same as ioRead() but optimized for small reads (intended for making repetitive reads that are smaller than ioBufferSize()) // Same as ioRead() but optimized for small reads (intended for making repetitive reads that are smaller than ioBufferSize())
size_t ioReadSmall(IoRead *this, Buffer *buffer); size_t ioReadSmall(IoRead *this, Buffer *buffer);
// Read linefeed-terminated string
String *ioReadLine(IoRead *this);
// Read linefeed-terminated string and optionally error on eof // Read linefeed-terminated string and optionally error on eof
String *ioReadLineParam(IoRead *this, bool allowEof); String *ioReadLineParam(IoRead *this, bool allowEof);
// Read linefeed-terminated string
__attribute__((always_inline)) static inline String *
ioReadLine(IoRead *const this)
{
ASSERT_INLINE(this != NULL);
return ioReadLineParam(this, false);
}
// Are there bytes ready to read immediately? There are no guarantees on how much data is available to read but it must be at least // Are there bytes ready to read immediately? There are no guarantees on how much data is available to read but it must be at least
// one byte. // one byte.
typedef struct IoReadReadyParam typedef struct IoReadReadyParam
@ -52,21 +89,6 @@ bool ioReadReady(IoRead *this, IoReadReadyParam param);
// Close the IO // Close the IO
void ioReadClose(IoRead *this); void ioReadClose(IoRead *this);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Do reads block when more bytes are requested than are available to read?
bool ioReadBlock(const IoRead *this);
// Is IO at EOF? All driver reads are complete and all data has been flushed from the filters (if any).
bool ioReadEof(const IoRead *this);
// Get filter group if filters need to be added
IoFilterGroup *ioReadFilterGroup(const IoRead *this);
// File descriptor for the read object. Not all read objects have a file descriptor and -1 will be returned in that case.
int ioReadFd(const IoRead *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -35,11 +35,35 @@ IoRead *ioReadNew(void *driver, IoReadInterface interface);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Getters/Setters Getters/Setters
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
typedef struct IoReadPub
{
MemContext *memContext; // Mem context
void *driver; // Driver object
IoReadInterface interface; // Driver interface
IoFilterGroup *filterGroup; // IO filters
bool eofAll; // Is the read done (read and filters complete)?
#ifndef NDEBUG
bool opened; // Has the io been opened?
bool closed; // Has the io been closed?
#endif
} IoReadPub;
// Driver for the read object // Driver for the read object
void *ioReadDriver(IoRead *this); __attribute__((always_inline)) static inline void *
ioReadDriver(IoRead *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoReadPub *)this)->driver;
}
// Interface for the read object // Interface for the read object
const IoReadInterface *ioReadInterface(const IoRead *this); __attribute__((always_inline)) static inline const IoReadInterface *
ioReadInterface(const IoRead *this)
{
ASSERT_INLINE(this != NULL);
return &((IoReadPub *)this)->interface;
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Macros for function logging Macros for function logging

View File

@ -4,7 +4,7 @@ Io Session Interface
#include "build.auto.h" #include "build.auto.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/io/session.intern.h" #include "common/io/session.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -13,9 +13,7 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct IoSession struct IoSession
{ {
MemContext *memContext; // Mem context IoSessionPub pub; // Publicly accessible variables
void *driver; // Driver object
const IoSessionInterface *interface; // Driver interface
}; };
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -40,29 +38,17 @@ ioSessionNew(void *driver, const IoSessionInterface *interface)
*this = (IoSession) *this = (IoSession)
{ {
.memContext = memContextCurrent(), .pub =
.driver = driver, {
.interface = interface, .memContext = memContextCurrent(),
.driver = driver,
.interface = interface,
},
}; };
FUNCTION_LOG_RETURN(IO_SESSION, this); FUNCTION_LOG_RETURN(IO_SESSION, this);
} }
/**********************************************************************************************************************************/
void
ioSessionClose(IoSession *this)
{
FUNCTION_LOG_BEGIN(logLevelTrace);
FUNCTION_LOG_PARAM(IO_SESSION, this);
FUNCTION_LOG_END();
ASSERT(this != NULL);
this->interface->close(this->driver);
FUNCTION_LOG_RETURN_VOID();
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
int int
ioSessionFd(IoSession *this) ioSessionFd(IoSession *this)
@ -73,46 +59,7 @@ ioSessionFd(IoSession *this)
ASSERT(this != NULL); ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->fd == NULL ? -1 : this->interface->fd(this->driver)); FUNCTION_TEST_RETURN(this->pub.interface->fd == NULL ? -1 : this->pub.interface->fd(this->pub.driver));
}
/**********************************************************************************************************************************/
IoRead *
ioSessionIoRead(IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->ioRead(this->driver));
}
/**********************************************************************************************************************************/
IoWrite *
ioSessionIoWrite(IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->ioWrite(this->driver));
}
/**********************************************************************************************************************************/
IoSessionRole
ioSessionRole(const IoSession *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_SESSION, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->interface->role(this->driver));
} }
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
@ -120,6 +67,6 @@ String *
ioSessionToLog(const IoSession *this) ioSessionToLog(const IoSession *this)
{ {
return strNewFmt( return strNewFmt(
"{type: %s, role: %s, driver: %s}", strZ(*this->interface->type), "{type: %s, role: %s, driver: %s}", strZ(*this->pub.interface->type),
ioSessionRole(this) == ioSessionRoleClient ? "client" : "server", strZ(this->interface->toLog(this->driver))); ioSessionRole(this) == ioSessionRoleClient ? "client" : "server", strZ(this->pub.interface->toLog(this->pub.driver)));
} }

View File

@ -12,10 +12,6 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
typedef struct IoSession IoSession; typedef struct IoSession IoSession;
#include "common/io/read.h"
#include "common/io/write.h"
#include "common/type/object.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Session roles Session roles
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -25,11 +21,58 @@ typedef enum
ioSessionRoleServer, // Server session ioSessionRoleServer, // Server session
} IoSessionRole; } IoSessionRole;
#include "common/io/read.h"
#include "common/io/session.intern.h"
#include "common/io/write.h"
#include "common/type/object.h"
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct IoSessionPub
{
MemContext *memContext; // Mem context
void *driver; // Driver object
const IoSessionInterface *interface; // Driver interface
} IoSessionPub;
// Session file descriptor, -1 if none
int ioSessionFd(IoSession *this);
// Read interface
__attribute__((always_inline)) static inline IoRead *
ioSessionIoRead(IoSession *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoSessionPub *)this)->interface->ioRead(((IoSessionPub *)this)->driver);
}
// Write interface
__attribute__((always_inline)) static inline IoWrite *
ioSessionIoWrite(IoSession *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoSessionPub *)this)->interface->ioWrite(((IoSessionPub *)this)->driver);
}
// Session role
__attribute__((always_inline)) static inline IoSessionRole
ioSessionRole(const IoSession *const this)
{
ASSERT_INLINE(this != NULL);
return ((const IoSessionPub *)this)->interface->role(((const IoSessionPub *)this)->driver);
}
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
// Close the session // Close the session
void ioSessionClose(IoSession *this); __attribute__((always_inline)) static inline void
ioSessionClose(IoSession *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoSessionPub *)this)->interface->close(((IoSessionPub *)this)->driver);
}
// Move to a new parent mem context // Move to a new parent mem context
__attribute__((always_inline)) static inline IoSession * __attribute__((always_inline)) static inline IoSession *
@ -38,21 +81,6 @@ ioSessionMove(IoSession *this, MemContext *parentNew)
return objMove(this, parentNew); return objMove(this, parentNew);
} }
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Session file descriptor, -1 if none
int ioSessionFd(IoSession *this);
// Read interface
IoRead *ioSessionIoRead(IoSession *this);
// Write interface
IoWrite *ioSessionIoWrite(IoSession *this);
// Session role
IoSessionRole ioSessionRole(const IoSession *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -5,6 +5,7 @@ Io Session Interface Internal
#define COMMON_IO_SESSION_INTERN_H #define COMMON_IO_SESSION_INTERN_H
#include "common/io/session.h" #include "common/io/session.h"
#include "common/io/write.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Interface Interface

View File

@ -10,7 +10,7 @@ Socket Client
#include "common/debug.h" #include "common/debug.h"
#include "common/log.h" #include "common/log.h"
#include "common/io/client.intern.h" #include "common/io/client.h"
#include "common/io/socket/client.h" #include "common/io/socket/client.h"
#include "common/io/socket/common.h" #include "common/io/socket/common.h"
#include "common/io/socket/session.h" #include "common/io/socket/session.h"

View File

@ -10,7 +10,7 @@ Socket Session
#include "common/io/fdRead.h" #include "common/io/fdRead.h"
#include "common/io/fdWrite.h" #include "common/io/fdWrite.h"
#include "common/io/socket/client.h" #include "common/io/socket/client.h"
#include "common/io/session.intern.h" #include "common/io/session.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -11,7 +11,7 @@ TLS Client
#include "common/crypto/common.h" #include "common/crypto/common.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/log.h" #include "common/log.h"
#include "common/io/client.intern.h" #include "common/io/client.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/io/tls/session.h" #include "common/io/tls/session.h"

View File

@ -8,11 +8,11 @@ TLS Session
#include "common/crypto/common.h" #include "common/crypto/common.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/io/session.intern.h" #include "common/io/session.h"
#include "common/io/tls/client.h" #include "common/io/tls/client.h"
#include "common/io/tls/session.h" #include "common/io/tls/session.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -7,7 +7,7 @@ IO Write Interface
#include "common/debug.h" #include "common/debug.h"
#include "common/io/io.h" #include "common/io/io.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
@ -16,10 +16,9 @@ Object type
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
struct IoWrite struct IoWrite
{ {
MemContext *memContext; // Mem context IoWritePub pub; // Publicly accessible variables
void *driver; // Driver object void *driver; // Driver object
IoWriteInterface interface; // Driver interface IoWriteInterface interface; // Driver interface
IoFilterGroup *filterGroup; // IO filters
Buffer *output; // Output buffer Buffer *output; // Output buffer
#ifdef DEBUG #ifdef DEBUG
@ -49,10 +48,13 @@ ioWriteNew(void *driver, IoWriteInterface interface)
*this = (IoWrite) *this = (IoWrite)
{ {
.memContext = memContextCurrent(), .pub =
{
.memContext = memContextCurrent(),
.filterGroup = ioFilterGroupNew(),
},
.driver = driver, .driver = driver,
.interface = interface, .interface = interface,
.filterGroup = ioFilterGroupNew(),
.output = bufNew(ioBufferSize()), .output = bufNew(ioBufferSize()),
}; };
} }
@ -77,11 +79,11 @@ ioWriteOpen(IoWrite *this)
// Track whether filters were added to prevent flush() from being called later since flush() won't work with most filters // Track whether filters were added to prevent flush() from being called later since flush() won't work with most filters
#ifdef DEBUG #ifdef DEBUG
this->filterGroupSet = ioFilterGroupSize(this->filterGroup) > 0; this->filterGroupSet = ioFilterGroupSize(this->pub.filterGroup) > 0;
#endif #endif
// Open the filter group // Open the filter group
ioFilterGroupOpen(this->filterGroup); ioFilterGroupOpen(this->pub.filterGroup);
#ifdef DEBUG #ifdef DEBUG
this->opened = true; this->opened = true;
@ -107,7 +109,7 @@ ioWrite(IoWrite *this, const Buffer *buffer)
{ {
do do
{ {
ioFilterGroupProcess(this->filterGroup, buffer, this->output); ioFilterGroupProcess(this->pub.filterGroup, buffer, this->output);
// Write data if the buffer is full // Write data if the buffer is full
if (bufRemains(this->output) == 0) if (bufRemains(this->output) == 0)
@ -116,7 +118,7 @@ ioWrite(IoWrite *this, const Buffer *buffer)
bufUsedZero(this->output); bufUsedZero(this->output);
} }
} }
while (ioFilterGroupInputSame(this->filterGroup)); while (ioFilterGroupInputSame(this->pub.filterGroup));
} }
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
@ -229,19 +231,19 @@ ioWriteClose(IoWrite *this)
// Flush remaining data // Flush remaining data
do do
{ {
ioFilterGroupProcess(this->filterGroup, NULL, this->output); ioFilterGroupProcess(this->pub.filterGroup, NULL, this->output);
// Write data if the buffer is full or if this is the last buffer to be written // Write data if the buffer is full or if this is the last buffer to be written
if (bufRemains(this->output) == 0 || (ioFilterGroupDone(this->filterGroup) && !bufEmpty(this->output))) if (bufRemains(this->output) == 0 || (ioFilterGroupDone(this->pub.filterGroup) && !bufEmpty(this->output)))
{ {
this->interface.write(this->driver, this->output); this->interface.write(this->driver, this->output);
bufUsedZero(this->output); bufUsedZero(this->output);
} }
} }
while (!ioFilterGroupDone(this->filterGroup)); while (!ioFilterGroupDone(this->pub.filterGroup));
// Close the filter group and gather results // Close the filter group and gather results
ioFilterGroupClose(this->filterGroup); ioFilterGroupClose(this->pub.filterGroup);
// Close the driver if there is a close function // Close the driver if there is a close function
if (this->interface.close != NULL) if (this->interface.close != NULL)
@ -254,19 +256,6 @@ ioWriteClose(IoWrite *this)
FUNCTION_LOG_RETURN_VOID(); FUNCTION_LOG_RETURN_VOID();
} }
/**********************************************************************************************************************************/
IoFilterGroup *
ioWriteFilterGroup(const IoWrite *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(IO_WRITE, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->filterGroup);
}
/**********************************************************************************************************************************/ /**********************************************************************************************************************************/
int int
ioWriteFd(const IoWrite *this) ioWriteFd(const IoWrite *this)

View File

@ -14,9 +14,30 @@ Object type
typedef struct IoWrite IoWrite; typedef struct IoWrite IoWrite;
#include "common/io/filter/group.h" #include "common/io/filter/group.h"
#include "common/io/write.intern.h"
#include "common/type/buffer.h" #include "common/type/buffer.h"
#include "common/type/object.h" #include "common/type/object.h"
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
typedef struct IoWritePub
{
MemContext *memContext; // Mem context
IoFilterGroup *filterGroup; // IO filters
} IoWritePub;
// Filter group. Filters must be set before open and cannot be reset
__attribute__((always_inline)) static inline IoFilterGroup *
ioWriteFilterGroup(IoWrite *const this)
{
ASSERT_INLINE(this != NULL);
return ((IoWritePub *)this)->filterGroup;
}
// File descriptor for the write object. Not all write objects have a file descriptor and -1 will be returned in that case.
int ioWriteFd(const IoWrite *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Functions Functions
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
@ -53,15 +74,6 @@ void ioWriteFlush(IoWrite *this);
// Close the IO and write any additional data that has not been written yet // Close the IO and write any additional data that has not been written yet
void ioWriteClose(IoWrite *this); void ioWriteClose(IoWrite *this);
/***********************************************************************************************************************************
Getters/Setters
***********************************************************************************************************************************/
// Filter group. Filters must be set before open and cannot be reset
IoFilterGroup *ioWriteFilterGroup(const IoWrite *this);
// File descriptor for the write object. Not all write objects have a file descriptor and -1 will be returned in that case.
int ioWriteFd(const IoWrite *this);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Destructor Destructor
***********************************************************************************************************************************/ ***********************************************************************************************************************************/

View File

@ -5,7 +5,7 @@ GCS Storage Read
#include "common/debug.h" #include "common/debug.h"
#include "common/io/http/client.h" #include "common/io/http/client.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -7,7 +7,7 @@ Posix Storage Read
#include <unistd.h> #include <unistd.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -9,7 +9,7 @@ Posix Storage File write
#include <utime.h> #include <utime.h>
#include "common/debug.h" #include "common/debug.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -4,7 +4,7 @@ Storage Read Interface Internal
#ifndef STORAGE_READ_INTERN_H #ifndef STORAGE_READ_INTERN_H
#define STORAGE_READ_INTERN_H #define STORAGE_READ_INTERN_H
#include "common/io/read.intern.h" #include "common/io/read.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Constructors Constructors

View File

@ -8,7 +8,7 @@ Remote Storage Read
#include "common/compress/helper.h" #include "common/compress/helper.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/io/read.intern.h" #include "common/io/read.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/convert.h" #include "common/type/convert.h"

View File

@ -5,7 +5,7 @@ Remote Storage File write
#include "common/compress/helper.h" #include "common/compress/helper.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "common/log.h" #include "common/log.h"
#include "common/memContext.h" #include "common/memContext.h"
#include "common/type/object.h" #include "common/type/object.h"

View File

@ -4,7 +4,7 @@ Storage Write Interface Internal
#ifndef STORAGE_WRITE_INTERN_H #ifndef STORAGE_WRITE_INTERN_H
#define STORAGE_WRITE_INTERN_H #define STORAGE_WRITE_INTERN_H
#include "common/io/write.intern.h" #include "common/io/write.h"
#include "version.h" #include "version.h"
/*********************************************************************************************************************************** /***********************************************************************************************************************************

View File

@ -316,7 +316,7 @@ testRun(void)
ioFilterGroupAdd(ioReadFilterGroup(bufferRead), ioTestFilterMultiplyNew("double", 2, 3, 'X')), ioFilterGroupAdd(ioReadFilterGroup(bufferRead), ioTestFilterMultiplyNew("double", 2, 3, 'X')),
" add filter to filter group"); " add filter to filter group");
TEST_RESULT_PTR( TEST_RESULT_PTR(
ioFilterGroupInsert(ioReadFilterGroup(bufferRead), 0, sizeFilter), bufferRead->filterGroup, ioFilterGroupInsert(ioReadFilterGroup(bufferRead), 0, sizeFilter), bufferRead->pub.filterGroup,
" add filter to filter group"); " add filter to filter group");
TEST_RESULT_VOID(ioFilterGroupAdd(ioReadFilterGroup(bufferRead), ioSizeNew()), " add filter to filter group"); TEST_RESULT_VOID(ioFilterGroupAdd(ioReadFilterGroup(bufferRead), ioSizeNew()), " add filter to filter group");
IoFilter *bufferFilter = ioBufferNew(); IoFilter *bufferFilter = ioBufferNew();
@ -572,8 +572,8 @@ testRun(void)
ioReadOpen(read); ioReadOpen(read);
TEST_RESULT_INT(ioReadFd(read), ((IoFdRead *)ioReadDriver(read))->fd, "check fd"); TEST_RESULT_INT(ioReadFd(read), ((IoFdRead *)ioReadDriver(read))->fd, "check fd");
TEST_RESULT_PTR(ioReadInterface(read), &read->interface, "check interface"); TEST_RESULT_PTR(ioReadInterface(read), &read->pub.interface, "check interface");
TEST_RESULT_PTR(ioReadDriver(read), read->driver, "check driver"); TEST_RESULT_PTR(ioReadDriver(read), read->pub.driver, "check driver");
// Read a string // Read a string
TEST_RESULT_STR_Z(ioReadLine(read), "test string 1", "read test string"); TEST_RESULT_STR_Z(ioReadLine(read), "test string 1", "read test string");
@ -581,9 +581,9 @@ testRun(void)
// Only part of the buffer is written before timeout // Only part of the buffer is written before timeout
Buffer *buffer = bufNew(16); Buffer *buffer = bufNew(16);
((IoFdRead *)read->driver)->timeout = 1; ((IoFdRead *)read->pub.driver)->timeout = 1;
TEST_RESULT_BOOL(ioReadReadyP(read), false, "read is not ready (without throwing error)"); TEST_RESULT_BOOL(ioReadReadyP(read), false, "read is not ready (without throwing error)");
((IoFdRead *)read->driver)->timeout = 1000; ((IoFdRead *)read->pub.driver)->timeout = 1000;
TEST_ERROR(ioRead(read, buffer), FileReadError, "timeout after 1000ms waiting for read from 'read test'"); TEST_ERROR(ioRead(read, buffer), FileReadError, "timeout after 1000ms waiting for read from 'read test'");
TEST_RESULT_UINT(bufSize(buffer), 16, "buffer is only partially read"); TEST_RESULT_UINT(bufSize(buffer), 16, "buffer is only partially read");

View File

@ -393,7 +393,7 @@ testRun(void)
hrnServerScriptAccept(tls); hrnServerScriptAccept(tls);
TEST_ASSIGN(session, ioClientOpen(client), "open client"); TEST_ASSIGN(session, ioClientOpen(client), "open client");
TlsSession *tlsSession = (TlsSession *)session->driver; TlsSession *tlsSession = (TlsSession *)session->pub.driver;
TEST_RESULT_INT(ioSessionFd(session), -1, "no fd for tls session"); TEST_RESULT_INT(ioSessionFd(session), -1, "no fd for tls session");
@ -445,11 +445,11 @@ testRun(void)
hrnServerScriptSleep(tls, 500); hrnServerScriptSleep(tls, 500);
output = bufNew(12); output = bufNew(12);
((IoFdRead *)((SocketSession *)tlsSession->ioSession->driver)->read->driver)->timeout = 100; ((IoFdRead *)((SocketSession *)tlsSession->ioSession->pub.driver)->read->pub.driver)->timeout = 100;
TEST_ERROR_FMT( TEST_ERROR_FMT(
ioRead(ioSessionIoRead(session), output), FileReadError, ioRead(ioSessionIoRead(session), output), FileReadError,
"timeout after 100ms waiting for read from '%s:%u'", strZ(hrnServerHost()), hrnServerPort(0)); "timeout after 100ms waiting for read from '%s:%u'", strZ(hrnServerHost()), hrnServerPort(0));
((IoFdRead *)((SocketSession *)tlsSession->ioSession->driver)->read->driver)->timeout = 5000; ((IoFdRead *)((SocketSession *)tlsSession->ioSession->pub.driver)->read->pub.driver)->timeout = 5000;
// ----------------------------------------------------------------------------------------------------------------- // -----------------------------------------------------------------------------------------------------------------
TEST_TITLE("second protocol exchange"); TEST_TITLE("second protocol exchange");