Initial implementation cwTcpSocket*

Standardized cwLex and cwLog namespaces.
This commit is contained in:
kpl 2019-12-27 16:52:45 -05:00
parent 85954c5498
commit 0ead6fa974
22 changed files with 1745 additions and 773 deletions

View File

@ -20,6 +20,9 @@ SRC += cwMidi.cpp cwMidiPort.cpp cwMidiAlsa.cpp
HDR += cwAudioBuf.h cwAudioDevice.h cwAudioDeviceAlsa.h
SRC += cwAudioBuf.cpp cwAudioDevice.cpp cwAudioDeviceAlsa.cpp cwAudioDeviceTest.cpp
HDR += cwTcpSocket.h cwTcpSocketSrv.h cwTcpSocketTest.h
SRC += cwTcpSocket.cpp cwTcpSocketSrv.cpp cwTcpSocketTest.cpp
LIBS = -lpthread -lwebsockets -lasound

View File

@ -16,4 +16,11 @@
- change cwMpScNbQueue so that it does not require 'new'.
- cwAudioBuf.cpp - the ch->fn in update() does not have the correct memory fence.
- change file names to match object names
- change all NULL's to nullptr
- implement kTcpFl in cwTcpSocket.cpp

View File

@ -44,6 +44,9 @@
*/
#define atomicUIntIncr( vRef, dVal ) std::atomic_fetch_add<unsigned>(vRef,dVal)
#define atomicUIntDecr( vRef, dVal ) std::atomic_fetch_sub<unsigned>(vRef,dVal)
namespace cw
{
namespace audio
@ -94,8 +97,8 @@ namespace cw
unsigned devCnt;
unsigned meterMs;
sample_t* zeroBuf; // buffer of zeros
unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
sample_t* zeroBuf; // buffer of zeros
unsigned zeroBufCnt; // max of all dspFrameCnt for all devices.
} cmApBuf;
cmApBuf _theBuf;
@ -411,8 +414,7 @@ cw::rc_t cw::audio::buf::update(
// advance the input channel buffer
cp->ii = n1>0 ? n1 : cp->ii + n0;
//cp->fn += pp->audioFramesCnt;
//cmThUIntIncr(&cp->fn,pp->audioFramesCnt);
std::atomic_fetch_add<unsigned>(&cp->fn,pp->audioFramesCnt);
atomicUIntIncr(&cp->fn,pp->audioFramesCnt);
}
}
@ -424,7 +426,7 @@ cw::rc_t cw::audio::buf::update(
for(i=0; i<outPktCnt; ++i)
{
device::audioPacket_t* pp = outPktArray + i;
cmApIO* op = _theBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
cmApIO* op = _theBuf.devArray[pp->devIdx].ioArray + kOutApIdx; // dest io recd
// if the base timestamp has not yet been set then set it.
if( op->timeStamp.tv_sec==0 && op->timeStamp.tv_nsec==0 )
@ -433,10 +435,10 @@ cw::rc_t cw::audio::buf::update(
// for each dest packet channel and enabled source channel
for(j=0; j<pp->chCnt; ++j)
{
cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
unsigned n0 = op->n - cp->oi; // first src segment
unsigned n1 = 0; // second src segment
volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
cmApCh* cp = op->chArray + pp->begChIdx + j; // dest ch
unsigned n0 = op->n - cp->oi; // first src segment
unsigned n1 = 0; // second src segment
volatile unsigned fn = cp->fn; // store fn because it may be changed by the client thread
// if the outgoing samples will underflow the buffer
if( pp->audioFramesCnt > fn )
@ -501,8 +503,7 @@ cw::rc_t cw::audio::buf::update(
// advance the output channel buffer
cp->oi = n1>0 ? n1 : cp->oi + n0;
//cp->fn -= pp->audioFramesCnt;
//cmThUIntDecr(&cp->fn,pp->audioFramesCnt);
std::atomic_fetch_sub<unsigned>(&cp->fn,pp->audioFramesCnt);
atomicUIntDecr(&cp->fn,pp->audioFramesCnt);
}
}
@ -750,7 +751,6 @@ void cw::audio::buf::getIO( unsigned iDevIdx, sample_t* iBufArray[], unsigned
if( oBufArray[i] != NULL )
memset( oBufArray[i], 0, byteCnt );
}
}
void cw::audio::buf::advance( unsigned devIdx, unsigned flags )
@ -768,8 +768,7 @@ void cw::audio::buf::advance( unsigned devIdx, unsigned flags )
{
cmApCh* cp = ioPtr->chArray + i;
cp->oi = (cp->oi + ioPtr->dspFrameCnt) % ioPtr->n;
//cmThUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
std::atomic_fetch_sub<unsigned>(&cp->fn,ioPtr->dspFrameCnt);
atomicUIntDecr(&cp->fn,ioPtr->dspFrameCnt);
}
@ -777,8 +776,7 @@ void cw::audio::buf::advance( unsigned devIdx, unsigned flags )
// count the number of samples input from this device
if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
{
//cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
std::atomic_fetch_add<unsigned>(&ioPtr->ioFrameCnt, ioPtr->dspFrameCnt);
atomicUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
}
}
@ -789,17 +787,14 @@ void cw::audio::buf::advance( unsigned devIdx, unsigned flags )
{
cmApCh* cp = ioPtr->chArray + i;
cp->ii = (cp->ii + ioPtr->dspFrameCnt) % ioPtr->n;
//cmThUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
std::atomic_fetch_add<unsigned>(&cp->fn,ioPtr->dspFrameCnt);
atomicUIntIncr(&cp->fn,ioPtr->dspFrameCnt);
}
// count the number of samples output from this device
if( ioPtr->timeStamp.tv_sec!=0 && ioPtr->timeStamp.tv_nsec!=0 )
{
//cmThUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
std::atomic_fetch_add<unsigned>(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
atomicUIntIncr(&ioPtr->ioFrameCnt,ioPtr->dspFrameCnt);
}
}
}
@ -865,17 +860,20 @@ void cw::audio::buf::report()
unsigned ii = 0;
unsigned oi = 0;
unsigned fn = 0;
sample_t m = 0;
for(k=0; k<ip->chCnt; ++k)
{
cmApCh* cp = ip->chArray + i;
ii += cp->ii;
oi += cp->oi;
fn += cp->fn;
m += _cmApMeterValue(cp);
}
cwLogInfo("%i : %s - i:%7i o:%7i f:%7i n:%7i err %s:%7i ",
cwLogInfo("%i : %s - i:%7i o:%7i f:%7i n:%7i err %s:%7i meter:%f",
i,j==0?"IN ":"OUT",
ii,oi,fn,ip->n, (j==0?"over":"under"), ip->faultCnt);
ii,oi,fn,ip->n, (j==0?"over ":"under"), ip->faultCnt, m/ip->chCnt);
}
}

View File

@ -5,26 +5,26 @@
// samples in a thread-safe manner.
//
// Usage example and testing code:
// See cmApBufTest() and cmAudioSysTest().
// \snippet cmApBuf.c cmApBufExample
// See audio::device::test()
// \snippet cwAudioBuf.c cwAudioBufExample
//
// Notes on channel flags:
// Disabled channels: kChFl is cleared
// cmApBufGet()
// get()
// in - return NULL buffer pointers
// out - return NULL buffer points
//
// cmApBufUpdate()
// update()
// in - incoming samples are set to 0.
// out - outgoing samples are set to 0.
//
// Muted channels: kMuteFl is set
// cmApBufUpdate()
// update()
// in - incoming samples are set to 0.
// out - outgoing samples are set to 0.
//
// Tone channels: kToneFl is set
// cmApBufUpdate()
// update()
// in - incoming samples are filled with a 1k sine tone
// out - outgoing samples are filled with a 1k sine tone
//)
@ -76,7 +76,7 @@ namespace cw
// This function is called asynchronously by the audio device driver to transfer incoming samples to the
// the buffer and to send outgoing samples to the DAC. This function is
// intended to be called from the audio port callback function (\see cmApCallbackPtr_t).
// intended to be called from the audio port callback function (\see auido::device::cbFunc_t).
// This function is thread-safe under the condition where the audio device uses
// different threads for input and output.
//

View File

@ -1,3 +1,24 @@
//( { file_desc: "Cross platform audio device interface." kw:[audio rt] }
//
// This interface provides data declarations for platform dependent
// audio I/O functions. The implementation for the functions are
// in platform specific modules. See cwAudioDeviceAlsa.cpp.
//
// ALSA Notes:
// Assign capture device to line or mic input:
// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Mic
// amixer -c 0 cset iface=MIXER,name='Input Source',index=0 Line
//
// -c 0 select the first card
// -iface=MIXER the cset is targetting the MIXER component
// -name='Input Source',index=0 the control to set is the first 'Input Source'
// Note that the 'Capture' control sets the input gain.
//
// See alsamixer for a GUI to accomplish the same thing.
//
//
//)
#ifndef cwAudioDevice_H
#define cwAudioDevice_H
@ -46,7 +67,7 @@ namespace cw
// In general it should be assmed that this call is made from a system thread which is not
// the same as the application thread.
// The usual thread safety precautions should therefore be taken if this function implementation
// interacts with data structures also handled by the application. The audio buffer class (\see cmApBuf.h)
// interacts with data structures also handled by the application. The audio buffer class (\see cwAudioBuf.h)
// is designed to provide a safe and efficient way to communicate between
// the audio thread and the application.
typedef void (*cbFunc_t)( audioPacket_t* inPktArray, unsigned inPktCnt, audioPacket_t* outPktArray, unsigned outPktCnt );
@ -79,7 +100,25 @@ namespace cw
unsigned deviceChannelCount( handle_t h, unsigned devIdx, bool inputFl );
double deviceSampleRate( handle_t h, unsigned devIdx );
unsigned deviceFramesPerCycle( handle_t h, unsigned devIdx, bool inputFl );
rc_t deviceSetup( handle_t h, unsigned devIdx, double sr, unsigned frmPerCycle, cbFunc_t cb, void* cbData );
// Configure a device.
// All devices must be setup before they are started.
// framesPerCycle is the requested number of samples per audio callback. The
// actual number of samples made from a callback may be smaller. See the note
// regarding this in audioPacket_t.
// If the device cannot support the requested configuration then the function
// will return an error code.
// If the device is started when this function is called then it will be
// automatically stopped and then restarted following the reconfiguration.
// If the reconfiguration fails then the device may not be restared.
rc_t deviceSetup(
handle_t h,
unsigned devIdx,
double sr,
unsigned frmPerCycle,
cbFunc_t cb,
void* cbData );
rc_t deviceStart( handle_t h, unsigned devIdx );
rc_t deviceStop( handle_t h, unsigned devIdx );
bool deviceIsStarted( handle_t h, unsigned devIdx );

View File

@ -110,7 +110,7 @@ namespace cw
if( alsaRC == 0 )
{
rc = logMsg( logGlobalHandle(), kError_LogLevel, func, fn, lineNumb, 0, kOpFailRC, fmt, vl0 );
rc = log::msg( log::globalHandle(), log::kError_LogLevel, func, fn, lineNumb, 0, kOpFailRC, fmt, vl0 );
}
else
{
@ -129,7 +129,7 @@ namespace cw
char msg1[n+1];
m = snprintf(msg1,n+1,fmt1,msg0,snd_strerror(alsaRC));
rc = logMsg( logGlobalHandle(), kError_LogLevel, func, fn, lineNumb, 0, kOpFailRC, "%s", msg1 );
rc = log::msg( log::globalHandle(), log::kError_LogLevel, func, fn, lineNumb, 0, kOpFailRC, "%s", msg1 );
}
va_end(vl0);
@ -367,6 +367,7 @@ namespace cw
int cnt = 0;
int err;
do
{
if((err = snd_pcm_open(pcmHPtr,devNameStr,inputFl ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,0)) < 0 )
@ -522,23 +523,48 @@ namespace cw
}
}
int NNN = 0;
void _rms( const char* devNameStr, const device::sample_t* s0p, const int* s1p, unsigned n )
{
double facc = 0.0;
int iacc = 0;
unsigned normN = 0;
unsigned zeroN = 0;
for(unsigned i=0; i<n; i+=2)
{
facc += s0p[i] * s0p[i];
iacc += s1p[i] * s1p[i];
if( s0p[i] == 0.0 )
zeroN += 1;
else
if( std::isnormal(s0p[i]) )
normN += 1;
}
printf("%i z:%i n:%i : %f %i : %s\n", n, zeroN, normN, facc/(n/2), iacc/(n/2), devNameStr);
}
// Returns count of frames written on success or < 0 on error;
// set smpPtr to NULL to write a buffer of silence
int _devWriteBuf( devRecd_t* drp, snd_pcm_t* pcmH, const device::sample_t* sp, unsigned chCnt, unsigned frmCnt, unsigned bits, unsigned sigBits )
{
int err = 0;
unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
unsigned smpCnt = chCnt * frmCnt;
unsigned byteCnt = bytesPerSmp * smpCnt;
int err = 0;
unsigned bytesPerSmp = (bits==24 ? 32 : bits)/8;
unsigned smpCnt = chCnt * frmCnt;
unsigned byteCnt = bytesPerSmp * smpCnt;
const device::sample_t* ep = sp + smpCnt;
char obuf[ byteCnt ];
char obuf[ byteCnt ];
//const device::sample_t* rms = sp;
// if no output was given then fill the device buffer with zeros
if( sp == NULL )
memset(obuf,0,byteCnt);
else
{
{
// otherwise convert the floating point samples to integers
switch( bits )
{
@ -575,13 +601,21 @@ namespace cw
int* dp = (int*)obuf;
while( sp < ep )
*dp++ = (int)(*sp++ * 0x7fffffff);
*dp++ = (int)(*sp++ * 0x7fffffff);
//*dp++ = (rand() - (RAND_MAX/2)) * 2;
}
break;
}
}
/*
NNN+=1;
if( NNN % 100 == 0)
{
_rms(drp->nameStr,rms,(const int*)obuf,smpCnt);
}
*/
// send the bytes to the device
err = snd_pcm_writei( pcmH, obuf, frmCnt );
@ -734,7 +768,7 @@ namespace cw
{
// callback to fill the buffer
drp->cbFunc(NULL,0,&pkt,1);
// note that the application may return fewer samples than were requested
err = _devWriteBuf(drp, pcmH, pkt.audioFramesCnt < frmCnt ? NULL : drp->oBuf,chCnt,frmCnt,drp->oBits,drp->oSigBits);

View File

@ -26,7 +26,7 @@ namespace cw
{
unsigned bufCnt; // 2=double buffering 3=triple buffering
unsigned chIdx; // first test channel
unsigned chCnt; // count of channels to test
//unsigned chCnt; // count of channels to test
unsigned framesPerCycle; // DSP frames per cycle
unsigned bufFrmCnt; // count of DSP frames used by the audio buffer (bufCnt * framesPerCycle)
unsigned bufSmpCnt; // count of samples used by the audio buffer (chCnt * bufFrmCnt)
@ -51,7 +51,8 @@ namespace cw
unsigned* ilog; // ilog[logCnt]
unsigned logIdx; // current log index
unsigned cbCnt; // count the callback
unsigned iCbCnt; // count the callback
unsigned oCbCnt;
} cmApPortTestRecd;
@ -215,7 +216,7 @@ namespace cw
// copy the incoming audio into an internal buffer where it can be picked up by _cpApCopyOut().
_cmApCopyIn( r, (sample_t*)inPktArray[i].audioBytesPtr, inPktArray[i].begChIdx, inPktArray[i].chCnt, inPktArray[i].audioFramesCnt );
}
++r->cbCnt;
++r->iCbCnt;
//printf("i %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
}
@ -252,7 +253,7 @@ namespace cw
//printf("o %4i in:%4i out:%4i\n",r->bufFullCnt,r->bufInIdx,r->bufOutIdx);
// count callbacks
++r->cbCnt;
++r->oCbCnt;
}
}
#endif
@ -304,10 +305,10 @@ namespace cw
void _cmApPortCb2( audioPacket_t* inPktArray, unsigned inPktCnt, audioPacket_t* outPktArray, unsigned outPktCnt )
{
for(unsigned i=0; i<inPktCnt; ++i)
static_cast<cmApPortTestRecd*>(inPktArray[i].cbArg)->cbCnt++;
static_cast<cmApPortTestRecd*>(inPktArray[i].cbArg)->iCbCnt++;
for(unsigned i=0; i<outPktCnt; ++i)
static_cast<cmApPortTestRecd*>(outPktArray[i].cbArg)->cbCnt++;
static_cast<cmApPortTestRecd*>(outPktArray[i].cbArg)->oCbCnt++;
buf::inputToOutput( _cmGlobalInDevIdx, _cmGlobalOutDevIdx );
@ -334,12 +335,12 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
runFl = _cmApGetOpt(argc,argv,"-p",0,true)?false:true;
r.srate = _cmApGetOpt(argc,argv,"-r",44100);
r.chIdx = _cmApGetOpt(argc,argv,"-a",0);
r.chCnt = _cmApGetOpt(argc,argv,"-c",2);
//r.chIdx = _cmApGetOpt(argc,argv,"-a",0);
//r.chCnt = _cmApGetOpt(argc,argv,"-c",2);
r.bufCnt = _cmApGetOpt(argc,argv,"-b",3);
r.framesPerCycle = _cmApGetOpt(argc,argv,"-f",512);
r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
//r.bufFrmCnt = (r.bufCnt*r.framesPerCycle);
//r.bufSmpCnt = (r.chCnt * r.bufFrmCnt);
r.logCnt = 100;
r.meterMs = 50;
@ -348,7 +349,7 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
unsigned ilog[r.logCnt];
r.inDevIdx = _cmGlobalInDevIdx = _cmApGetOpt(argc,argv,"-i",0);
r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",2);
r.outDevIdx = _cmGlobalOutDevIdx = _cmApGetOpt(argc,argv,"-o",0);
r.phase = 0;
r.frqHz = 2000;
r.bufInIdx = 0;
@ -359,9 +360,10 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
r.buf = buf;
r.log = log;
r.ilog = ilog;
r.cbCnt = 0;
cwLogInfo("Program cfg: %s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate);
r.iCbCnt = 0;
r.oCbCnt = 0;
//cwLogInfo("Program cfg: %s in:%i out:%i chidx:%i chs:%i bufs=%i frm=%i rate=%f",runFl?"exec":"rpt",r.inDevIdx,r.outDevIdx,r.chIdx,r.chCnt,r.bufCnt,r.framesPerCycle,r.srate);
// initialize the audio device interface
@ -388,11 +390,11 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
// report the current audio device configuration
for(i=0; i<deviceCount(h); ++i)
{
cwLogInfo("%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%f %s",i,deviceChannelCount(h,i,true),deviceFramesPerCycle(h,i,true),deviceChannelCount(h,i,false),deviceFramesPerCycle(h,i,false),deviceSampleRate(h,i),deviceLabel(h,i));
cwLogInfo("%i [in: chs=%i frames=%i] [out: chs=%i frames=%i] srate:%8.1f %s",i,deviceChannelCount(h,i,true),deviceFramesPerCycle(h,i,true),deviceChannelCount(h,i,false),deviceFramesPerCycle(h,i,false),deviceSampleRate(h,i),deviceLabel(h,i));
}
// report the current audio devices using the audio port interface function
report(h);
//report(h);
if( runFl )
{
@ -403,7 +405,7 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
buf::setup( r.outDevIdx, r.srate, r.framesPerCycle, r.bufCnt, deviceChannelCount(h,r.outDevIdx,true), r.framesPerCycle, deviceChannelCount(h,r.outDevIdx,false), r.framesPerCycle );
// setup the buffer for the input device
if( r.inDevIdx != r.outDevIdx )
//if( r.inDevIdx != r.outDevIdx )
buf::setup( r.inDevIdx, r.srate, r.framesPerCycle, r.bufCnt, deviceChannelCount(h,r.inDevIdx,true), r.framesPerCycle, deviceChannelCount(h,r.inDevIdx,false), r.framesPerCycle );
// setup an output device
@ -425,7 +427,11 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
cwLogInfo("Setup complete!");
cwLogInfo("q=quit O/o output tone, I/i input tone P/p pass s=buf report");
cwLogInfo("q=quit O/o output tone, I/i input tone, P/p pass M/m meter s=buf report");
//buf::enableTone( r.outDevIdx,-1,buf::kOutFl | buf::kEnableFl);
//buf::enableMeter(r.outDevIdx,-1,buf::kOutFl | buf::kEnableFl);
char c;
while((c=getchar()) != 'q')
{
@ -447,6 +453,12 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
case 'P':
buf::enablePass(r.outDevIdx,-1,buf::kOutFl | (c=='P'?buf::kEnableFl:0));
break;
case 'M':
case 'm':
buf::enableMeter( r.inDevIdx, -1, buf::kInFl | (c=='M'?buf::kEnableFl:0));
buf::enableMeter( r.outDevIdx, -1, buf::kOutFl | (c=='M'?buf::kEnableFl:0));
break;
case 's':
buf::report();
@ -480,7 +492,7 @@ cw::rc_t cw::audio::device::test( int argc, const char** argv )
//cmApFileFree();
// report the count of audio buffer callbacks
cwLogInfo("cb count:%i", r.cbCnt );
cwLogInfo("cb count: i:%i o:%i", r.iCbCnt, r.oCbCnt );
return rcSelect(rc,rc0,rc1,rc2);
}

View File

@ -18,7 +18,8 @@
#if defined(cwLINUX) || defined(cwOSX)
#define cwPOSIX_FILE_SYS
#include <time.h> // linux time.h
#include <time.h> // timespec
#include <netinet/in.h> // struct sockaddr_in
#define cwPathSeparatorChar '/'
#endif

956
cwLex.cpp

File diff suppressed because it is too large Load Diff

192
cwLex.h
View File

@ -6,131 +6,131 @@
namespace cw
{
// Predefined Lexer Id's
enum
namespace lex
{
kErrorLexTId, // 0 the lexer was unable to identify the current token
kUnknownLexTId, // 1 the token is of an unknown type (only used when kReturnUnknownLexFl is set)
kEofLexTId, // 2 the lexer reached the end of input
kSpaceLexTId, // 3 white space
kRealLexTId, // 4 real number (contains a decimal point or is in scientific notation)
kIntLexTId, // 5 decimal integer
kHexLexTId, // 6 hexidecimal integer
kIdentLexTId, // 7 identifier
kQStrLexTId, // 8 quoted string
kQCharLexTId, // 9 quoted char
kBlockCmtLexTId, // 10 block comment
kLineCmtLexTId, // 11 line comment
kUserLexTId // 12 user registered token (See lexRegisterToken().)
};
// Lexer control flags used with lexInit().
enum
{
kReturnSpaceLexFl = 0x01, //< Return space tokens
kReturnCommentsLexFl = 0x02, //< Return comment tokens
kReturnUnknownLexFl = 0x04, //< Return unknown tokens
kReturnQCharLexFl = 0x08, //< Return quoted characters
kUserDefPriorityLexFl= 0x10 //< User defined tokens take priority even if a kIdentLexTId token has a longer match
};
// Predefined Lexer Id's
enum
{
kErrorLexTId, // 0 the lexer was unable to identify the current token
kUnknownLexTId, // 1 the token is of an unknown type (only used when kReturnUnknownLexFl is set)
kEofLexTId, // 2 the lexer reached the end of input
kSpaceLexTId, // 3 white space
kRealLexTId, // 4 real number (contains a decimal point or is in scientific notation)
kIntLexTId, // 5 decimal integer
kHexLexTId, // 6 hexidecimal integer
kIdentLexTId, // 7 identifier
kQStrLexTId, // 8 quoted string
kQCharLexTId, // 9 quoted char
kBlockCmtLexTId, // 10 block comment
kLineCmtLexTId, // 11 line comment
kUserLexTId // 12 user registered token (See lexRegisterToken().)
};
// Lexer control flags used with lexInit().
enum
{
kReturnSpaceLexFl = 0x01, //< Return space tokens
kReturnCommentsLexFl = 0x02, //< Return comment tokens
kReturnUnknownLexFl = 0x04, //< Return unknown tokens
kReturnQCharLexFl = 0x08, //< Return quoted characters
kUserDefPriorityLexFl= 0x10 //< User defined tokens take priority even if a kIdentLexTId token has a longer match
};
typedef handle<struct lex_str> lexH_t;
extern lexH_t lexNullHandle;
typedef handle<struct lex_str> handle_t;
// Iniitalize the lexer and receive a lexer handle in return.
// Set cp to nullptr if the buffer will be later via lexSetTextBuffer();
// See the kXXXLexFl enum's above for possible flag values.
rc_t lexCreate( lexH_t& hRef, const char* cp, unsigned cn, unsigned flags );
// Iniitalize the lexer and receive a lexer handle in return.
// Set cp to nullptr if the buffer will be later via lexSetTextBuffer();
// See the kXXXLexFl enum's above for possible flag values.
rc_t create( handle_t& hRef, const char* cp, unsigned cn, unsigned flags );
// Finalize a lexer created by an earlier call to lexInit()
rc_t lexDestroy( lexH_t& hRef );
// Finalize a lexer created by an earlier call to lexInit()
rc_t destroy( handle_t& hRef );
// Rewind the lexer to the begining of the buffer (the same as post initialize state)
rc_t lexReset( lexH_t h );
// Rewind the lexer to the begining of the buffer (the same as post initialize state)
rc_t reset( handle_t h );
// Verify that a lexer handle is valid
bool lexIsValid( lexH_t h );
// Verify that a lexer handle is valid
bool isValid( handle_t h );
// Set a new text buffer and reset the lexer to the post initialize state.
rc_t lexSetTextBuffer( lexH_t h, const char* cp, unsigned cn );
rc_t lexSetFile( lexH_t h, const char* fn );
// Set a new text buffer and reset the lexer to the post initialize state.
rc_t setTextBuffer( handle_t h, const char* cp, unsigned cn );
rc_t setFile( handle_t h, const char* fn );
// Register a user defined token. The id of the first user defined token should be
// kUserLexTId+1. Neither the id or token text can be used by a previously registered
// or built-in token.
rc_t lexRegisterToken( lexH_t h, unsigned id, const char* token );
// Register a user defined token. The id of the first user defined token should be
// kUserLexTId+1. Neither the id or token text can be used by a previously registered
// or built-in token.
rc_t registerToken( handle_t h, unsigned id, const char* token );
// Register a user defined token recognition function. This function should return the count
// of initial, consecutive, characters in 'cp[cn]' which match its token pattern.
typedef unsigned (*lexUserMatcherPtr_t)( const char* cp, unsigned cn );
// Register a user defined token recognition function. This function should return the count
// of initial, consecutive, characters in 'cp[cn]' which match its token pattern.
typedef unsigned (*lexUserMatcherPtr_t)( const char* cp, unsigned cn );
rc_t lexRegisterMatcher( lexH_t h, unsigned id, lexUserMatcherPtr_t funcPtr );
rc_t registerMatcher( handle_t h, unsigned id, lexUserMatcherPtr_t funcPtr );
// Enable or disable the specified token type.
rc_t lexEnableToken( lexH_t h, unsigned id, bool enableFl );
// Enable or disable the specified token type.
rc_t enableToken( handle_t h, unsigned id, bool enableFl );
// Get and set the lexer filter flags kReturnXXXLexFl.
// These flags can be safely enabled and disabled between
// calls to lexGetNextToken().
unsigned lexFilterFlags( lexH_t h );
void lexSetFilterFlags( lexH_t h, unsigned flags );
// Get and set the lexer filter flags kReturnXXXLexFl.
// These flags can be safely enabled and disabled between
// calls to lexGetNextToken().
unsigned filterFlags( handle_t h );
void setFilterFlags( handle_t h, unsigned flags );
// Return the type id of the current token and advances to the next token
unsigned lexGetNextToken( lexH_t h );
// Return the type id of the current token and advances to the next token
unsigned getNextToken( handle_t h );
// Return the type id associated with the current token. This is the same value
// returned by the previous call to lexGetNextToken().
unsigned lexTokenId( lexH_t h );
// Return the type id associated with the current token. This is the same value
// returned by the previous call to lexGetNextToken().
unsigned tokenId( handle_t h );
// Return a pointer to the first character of text associated with the
// current token. The returned pointer directly references the text contained
// in the buffer given to the lexer in the call to lexInit(). The string
// is therefore not zero terminated. Use lexTokenCharCount() to get the
// length of the token string.
const char* lexTokenText( lexH_t h );
// Return a pointer to the first character of text associated with the
// current token. The returned pointer directly references the text contained
// in the buffer given to the lexer in the call to lexInit(). The string
// is therefore not zero terminated. Use lexTokenCharCount() to get the
// length of the token string.
const char* tokenText( handle_t h );
// Return the count of characters in the text associated with the current token.
// This is the only way to get this count since the string returned by
// lexTokenText() is not zero terminated.
unsigned lexTokenCharCount( lexH_t h );
// Return the count of characters in the text associated with the current token.
// This is the only way to get this count since the string returned by
// lexTokenText() is not zero terminated.
unsigned tokenCharCount( handle_t h );
// Return the value of the current token as an integer.
int lexTokenInt( lexH_t h );
// Return the value of the current token as an integer.
int tokenInt( handle_t h );
// Return the value of the current token as an unsigned integer.
unsigned lexTokenUInt( lexH_t h );
// Return the value of the current token as an unsigned integer.
unsigned tokenUInt( handle_t h );
// Return the value of the current token as a float.
float lexTokenFloat( lexH_t h );
// Return the value of the current token as a float.
float tokenFloat( handle_t h );
// Return the value of the current token as a double.
double lexTokenDouble( lexH_t h );
// Return the value of the current token as a double.
double tokenDouble( handle_t h );
// Return true if the current token is an int and it was suffixed
// with 'u' to indicate that it is unsigned.
bool lexTokenIsUnsigned( lexH_t h );
// Return true if the current token is an int and it was suffixed
// with 'u' to indicate that it is unsigned.
bool tokenIsUnsigned( handle_t h );
// Return true if the current token is a real and it was suffexed
// with 'f' to indicate that it is a single precision float.
bool lexTokenIsSinglePrecision( lexH_t h );
// Return true if the current token is a real and it was suffexed
// with 'f' to indicate that it is a single precision float.
bool tokenIsSinglePrecision( handle_t h );
// Return the line number associated with the current token
unsigned lexCurrentLineNumber( lexH_t h );
// Return the line number associated with the current token
unsigned currentLineNumber( handle_t h );
// Return the starting column of the current token
unsigned lexCurrentColumnNumber( lexH_t h );
// Return the starting column of the current token
unsigned currentColumnNumber( handle_t h );
// Return the RC code associated with the last error
unsigned lexErrorRC( lexH_t h );
// Return the RC code associated with the last error
unsigned errorRC( handle_t h );
// Return the label associated with a token id
const char* lexIdToLabel( lexH_t h, unsigned typeId );
// Lexer testing stub.
void lexTest( );
// Return the label associated with a token id
const char* idToLabel( handle_t h, unsigned typeId );
// Lexer testing stub.
void test( );
}
}
//)

View File

@ -6,46 +6,47 @@
namespace cw
{
typedef struct log_str
namespace log
{
logOutputCbFunc_t outCbFunc;
void* outCbArg;
logFormatCbFunc_t fmtCbFunc;
void* fmtCbArg;
unsigned level;
} log_t;
typedef struct log_str
{
logOutputCbFunc_t outCbFunc;
void* outCbArg;
logFormatCbFunc_t fmtCbFunc;
void* fmtCbArg;
unsigned level;
} log_t;
logHandle_t __logGlobalHandle__;
logHandle_t logNullHandle;
#define logHandleToPtr(h) handleToPtr<logHandle_t,log_t>(h)
idLabelPair_t logLevelLabelArray[] =
{
{ kDebug_LogLevel, "debug" },
{ kInfo_LogLevel, "info" },
{ kWarning_LogLevel, "warning" },
{ kError_LogLevel, "error" },
{ kFatal_LogLevel, "fatal" },
{ kInvalid_LogLevel, "<invalid>" }
};
handle_t __logGlobalHandle__;
idLabelPair_t logLevelLabelArray[] =
{
{ kDebug_LogLevel, "debug" },
{ kInfo_LogLevel, "info" },
{ kWarning_LogLevel, "warning" },
{ kError_LogLevel, "error" },
{ kFatal_LogLevel, "fatal" },
{ kInvalid_LogLevel, "<invalid>" }
};
log_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,log_t>(h); }
}
}
cw::rc_t cw::logCreate( logHandle_t& hRef, unsigned level, logOutputCbFunc_t outCbFunc, void* outCbArg, logFormatCbFunc_t fmtCbFunc, void* fmtCbArg )
cw::rc_t cw::log::create( handle_t& hRef, unsigned level, logOutputCbFunc_t outCbFunc, void* outCbArg, logFormatCbFunc_t fmtCbFunc, void* fmtCbArg )
{
rc_t rc;
if((rc = logDestroy(hRef)) != kOkRC)
if((rc = destroy(hRef)) != kOkRC)
return rc;
log_t* p = memAllocZ<log_t>();
p->outCbFunc = outCbFunc == nullptr ? logDefaultOutput : outCbFunc;
p->outCbFunc = outCbFunc == nullptr ? defaultOutput : outCbFunc;
p->outCbArg = outCbArg;
p->fmtCbFunc = fmtCbFunc == nullptr ? logDefaultFormatter : fmtCbFunc;
p->fmtCbFunc = fmtCbFunc == nullptr ? defaultFormatter : fmtCbFunc;
p->fmtCbArg = fmtCbArg;
p->level = level;
hRef.p = p;
@ -53,7 +54,7 @@ cw::rc_t cw::logCreate( logHandle_t& hRef, unsigned level, logOutputCbFunc_t ou
return rc;
}
cw::rc_t cw::logDestroy( logHandle_t& hRef )
cw::rc_t cw::log::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
@ -64,9 +65,9 @@ cw::rc_t cw::logDestroy( logHandle_t& hRef )
return rc;
}
cw::rc_t cw::logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl )
cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl )
{
log_t* p = logHandleToPtr(h);
log_t* p = _handleToPtr(h);
va_list vl1;
va_copy(vl1,vl);
@ -88,30 +89,30 @@ cw::rc_t cw::logMsg( logHandle_t h, unsigned level, const char* function, const
return rc;
}
cw::rc_t cw::logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... )
cw::rc_t cw::log::msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t returnCode, const char* fmt, ... )
{
rc_t rc;
va_list vl;
va_start(vl,fmt);
rc = logMsg( h, level, function, filename, line, systemErrorCode, returnCode, fmt, vl );
rc = msg( h, level, function, filename, line, systemErrorCode, returnCode, fmt, vl );
va_end(vl);
return rc;
}
void cw::logSetLevel( logHandle_t h, unsigned level )
void cw::log::setLevel( handle_t h, unsigned level )
{
log_t* p = logHandleToPtr(h);
log_t* p = _handleToPtr(h);
p->level = level;
}
unsigned cw::logLevel( logHandle_t h )
unsigned cw::log::level( handle_t h )
{
log_t* p = logHandleToPtr(h);
log_t* p = _handleToPtr(h);
return p->level;
}
const char* cw::logLevelToLabel( unsigned level )
const char* cw::log::levelToLabel( unsigned level )
{
const char* label;
if((label = idToLabel(logLevelLabelArray,level,kInvalid_LogLevel)) == nullptr)
@ -122,20 +123,20 @@ const char* cw::logLevelToLabel( unsigned level )
void cw::logDefaultOutput( void* arg, unsigned level, const char* text )
void cw::log::defaultOutput( void* arg, unsigned level, const char* text )
{
FILE* f = level >= kWarning_LogLevel ? stderr : stdout;
fprintf(f,"%s",text);
}
void cw::logDefaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned lineno, int sys_errno, rc_t rc, const char* msg )
void cw::log::defaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned lineno, int sys_errno, rc_t rc, const char* msg )
{
// TODO: This code is avoids the use of dynamic memory allocation but relies on stack allocation. It's a security vulnerability.
//
const char* systemLabel = sys_errno==0 ? "" : "System Error: ";
const char* systemMsg = sys_errno==0 ? "" : strerror(sys_errno);
const char* levelStr = logLevelToLabel(level);
const char* levelStr = levelToLabel(level);
const char* rcFmt = "rc:%i";
int rcn = snprintf(nullptr,0,rcFmt,rc);
@ -175,35 +176,33 @@ void cw::logDefaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outC
int m = snprintf(s,n+1,fmt,levelStr,msg,syStr,rcStr,loStr);
cwAssert(m==n);
outFunc(outCbArg,level,s);
outFunc(outCbArg,level,s);
}
cw::rc_t cw::logCreateGlobal( unsigned level, logOutputCbFunc_t outCb, void* outCbArg, logFormatCbFunc_t fmtCb, void* fmtCbArg )
cw::rc_t cw::log::createGlobal( unsigned level, logOutputCbFunc_t outCb, void* outCbArg, logFormatCbFunc_t fmtCb, void* fmtCbArg )
{
logHandle_t h;
handle_t h;
rc_t rc;
if((rc = logCreate(h, level, outCb, outCbArg, fmtCb, fmtCbArg )) == kOkRC )
logSetGlobalHandle(h);
if((rc = create(h, level, outCb, outCbArg, fmtCb, fmtCbArg )) == kOkRC )
setGlobalHandle(h);
return rc;
}
cw::rc_t cw::logDestroyGlobal()
cw::rc_t cw::log::destroyGlobal()
{
return logDestroy(__logGlobalHandle__);
return destroy(__logGlobalHandle__);
}
void cw::logSetGlobalHandle( logHandle_t h )
void cw::log::setGlobalHandle( handle_t h )
{
__logGlobalHandle__ = h;
}
cw::logHandle_t cw::logGlobalHandle()
cw::log::handle_t cw::log::globalHandle()
{
return __logGlobalHandle__;
}

109
cwLog.h
View File

@ -4,73 +4,74 @@
namespace cw
{
typedef enum
namespace log
{
kInvalid_LogLevel,
kDebug_LogLevel,
kInfo_LogLevel,
kWarning_LogLevel,
kError_LogLevel,
kFatal_LogLevel,
} logLevelId_t;
typedef enum
{
kInvalid_LogLevel,
kDebug_LogLevel,
kInfo_LogLevel,
kWarning_LogLevel,
kError_LogLevel,
kFatal_LogLevel,
} logLevelId_t;
typedef handle<struct log_str> logHandle_t;
extern logHandle_t logNullHandle;
typedef handle<struct log_str> handle_t;
typedef void (*logOutputCbFunc_t)( void* cbArg, unsigned level, const char* text );
typedef void (*logFormatCbFunc_t)( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg );
typedef void (*logOutputCbFunc_t)( void* cbArg, unsigned level, const char* text );
typedef void (*logFormatCbFunc_t)( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg );
rc_t logCreate( logHandle_t& hRef, unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr );
rc_t logDestroy( logHandle_t& hRef );
rc_t create( handle_t& hRef, unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr );
rc_t destroy( handle_t& hRef );
rc_t logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl );
rc_t logMsg( logHandle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, ... );
rc_t msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, va_list vl );
rc_t msg( handle_t h, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* fmt, ... );
void logSetLevel( logHandle_t h, unsigned level );
unsigned logLevel( logHandle_t h );
void setLevel( handle_t h, unsigned level );
unsigned level( handle_t h );
const char* logLevelToLabel( unsigned level );
const char* levelToLabel( unsigned level );
void logDefaultOutput( void* arg, unsigned level, const char* text );
void logDefaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg );
void defaultOutput( void* arg, unsigned level, const char* text );
void defaultFormatter( void* cbArg, logOutputCbFunc_t outFunc, void* outCbArg, unsigned level, const char* function, const char* filename, unsigned line, int systemErrorCode, rc_t rc, const char* msg );
rc_t logCreateGlobal( unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr );
rc_t logDestroyGlobal( );
rc_t createGlobal( unsigned level=kDebug_LogLevel, logOutputCbFunc_t outCb=nullptr, void* outCbArg=nullptr, logFormatCbFunc_t fmtCb=nullptr, void* fmtCbArg=nullptr );
rc_t destroyGlobal( );
void logSetGlobalHandle( logHandle_t h );
logHandle_t logGlobalHandle();
void setGlobalHandle( handle_t h );
handle_t globalHandle();
}
}
#define cwLogVDebugH(h,rc,fmt, vl) cw::logMsg( h, cw::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogDebugH( h,rc,fmt,...) cw::logMsg( h, cw::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVDebugH(h,rc,fmt, vl) cw::log::msg( h, cw::log::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogDebugH( h,rc,fmt,...) cw::log::msg( h, cw::log::kDebug_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVInfoH(h,fmt, vl) cw::logMsg( h, cw::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, vl )
#define cwLogInfoH( h,fmt,...) cw::logMsg( h, cw::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, ##__VA_ARGS__ )
#define cwLogVInfoH(h,fmt, vl) cw::log::msg( h, cw::log::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, vl )
#define cwLogInfoH( h,fmt,...) cw::log::msg( h, cw::log::kInfo_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, cw::kOkRC, fmt, ##__VA_ARGS__ )
#define cwLogVWarningH(h,rc,fmt, vl) cw::logMsg( h, cw::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogWarningH( h,rc,fmt,...) cw::logMsg( h, cw::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVWarningH(h,rc,fmt, vl) cw::log::msg( h, cw::log::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogWarningH( h,rc,fmt,...) cw::log::msg( h, cw::log::kWarning_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVErrorH(h,rc,fmt, vl) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogErrorH( h,rc,fmt,...) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVErrorH(h,rc,fmt, vl) cw::log::msg( h, cw::log::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogErrorH( h,rc,fmt,...) cw::log::msg( h, cw::log::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVSysErrorH(h,rc,sysRc,fmt, vl) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, vl )
#define cwLogSysErrorH( h,rc,sysRc,fmt,...) cw::logMsg( h, cw::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, ##__VA_ARGS__ )
#define cwLogVSysErrorH(h,rc,sysRc,fmt, vl) cw::log::msg( h, cw::log::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, vl )
#define cwLogSysErrorH( h,rc,sysRc,fmt,...) cw::log::msg( h, cw::log::kError_LogLevel, __FUNCTION__, __FILE__, __LINE__, sysRc, rc, fmt, ##__VA_ARGS__ )
#define cwLogVFatalH(h,rc,fmt, vl) cw::logMsg( h, cw::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogFatalH( h,rc,fmt,...) cw::logMsg( h, cw::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#define cwLogVFatalH(h,rc,fmt, vl) cw::log::msg( h, cw::log::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, vl )
#define cwLogFatalH( h,rc,fmt,...) cw::log::msg( h, cw::log::kFatal_LogLevel, __FUNCTION__, __FILE__, __LINE__, 0, rc, fmt, ##__VA_ARGS__ )
#ifdef cwLOG_DEBUG
#define cwLogVDebugRC(rc,fmt, vl) cwLogVDebugH( cw::logGlobalHandle(), (rc), (fmt), (vl) )
#define cwLogDebugRC( rc,fmt,...) cwLogDebugH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVDebugRC(rc,fmt, vl) cwLogVDebugH( cw::log::globalhandle(), (rc), (fmt), (vl) )
#define cwLogDebugRC( rc,fmt,...) cwLogDebugH( cw::log::globalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVDebug(fmt, vl) cwLogVDebugH( cw::logGlobalHandle(), cw::kOkRC, (fmt), (vl) )
#define cwLogDebug( fmt,...) cwLogDebugH( cw::logGlobalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ )
#define cwLogVDebug(fmt, vl) cwLogVDebugH( cw::log::globalHandle(), cw::kOkRC, (fmt), (vl) )
#define cwLogDebug( fmt,...) cwLogDebugH( cw::log::globalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ )
#else
@ -83,23 +84,23 @@ namespace cw
#endif
#define cwLogVInfo(fmt, vl) cwLogVInfoH( cw::logGlobalHandle(), (fmt), (vl) )
#define cwLogInfo( fmt,...) cwLogInfoH( cw::logGlobalHandle(), (fmt), ##__VA_ARGS__ )
#define cwLogVInfo(fmt, vl) cwLogVInfoH( cw::log::globalHandle(), (fmt), (vl) )
#define cwLogInfo( fmt,...) cwLogInfoH( cw::log::globalHandle(), (fmt), ##__VA_ARGS__ )
#define cwLogVWarningRC(rc,fmt, vl) cwLogVWarningH( cw::logGlobalHandle(), (rc), (fmt), (vl) )
#define cwLogWarningRC( rc,fmt,...) cwLogWarningH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVWarningRC(rc,fmt, vl) cwLogVWarningH( cw::log::globalHandle(), (rc), (fmt), (vl) )
#define cwLogWarningRC( rc,fmt,...) cwLogWarningH( cw::log::globalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVWarning(fmt, vl) cwLogVWarningH( cw::logGlobalHandle(), cw::kOkRC, (fmt), (vl) )
#define cwLogWarning( fmt,...) cwLogWarningH( cw::logGlobalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ )
#define cwLogVWarning(fmt, vl) cwLogVWarningH( cw::log::globalHandle(), cw::kOkRC, (fmt), (vl) )
#define cwLogWarning( fmt,...) cwLogWarningH( cw::log::globalHandle(), cw::kOkRC, (fmt), ##__VA_ARGS__ )
#define cwLogVSysError(rc,sysRC,fmt, vl) cwLogVSysErrorH( cw::logGlobalHandle(), (rc), (sysRC), (fmt), (vl) )
#define cwLogSysError( rc,sysRC,fmt,...) cwLogSysErrorH( cw::logGlobalHandle(), (rc), (sysRC), (fmt), ##__VA_ARGS__ )
#define cwLogVSysError(rc,sysRC,fmt, vl) cwLogVSysErrorH( cw::log::globalHandle(), (rc), (sysRC), (fmt), (vl) )
#define cwLogSysError( rc,sysRC,fmt,...) cwLogSysErrorH( cw::log::globalHandle(), (rc), (sysRC), (fmt), ##__VA_ARGS__ )
#define cwLogVError(rc,fmt, vl) cwLogVErrorH( cw::logGlobalHandle(), (rc), (fmt), (vl) )
#define cwLogError( rc,fmt,...) cwLogErrorH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVError(rc,fmt, vl) cwLogVErrorH( cw::log::globalHandle(), (rc), (fmt), (vl) )
#define cwLogError( rc,fmt,...) cwLogErrorH( cw::log::globalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVFatal(rc,fmt, vl) cwLogVFatalH( cw::logGlobalHandle(), (rc), (fmt), (vl) )
#define cwLogFatal( rc,fmt,...) cwLogFatalH( cw::logGlobalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#define cwLogVFatal(rc,fmt, vl) cwLogVFatalH( cw::log::globalHandle(), (rc), (fmt), (vl) )
#define cwLogFatal( rc,fmt,...) cwLogFatalH( cw::log::globalHandle(), (rc), (fmt), ##__VA_ARGS__ )
#endif

View File

@ -16,7 +16,7 @@ namespace cw
{
enum
{
kLCurlyLexTId = cw::kUserLexTId+1,
kLCurlyLexTId = cw::lex::kUserLexTId+1,
kRCurlyLexTId,
kLHardLexTId,
kRHardLexTId,
@ -38,7 +38,7 @@ namespace cw
{ kTrueLexTId, "true"},
{ kFalseLexTId, "false"},
{ kNullLexTId, "null" },
{ cw::kErrorLexTId,""}
{ lex::kErrorLexTId,""}
};
@ -98,8 +98,7 @@ namespace cw
{
for(unsigned i=0; i<indent; ++i)
printf(indentStr);
printf("%s",text);
printf("%s",text);
}
void _objTypePrintChild( const object_t* o, print_ctx_t& c, const char* eolStr=",\n", const char* indentStr=" " )
@ -244,19 +243,19 @@ namespace cw
return o;
}
rc_t _objSyntaxError( lexH_t lexH, const char* fmt, ... )
rc_t _objSyntaxError( lex::handle_t lexH, const char* fmt, ... )
{
va_list vl;
va_start(vl,fmt);
cwLogVError( kSyntaxErrorRC, fmt, vl );
cwLogError( kSyntaxErrorRC, "Error on line: %i.", lexCurrentLineNumber(lexH));
cwLogError( kSyntaxErrorRC, "Error on line: %i.", lex::currentLineNumber(lexH));
va_end(vl);
return kSyntaxErrorRC;
}
rc_t _objVerifyParentIsValueContainer( lexH_t lexH, const object_t* parent, const char* msg )
rc_t _objVerifyParentIsValueContainer( lex::handle_t lexH, const object_t* parent, const char* msg )
{
if( parent == nullptr )
return _objSyntaxError(lexH,"The parent node must always be valid.");
@ -289,7 +288,7 @@ namespace cw
return newNode;
}
object_t* _objCreateConainerNode( lexH_t lexH, object_t* parent, objTypeId_t tid )
object_t* _objCreateConainerNode( lex::handle_t lexH, object_t* parent, objTypeId_t tid )
{
if( _objVerifyParentIsValueContainer(lexH,parent,_objTypeIdToLabel(tid)) == kOkRC )
return _objAppendLeftMostNode( parent, _objAllocate( tid, parent ));
@ -390,28 +389,28 @@ void cw::object_t::print(const print_ctx_t* c) const
cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
{
lexH_t lexH;
rc_t rc;
unsigned lexFlags = 0;
unsigned lexId = kErrorLexTId;
object_t* cnp = _objAllocate(kRootTId,nullptr);
object_t* root = cnp;
lex::handle_t lexH;
rc_t rc;
unsigned lexFlags = 0;
unsigned lexId = lex::kErrorLexTId;
object_t* cnp = _objAllocate(kRootTId,nullptr);
object_t* root = cnp;
objRef = nullptr;
if((rc = lexCreate(lexH,s,textLength(s), lexFlags )) != kOkRC )
if((rc = lex::create(lexH,s,textLength(s), lexFlags )) != kOkRC )
return rc;
// setup the lexer with additional tokens
for(unsigned i=0; _objTokenArray[i].id != cw::kErrorLexTId; ++i)
if((rc = lexRegisterToken( lexH, _objTokenArray[i].id, _objTokenArray[i].label )) != kOkRC )
for(unsigned i=0; _objTokenArray[i].id != lex::kErrorLexTId; ++i)
if((rc = lex::registerToken( lexH, _objTokenArray[i].id, _objTokenArray[i].label )) != kOkRC )
{
rc = cwLogError(rc,"Object lexer token registration failed on token id:%i : '%s'",_objTokenArray[i].id, _objTokenArray[i].label);
goto errLabel;
}
// main parser loop
while((lexId = lexGetNextToken(lexH)) != cw::kErrorLexTId && (lexId != kEofLexTId) && (rc == kOkRC))
while((lexId = lex::getNextToken(lexH)) != lex::kErrorLexTId && (lexId != lex::kEofLexTId) && (rc == kOkRC))
{
//printf("Lex:%s\n",lexIdToLabel(lexH,lexId));
@ -450,16 +449,16 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
rc = _objSyntaxError(lexH,"Unexpected comma outside of 'array' or 'object'.");
break;
case kRealLexTId:
_objCreateValueNode( cnp, lexTokenDouble(lexH), "real" );
case lex::kRealLexTId:
_objCreateValueNode( cnp, lex::tokenDouble(lexH), "real" );
break;
case kIntLexTId:
_objCreateValueNode( cnp, lexTokenInt(lexH), "int" );
case lex::kIntLexTId:
_objCreateValueNode( cnp, lex::tokenInt(lexH), "int" );
break;
case kHexLexTId:
_objCreateValueNode( cnp, lexTokenInt(lexH), "int", kHexFl );
case lex::kHexLexTId:
_objCreateValueNode( cnp, lex::tokenInt(lexH), "int", kHexFl );
break;
case kTrueLexTId:
@ -474,8 +473,8 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
_objAppendLeftMostNode( cnp, _objAllocate( kNullTId, cnp ));
break;
case kIdentLexTId:
case kQStrLexTId:
case lex::kIdentLexTId:
case lex::kQStrLexTId:
{
// if the parent is an object then this string must be a pair label
@ -483,15 +482,15 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
cnp = _objAppendLeftMostNode( cnp, _objAllocate( kPairTId, cnp ));
char* v = memDuplStr(lexTokenText(lexH),lexTokenCharCount(lexH));
unsigned identFl = lexId == kIdentLexTId ? kIdentFl : 0;
char* v = memDuplStr(lex::tokenText(lexH),lex::tokenCharCount(lexH));
unsigned identFl = lexId == lex::kIdentLexTId ? kIdentFl : 0;
_objCreateValueNode<char*>( cnp, v, "string", identFl );
}
break;
case kEofLexTId:
case lex::kEofLexTId:
break;
default:
@ -509,7 +508,7 @@ cw::rc_t cw::objectFromString( const char* s, object_t*& objRef )
objRef = root;
errLabel:
rc_t rc0 = lexDestroy(lexH);
rc_t rc0 = lex::destroy(lexH);
return rc != kOkRC ? rc : rc0;

View File

@ -6,7 +6,7 @@ namespace cw
{
objType_t* _objIdToType( objTypeId_t tid );
object_t* _objAllocate( objTypeId_t tid=kInvalidTId, object_t* parent=NULL );
object_t* _objCreateConainerNode( lexH_t lexH, object_t* parent, objTypeId_t tid );
object_t* _objCreateConainerNode( lex::handle_t lexH, object_t* parent, objTypeId_t tid );
object_t* _objAppendLeftMostNode( object_t* parent, object_t* newNode );
template< typename T >

389
cwTcpSocket.cpp Normal file
View File

@ -0,0 +1,389 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h> // close
#include "cwTcpSocket.h"
#define cwSOCKET_SYS_ERR (-1)
#define cwSOCKET_NULL_SOCK (-1)
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
#endif
namespace cw
{
namespace net
{
namespace socket
{
enum
{
kIsConnectedFl = 0x01,
kIsBlockingFl = 0x02
};
typedef struct socket_str
{
int sockH;
unsigned flags;
unsigned recvBufByteCnt;
struct sockaddr_in sockaddr;
char ntopBuf[ INET_ADDRSTRLEN+1 ]; // use INET6_ADDRSTRLEN for IPv6
char hnameBuf[ HOST_NAME_MAX+1 ];
} socket_t;
inline socket_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,socket_t>(h); }
rc_t _destroy( socket_t* p )
{
// close the socket
if( p->sockH != cwSOCKET_NULL_SOCK )
{
errno = 0;
if( ::close(p->sockH) != 0 )
cwLogSysError(kOpFailRC,errno,"The socket close failed." );
p->sockH = cwSOCKET_NULL_SOCK;
}
memRelease(p);
return kOkRC;
}
rc_t _initAddr( socket_t* p, const char* addrStr, portNumber_t portNumber, struct sockaddr_in* retAddrPtr )
{
memset(retAddrPtr,0,sizeof(struct sockaddr_in));
if( portNumber == kInvalidPortNumber )
return cwLogError(kInvalidArgRC,"The port number %i cannot be used.",kInvalidPortNumber);
if( addrStr == NULL )
retAddrPtr->sin_addr.s_addr = htonl(INADDR_ANY);
else
{
errno = 0;
if(inet_pton(AF_INET,addrStr,&retAddrPtr->sin_addr) == 0 )
return cwLogSysError(kOpFailRC,errno, "The network address string '%s' could not be converted to a netword address structure.",cwStringNullGuard(addrStr) );
}
//retAddrPtr->sin_len = sizeof(struct sockaddr_in);
retAddrPtr->sin_family = AF_INET;
retAddrPtr->sin_port = htons(portNumber);
return kOkRC;
}
rc_t _connect( socket_t* p, const char* remoteAddr, portNumber_t remotePort )
{
struct sockaddr_in addr;
rc_t rc;
// create the remote address
if((rc = _initAddr(p, remoteAddr, remotePort, &addr )) != kOkRC )
return rc;
errno = 0;
// ... and connect this socket to the remote address/port
if( connect(p->sockH, (struct sockaddr*)&addr, sizeof(addr)) == cwSOCKET_SYS_ERR )
return cwLogSysError(kOpFailRC, errno, "Socket connect failed." );
p->flags = cwSetFlag(p->flags,kIsConnectedFl);
return rc;
}
}
}
}
cw::rc_t cw::net::socket::create(
handle_t& hRef,
portNumber_t port,
unsigned flags,
unsigned timeOutMs,
const char* remoteAddr,
portNumber_t remotePort )
{
rc_t rc;
if((rc = destroy(hRef)) != kOkRC )
return rc;
socket_t* p = memAllocZ<socket_t>();
p->sockH = cwSOCKET_NULL_SOCK;
// get a handle to the socket
if(( p->sockH = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ) ) == cwSOCKET_SYS_ERR )
return cwLogSysError(kOpFailRC, errno, "Socket create failed." );
// create the local address
if((rc = _initAddr(p, NULL, port, &p->sockaddr )) != kOkRC )
goto errLabel;
// bind the socket to a local address/port
if( (bind( p->sockH, (struct sockaddr*)&p->sockaddr, sizeof(p->sockaddr))) == cwSOCKET_SYS_ERR )
{
rc = cwLogSysError(kOpFailRC,errno,"Socket bind failed." );
goto errLabel;
}
// if a remote addr was given connect this socket to it
if( remoteAddr != NULL )
if((rc = _connect(p,remoteAddr,remotePort)) != kOkRC )
goto errLabel;
// if this socket should block
if( cwIsFlag(flags,kBlockingFl) )
{
struct timeval timeOut;
// set the socket time out
timeOut.tv_sec = timeOutMs/1000;
timeOut.tv_usec = (timeOutMs - (timeOut.tv_sec * 1000)) * 1000;
if( setsockopt( p->sockH, SOL_SOCKET, SO_RCVTIMEO, &timeOut, sizeof(timeOut) ) == cwSOCKET_SYS_ERR )
{
rc = cwLogSysError(kOpFailRC,errno, "Attempt to set the socket timeout failed." );
goto errLabel;
}
p->flags = cwSetFlag(p->flags,kIsBlockingFl);
}
else
{
int opts;
// get the socket options flags
if( (opts = fcntl(p->sockH,F_GETFL)) < 0 )
{
rc = cwLogSysError(kOpFailRC,errno, "Attempt to get the socket options flags failed." );
goto errLabel;
}
opts = (opts | O_NONBLOCK);
// set the socket options flags
if(fcntl(p->sockH,F_SETFL,opts) < 0)
{
rc = cwLogSysError(kOpFailRC,errno, "Attempt to set the socket to non-blocking failed." );
goto errLabel;
}
}
// if broadcast option was requested.
if( cwIsFlag(flags,kBroadcastFl) )
{
int bcastFl = 1;
if( setsockopt( p->sockH, SOL_SOCKET, SO_BROADCAST, &bcastFl, sizeof(bcastFl) ) == cwSOCKET_SYS_ERR )
{
rc = cwLogSysError(kOpFailRC,errno, "Attempt to set the socket broadcast attribute failed." );
goto errLabel;
}
}
errLabel:
if(rc != kOkRC )
_destroy(p);
else
hRef.set(p);
return rc;
}
cw::rc_t cw::net::socket::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
if( !hRef.isValid() )
return rc;
socket_t* p = _handleToPtr(hRef);
if((rc = _destroy(p)) != kOkRC )
return rc;
hRef.clear();
return rc;
}
cw::rc_t cw::net::socket::connect( handle_t h, const char* remoteAddr, portNumber_t remotePort )
{
socket_t* p = _handleToPtr(h);
return _connect(p,remoteAddr,remotePort);
}
cw::rc_t cw::net::socket::send( handle_t h, const void* data, unsigned dataByteCnt )
{
socket_t* p = _handleToPtr(h);
errno = 0;
if( cwIsFlag(p->flags,kIsConnectedFl) == false )
return cwLogError(kInvalidOpRC,"cmUdpSend() only works with connected sockets.");
if( ::send( p->sockH, data, dataByteCnt, 0 ) == cwSOCKET_SYS_ERR )
return cwLogSysError(kOpFailRC,errno,"Send failed.");
return kOkRC;
}
cw::rc_t cw::net::socket::send( handle_t h, const void* data, unsigned dataByteCnt, const struct sockaddr_in* remoteAddr )
{
socket_t* p = _handleToPtr(h);
errno = 0;
if( sendto(p->sockH, data, dataByteCnt, 0, (struct sockaddr*)remoteAddr, sizeof(*remoteAddr)) == cwSOCKET_SYS_ERR )
return cwLogSysError(kOpFailRC,errno,"Send to remote addr. failed.");
return kOkRC;
}
cw::rc_t cw::net::socket::send( handle_t h, const void* data, unsigned dataByteCnt, const char* remoteAddr, portNumber_t remotePort )
{
rc_t rc;
socket_t* p = _handleToPtr(h);
struct sockaddr_in addr;
if((rc = _initAddr(p,remoteAddr,remotePort,&addr)) != kOkRC )
return rc;
return send( h, data, dataByteCnt, &addr );
}
cw::rc_t cw::net::socket::recieve( handle_t h, char* data, unsigned dataByteCnt, unsigned* recvByteCntRef, struct sockaddr_in* fromAddr )
{
socket_t* p = _handleToPtr(h);
rc_t rc = kOkRC;
ssize_t retVal = 0;
socklen_t sizeOfRemoteAddr = fromAddr==NULL ? 0 : sizeof(struct sockaddr_in);
errno = 0;
if( recvByteCntRef != NULL )
*recvByteCntRef = 0;
if((retVal = recvfrom(p->sockH, data, dataByteCnt, 0, (struct sockaddr*)fromAddr, &sizeOfRemoteAddr )) == cwSOCKET_SYS_ERR )
return errno == EAGAIN ? kTimeOutRC : cwLogSysError(kOpFailRC,errno,"recvfrom() failed.");
if( recvByteCntRef != NULL )
*recvByteCntRef = retVal;
return rc;
}
cw::rc_t cw::net::socket::select_recieve(handle_t h, char* buf, unsigned bufByteCnt, unsigned timeOutMs, unsigned* recvByteCntRef, struct sockaddr_in* fromAddr )
{
rc_t rc = kOkRC;
socket_t* p = _handleToPtr(h);
fd_set rdSet;
struct timeval timeOut;
// setup the select() call
FD_ZERO(&rdSet);
FD_SET(p->sockH, &rdSet );
timeOut.tv_sec = timeOutMs/1000;
timeOut.tv_usec = (timeOutMs - (timeOut.tv_sec * 1000)) * 1000;
if( recvByteCntRef != nullptr )
*recvByteCntRef = 0;
// NOTE; select() takes the highest socket value plus one of all the sockets in all the sets.
switch( select(p->sockH+1,&rdSet,NULL,NULL,&timeOut) )
{
case -1: // error
if( errno != EINTR )
cwLogSysError(kOpFailRC,errno,"Select failed.");
break;
case 0: // select() timed out
break;
case 1: // (> 0) count of ready descripters
if( FD_ISSET(p->sockH,&rdSet) )
{
socklen_t addrByteCnt = fromAddr==nullptr ? 0 : sizeof(*fromAddr);
ssize_t retByteCnt;
errno = 0;
// recv the incoming msg into buf[]
if(( retByteCnt = recvfrom( p->sockH, buf, bufByteCnt, 0, (struct sockaddr*)fromAddr, &addrByteCnt )) == cwSOCKET_SYS_ERR )
rc = cwLogSysError(kOpFailRC,errno,"recvfrom() failed.");
else
{
// check for overflow
if( retByteCnt == bufByteCnt )
rc = cwLogError(kBufTooSmallRC,"The receive buffer requires more than %i bytes.",bufByteCnt);
if( recvByteCntRef != nullptr )
*recvByteCntRef = retByteCnt;
}
}
break;
default:
{ cwAssert(0); }
} // switch
return rc;
}
cw::rc_t cw::net::socket::initAddr( handle_t h, const char* addrStr, portNumber_t portNumber, struct sockaddr_in* retAddrPtr )
{
socket_t* p = _handleToPtr(h);
return _initAddr(p,addrStr,portNumber,retAddrPtr);
}
const char* cw::net::socket::addrToString( const struct sockaddr_in* addr, char* buf, unsigned bufN )
{
errno = 0;
if( inet_ntop(AF_INET, &(addr->sin_addr), buf, bufN) == NULL)
{
cwLogSysError(kOpFailRC,errno, "Network address to string conversion failed." );
return NULL;
}
buf[bufN-1]=0;
return buf;
}
bool cw::net::socket::addrIsEqual( const struct sockaddr_in* a0, const struct sockaddr_in* a1 )
{
return a0->sin_family == a1->sin_family
&& a0->sin_port == a1->sin_port
&& memcmp(&a0->sin_addr,&a1->sin_addr,sizeof(a0->sin_addr))==0;
}
const char* cw::net::socket::hostName( handle_t h )
{
socket_t* p = _handleToPtr(h);
errno = 0;
if( gethostname(p->hnameBuf,HOST_NAME_MAX) != 0 )
{
cwLogSysError(kOpFailRC,errno, "gethostname() failed." );
return NULL;
}
p->hnameBuf[HOST_NAME_MAX] = 0;
return p->hnameBuf;
}

85
cwTcpSocket.h Normal file
View File

@ -0,0 +1,85 @@
#ifndef cwTcpPort_H
#define cwTcpPort_H
namespace cw
{
namespace net
{
namespace socket
{
typedef handle<struct socket_str> handle_t;
typedef unsigned short portNumber_t;
enum
{
kNonBlockingFl = 0x00, // create a non-blocking socket
kBlockingFl = 0x01, // create a blocking socket
kTcpFl = 0x02, // create a TCP socket rather than a UDP socket
kBroadcastFl = 0x04
};
enum
{
// port 0 is reserved by and is therefore a convenient invalid port number
kInvalidPortNumber = 0
};
rc_t create( handle_t& hRef,
portNumber_t port,
unsigned flags,
unsigned timeOutMs = 100, // time out to use with recv() on blocking sockets
const char* remoteAddr = NULL,
portNumber_t remotePort = socket::kInvalidPortNumber );
rc_t destroy( handle_t& hRef );
// Set a destination address for this socket. Once a destination address is set
// the caller may use send() to communicate with the specified remote socket
// without having to specify a destination address on each call.
rc_t connect( handle_t h, const char* remoteAddr, portNumber_t port );
// Send a message to a remote UDP socket over a previously connected socket
rc_t send( handle_t h, const void* data, unsigned dataByteCnt );
// Send a message to a specific remote node over an unconnected socket.
// Use the function initAddr() to setup the 'sockaddr_in';
rc_t send( handle_t h, const void* data, unsigned dataByteCnt, const struct sockaddr_in* remoteAddr );
rc_t send( handle_t h, const void* data, unsigned dataByteCnt, const char* remoteAddr, portNumber_t port );
// Receive incoming messages by directly checking the internal
// socket for waiting data. This function is used to receive
// incoming data when the internal listening thread is not used.
// Note that if kBlockingFl was set in create() this call will
// block for available data or for 'timeOutMs' milliseconds,
// whichever comes first (as set in create()). If
// kNonBlockingFl was set in create() then the function will
// return immediately if no incoming messages are waiting. If
// recvByteCntRef is valid (non-NULL) then it is set to the
// length of the received message or 0 if no msg was received.
rc_t recieve(handle_t h, char* data, unsigned dataByteCnt, unsigned* recvByteCntRef=nullptr, struct sockaddr_in* fromAddr=nullptr );
//
rc_t select_recieve(handle_t h, char* buf, unsigned bufByteCnt, unsigned timeOutMs, unsigned* recvByteCntRef=nullptr, struct sockaddr_in* fromAddr=nullptr );
// Prepare a struct sockadddr_in for use with send()
rc_t initAddr( handle_t h, const char* addrStr, portNumber_t portNumber, struct sockaddr_in* retAddrPtr );
const char* addrToString( const struct sockaddr_in* addr, char* buf, unsigned bufN=INET_ADDRSTRLEN );
bool addrIsEqual( const struct sockaddr_in* addr0, const struct sockaddr_in* addr1 );
const char* hostName( handle_t h );
}
}
}
#endif

151
cwTcpSocketSrv.cpp Normal file
View File

@ -0,0 +1,151 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwThread.h"
#include "cwTcpSocket.h"
#include "cwTcpSocketSrv.h"
namespace cw
{
namespace net
{
namespace srv
{
typedef struct socksrv_str
{
socket::handle_t sockH;
thread::handle_t threadH;
cbFunc_t cbFunc;
void* cbArg;
unsigned timeOutMs;
char* recvBuf;
unsigned recvBufByteCnt;
} socksrv_t;
inline socksrv_t* _handleToPtr( handle_t h ) { return handleToPtr<handle_t,socksrv_t>(h); }
rc_t _destroy( socksrv_t* p )
{
rc_t rc;
if((rc = thread::destroy(p->threadH)) != kOkRC )
return rc;
if((rc = socket::destroy(p->sockH)) != kOkRC )
return rc;
memRelease(p->recvBuf);
memRelease(p);
return rc;
}
bool _threadFunc( void* arg )
{
socksrv_t* p = static_cast<socksrv_t*>(arg);
unsigned rcvByteCnt = 0;
struct sockaddr_in fromAddr;
if( select_recieve(p->sockH, p->recvBuf, p->recvBufByteCnt, p->timeOutMs, &rcvByteCnt, &fromAddr ) == kOkRC )
if( rcvByteCnt>0 && p->cbFunc != nullptr )
p->cbFunc( p->cbArg, p->recvBuf, rcvByteCnt, &fromAddr );
return true;
}
}
}
}
cw::rc_t cw::net::srv::create(
handle_t& hRef,
socket::portNumber_t port,
unsigned flags,
cbFunc_t cbFunc,
void* cbArg,
unsigned recvBufByteCnt,
unsigned timeOutMs,
const char* remoteAddr,
socket::portNumber_t remotePort )
{
rc_t rc;
if((rc = destroy(hRef)) != kOkRC )
return rc;
socksrv_t* p = memAllocZ<socksrv_t>();
if((rc = socket::create( p->sockH, port, socket::kNonBlockingFl, 0, remoteAddr, remotePort )) != kOkRC )
goto errLabel;
if((rc = thread::create( p->threadH, _threadFunc, p )) != kOkRC )
goto errLabel;
p->recvBuf = memAllocZ<char>( recvBufByteCnt );
p->recvBufByteCnt = recvBufByteCnt;
p->cbFunc = cbFunc;
p->cbArg = cbArg;
errLabel:
if( rc == kOkRC )
hRef.set(p);
else
_destroy(p);
return rc;
}
cw::rc_t cw::net::srv::destroy( handle_t& hRef )
{
rc_t rc = kOkRC;
if( !hRef.isValid() )
return rc;
socksrv_t* p = _handleToPtr(hRef);
if((rc = _destroy(p)) != kOkRC )
return rc;
hRef.clear();
return rc;
}
cw::thread::handle_t cw::net::srv::threadHandle( handle_t h )
{
socksrv_t* p = _handleToPtr(h);
return p->threadH;
}
cw::net::socket::handle_t cw::net::srv::socketHandle( handle_t h )
{
socksrv_t* p = _handleToPtr(h);
return p->sockH;
}
cw::rc_t cw::net::srv::start( handle_t h )
{
socksrv_t* p = _handleToPtr(h);
return thread::pause( p->threadH, thread::kWaitFl );
}
cw::rc_t cw::net::srv::pause( handle_t h )
{
socksrv_t* p = _handleToPtr(h);
return thread::pause( p->threadH, thread::kWaitFl|thread::kPauseFl );
}
cw::rc_t cw::net::srv::send( handle_t h, const void* data, unsigned dataByteCnt )
{
socksrv_t* p = _handleToPtr(h);
return socket::send(p->sockH,data,dataByteCnt);
}
cw::rc_t cw::net::srv::send( handle_t h, const void* data, unsigned dataByteCnt, const struct sockaddr_in* remoteAddr )
{
socksrv_t* p = _handleToPtr(h);
return socket::send(p->sockH,data,dataByteCnt,remoteAddr);
}
cw::rc_t cw::net::srv::send( handle_t h, const void* data, unsigned dataByteCnt, const char* remoteAddr, socket::portNumber_t remotePort )
{
socksrv_t* p = _handleToPtr(h);
return socket::send(p->sockH,data,dataByteCnt,remoteAddr,remotePort);
}

44
cwTcpSocketSrv.h Normal file
View File

@ -0,0 +1,44 @@
#ifndef cwTcpSocketSrv_H
#define cwTcpSocketSrv_H
namespace cw
{
namespace net
{
namespace srv
{
typedef void (*cbFunc_t)( void* cbArg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr );
typedef handle< struct socksrv_str > handle_t;
rc_t create( handle_t& hRef, //
socket::portNumber_t port, // local port number
unsigned flags, // see socket::flags
cbFunc_t cbFunc, // callback for received messages
void* cbArg, // callback arg
unsigned recvBufByteCnt = 1024,// recieve buffer size
unsigned timeOutMs = 100, // time out to use with recv() on thread select()
const char* remoteAddr = NULL,
socket::portNumber_t remotePort = socket::kInvalidPortNumber );
rc_t destroy( handle_t& hRef );
thread::handle_t threadHandle( handle_t h );
socket::handle_t socketHandle( handle_t h );
rc_t start( handle_t h );
rc_t pause( handle_t h );
// Send a message to a remote UDP socket over a previously connected socket
rc_t send( handle_t h, const void* data, unsigned dataByteCnt );
// Send a message to a specific remote node over an unconnected socket.
// Use the function initAddr() to setup the 'sockaddr_in';
rc_t send( handle_t h, const void* data, unsigned dataByteCnt, const struct sockaddr_in* remoteAddr );
rc_t send( handle_t h, const void* data, unsigned dataByteCnt, const char* remoteAddr, socket::portNumber_t port );
}
}
}
#endif

155
cwTcpSocketTest.cpp Normal file
View File

@ -0,0 +1,155 @@
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwMem.h"
#include "cwThread.h"
#include "cwTcpSocket.h"
#include "cwTcpSocketSrv.h"
#include "cwTcpSocketTest.h"
namespace cw
{
namespace net
{
namespace socket
{
typedef struct app_str
{
unsigned recvBufByteN;
handle_t sockH;
thread::handle_t threadH;
unsigned cbN;
} app_t;
bool _threadFunc( void* arg )
{
rc_t rc;
app_t* app = static_cast<app_t*>(arg);
struct sockaddr_in fromAddr;
char addrBuf[ INET_ADDRSTRLEN ];
char buf[ app->recvBufByteN ];
unsigned recvBufByteN = 0;
if((rc = recieve( app->sockH, buf, app->recvBufByteN, &recvBufByteN, &fromAddr )) == kOkRC )
{
addrToString( &fromAddr, addrBuf );
printf("%i %s from %s\n", recvBufByteN, buf, addrBuf );
}
app->cbN += 1;
if( app->cbN % 10 == 0)
{
printf(".");
fflush(stdout);
}
return true;
}
}
}
}
cw::rc_t cw::net::socket::test( portNumber_t localPort, const char* remoteAddr, portNumber_t remotePort )
{
rc_t rc;
unsigned timeOutMs = 100;
const unsigned sbufN = 31;
char sbuf[ sbufN+1 ];
app_t app;
app.cbN = 0;
app.recvBufByteN = sbufN+1;
if((rc = create(app.sockH,localPort, kBlockingFl,timeOutMs, NULL, kInvalidPortNumber )) != kOkRC )
return rc;
if((rc = thread::create( app.threadH, _threadFunc, &app )) != kOkRC )
goto errLabel;
if((rc = thread::unpause( app.threadH )) != kOkRC )
goto errLabel;
while( true )
{
printf("? ");
if( std::fgets(sbuf,sbufN,stdin) == sbuf )
{
printf("Sending:%s",sbuf);
send(app.sockH, sbuf, strlen(sbuf)+1, remoteAddr, remotePort );
if( strcmp(sbuf,"quit\n") == 0)
break;
}
}
errLabel:
rc_t rc0 = thread::destroy(app.threadH);
rc_t rc1 = destroy(app.sockH);
return rcSelect(rc,rc0,rc1);
}
namespace cw
{
namespace net
{
namespace srv
{
typedef struct app_str
{
handle_t srvH;
unsigned cbN;
} app_t;
void srvRecieveCallback( void* arg, const char* data, unsigned dataByteCnt, const struct sockaddr_in* fromAddr )
{
app_t* p = static_cast<app_t*>(arg);
char addrBuf[ INET_ADDRSTRLEN ];
socket::addrToString( fromAddr, addrBuf, INET_ADDRSTRLEN );
p->cbN += 1;
printf("%i %s %s", p->cbN, addrBuf, data );
}
}
}
}
cw::rc_t cw::net::srv::test( socket::portNumber_t localPort, const char* remoteAddr, socket::portNumber_t remotePort )
{
rc_t rc;
unsigned recvBufByteCnt = 1024;
unsigned timeOutMs = 100;
const unsigned sbufN = 31;
char sbuf[ sbufN+1 ];
app_t app;
app.cbN = 0;
if((rc = srv::create(app.srvH, localPort, socket::kBlockingFl, srvRecieveCallback, &app, recvBufByteCnt, timeOutMs, NULL, socket::kInvalidPortNumber )) != kOkRC )
return rc;
if((rc = srv::start( app.srvH )) != kOkRC )
goto errLabel;
while( true )
{
printf("? ");
if( std::fgets(sbuf,sbufN,stdin) == sbuf )
{
printf("Sending:%s",sbuf);
send(app.srvH, sbuf, strlen(sbuf)+1, remoteAddr, remotePort );
if( strcmp(sbuf,"quit\n") == 0)
break;
}
}
errLabel:
rc_t rc0 = destroy(app.srvH);
return rcSelect(rc,rc0);
}

21
cwTcpSocketTest.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef cwTcpSocketTest_H
#define cwTcpSocketTest_H
namespace cw
{
namespace net
{
namespace socket
{
rc_t test( portNumber_t localPort, const char* remoteAddr, portNumber_t remotePort );
}
namespace srv
{
rc_t test( socket::portNumber_t localPort, const char* remoteAddr, socket::portNumber_t remotePort );
}
}
}
#endif

View File

@ -20,6 +20,9 @@
#include "cwAudioDeviceTest.h"
#include "cwAudioDeviceAlsa.h"
#include "cwAudioBuf.h"
#include "cwTcpSocket.h"
#include "cwTcpSocketSrv.h"
#include "cwTcpSocketTest.h"
#include <iostream>
@ -131,6 +134,33 @@ void audioBufTest( cw::object_t* cfg, int argc, const char* argv[] ) { cw::
void audioDevTest( cw::object_t* cfg, int argc, const char* argv[] ) { cw::audio::device::test( argc, argv ); }
void audioDevAlsaTest( cw::object_t* cfg, int argc, const char* argv[] ) { cw::audio::device::alsa::report(); }
void socketTest( cw::object_t* cfg, int argc, const char* argv[] )
{
if( argc >= 3 )
{
unsigned short localPort = atoi(argv[1]);
unsigned short remotePort = atoi(argv[2]);
printf("local:%i remote:%i\n", localPort, remotePort);
cw::net::socket::test( localPort, "127.0.0.1", remotePort );
}
}
void socketSrvTest( cw::object_t* cfg, int argc, const char* argv[] )
{
if( argc >= 3 )
{
unsigned short localPort = atoi(argv[1]);
unsigned short remotePort = atoi(argv[2]);
printf("local:%i remote:%i\n", localPort, remotePort);
cw::net::srv::test( localPort, "127.0.0.1", remotePort );
}
}
void stubTest( cw::object_t* cfg, int argc, const char* argv[] )
{
typedef struct v_str
@ -170,6 +200,8 @@ int main( int argc, const char* argv[] )
{ "audioBuf", audioBufTest },
{ "audioDev",audioDevTest },
{ "audioDevAlsa", audioDevAlsaTest },
{ "socket", socketTest },
{ "socketSrv", socketSrvTest },
{ "stub", stubTest },
{ nullptr, nullptr }
};
@ -180,7 +212,7 @@ int main( int argc, const char* argv[] )
const char* mode = argc > 2 ? argv[2] : nullptr;
cw::logCreateGlobal();
cw::log::createGlobal();
// if valid command line args were given and the cfg file was successfully read
if( cfgFn != nullptr && mode != nullptr && objectFromFile( cfgFn, cfg ) == cw::kOkRC )
@ -201,7 +233,7 @@ int main( int argc, const char* argv[] )
cfg->free();
}
cw::logDestroyGlobal();
cw::log::destroyGlobal();
return 0;
}

View File

@ -1,4 +1,4 @@
valgrind --track-origins=yes --leak-check=yes --log-file=vg0.txt ./cw_rt $1 $2 $3
valgrind --track-origins=yes --leak-check=yes --log-file=vg0.txt ./cw_rt $1 $2 $3 $4