Chitika

Wednesday, December 7, 2011

Shift characters left in string

Recently I have to received data from serial port, but data was asynchronous, so I have to match the sentinal value in the string. I read a buffer then I keep reading one character at a time and shifting string left one character and searching string to find the sentinal value, following is the function to shift characters left:


char *shiftLeft(char *str, int count) {

        char *savPtr=str;
        int len = strlen(str);

        while(*str) {

                if(str+count <= savPtr + len) {
                        *str = *(str+count);
                        str++;
                } else {
                        *str = 0;
                }
        }

        return savPtr;
}

Thursday, November 3, 2011

ASP file upload

Use following HTML for file upload:

  <form name="frmSend" method="POST" enctype="multipart/form-data" accept-charset="utf-8">
            Bulk Upload: <input name="bulkfile" type="file" />
            <input type="submit" value="Upload" / id=submit1 name=submit1>
</form>

Obtain zip file from http://www.freeaspupload.net/freeaspupload/download.asp. Extract it and place freeASPUpload.asp in the directory containing scripts.

I had to comment out /disable line 122

Open the script to have upload functionality and add following line:

'At start of script


<%@ Language=VBScript %>
<%

    Response.Expires = -1
    Server.ScriptTimeout = 600
    ' All communication must be in UTF-8, including the response back from the request
    Session.CodePage  = 65001

.
.
.
%>

'then

<!-- #include file="freeaspupload.asp" -->

Dim uploadsDirVar
 uploadsDirVar = Session("UploadPath")

            if Request.ServerVariables("REQUEST_METHOD") = "POST" then
           
                Dim Upload, origName, savName, fullName
               
                Set Upload = New FreeASPUpload
                Upload.SaveOne uploadsDirVar,0,origName,savName

ASP/VBS reading excel file

Following is the code to read excel file, inspired from http://forums.aspfree.com/code-bank-54/classic-asp-read-excel-file-into-recordset-141448.html

Dim objConnXls, objRSXls, strSQLXls

Set objConnXls = Server.CreateObject("ADODB.Connection")
objConnXls.Open "DRIVER={Microsoft Excel Driver (*.xls)}; IMEX=1; HDR=NO; "& _
"Excel 8.0; DBQ=" & fullName & "; "
           
strSQLXls = "select * from [data]"

set objRSXls = objConnXls.Execute(strSQLXls)

'Fields can be processed as follows:

Dim username, userpassword, firstname, lastname, gender, profession_id, email, dob, country
                   
 username = objRSXls.Fields(0).value
 userpassword = objRSXls.Fields(1).value
 firstname = objRSXls.Fields(2).value
 lastname = objRSXls.Fields(3).value
 gender = objRSXls.Fields(4).value
 profession_id = objRSXls.Fields(5).value
 email = objRSXls.Fields(6).value
 dob = objRSXls.Fields(7).value
 country = objRSXls.Fields(8).value

objRSXls.Close

C++: CGI GET Request

GET request is easy to process, as Query String is passed in environment variable.
Follow is the code to process GET Request:

void ProcessQueryString(map<string,string>& query) {
    char *queryStr = getenv("QUERY_STRING");

    char *ptr=NULL;
    char *savPtr;
    ptr=strtok_r(queryStr, "&=", &savPtr);
    while (ptr != NULL) {
        string key(ptr);
        ptr = strtok_r(NULL, "&=", &savPtr);
        if(ptr!=NULL && strlen(ptr) > 0) {
            string value(ptr);
            query[key]=value;
        }
        ptr=strtok_r(NULL, "&=", &savPtr);

    }
}

C++: CGI POST Data

Following is the code to process HTTP-Post request, it stores the result in map<string, string>, idea is taken from http://www.openroad.org/cgihelp/cgi.html. To decode code from HTTP encoding to is taken from http://www.cs.tut.fi/~jkorpela/forms/cgic.html.


void ProcessPostQueryString(map<string,string>& query) {


        //FILE *fp = fopen("post.txt", "w+");
        char tmp[5*1024];

        char *ptmp = getenv("CONTENT_LENGTH");

        if(ptmp == NULL || strlen(ptmp) < 1) {
                return;
        }


        int contentLength = atoi(getenv("CONTENT_LENGTH"));
        char queryStr[5*1024];

        //sprintf(tmp, "content length[%d]\n", contentLength);
        //fputs(tmp, fp);
        //fflush(fp);

        if(contentLength > sizeof(queryStr)) {

                throw "only 5K of Post query string supported";
        }

        //fputs("post data: ", fp);
        int length=contentLength;
        //while(length > 0)
        {
                int nread = fread(queryStr, length, 1, stdin);
                //fputs(queryStr, fp);

                length -= nread;
        }
        queryStr[contentLength] = 0;

        //fputs("\n", fp);
        //fflush(fp);

        char *ptrSentence=NULL,
                 *ptrWord=NULL;
        char *savPtr1, *savPtr2;
        ptrSentence=strtok_r(queryStr, "&", &savPtr1);
        while (ptrSentence != NULL) {


                //cout << '[' << ptrSentence << ']' << endl;
                ptrWord = strtok_r(ptrSentence, "=", &savPtr2);
                if(ptrWord == NULL && strlen(ptrWord) < 1)  continue;
                string key(ptrWord);
                ptrWord = strtok_r(NULL, "=", &savPtr2);
                if(ptrWord !=NULL && strlen(ptrWord) > 0) {

                        int len=strlen(ptrWord);
                        HTMLDecode(ptrWord, ptrWord+len, tmp);
                        tmp[len]=0;

                        string value(tmp);
                        query[key]=value;

                        sprintf(tmp, "(%s)=(%s)\n", key.c_str(), value.c_str());
                        //fputs(tmp, fp);
                        //fflush(fp);
                }

                ptrSentence=strtok_r(NULL, "&", &savPtr1);
                if(ptrSentence==NULL)  break;

        }

        //fclose(fp);
}

Following is the code to decode HTML encoded string:

int HTMLDecode(char *src, char *last, char *dest)
{
    int sz=0;
 for(; src != last; src++, dest++, sz++)
   if(*src == '+')
     *dest = ' ';
   else if(*src == '%') {
     int code;
     sscanf(src+1, "%2x", &code) != 1;
     *dest = code;
     src +=2; }
   else
     *dest = *src;

 *dest=0;
 return sz;
}

Wednesday, October 26, 2011

C++: Load configurations


Header:
#include <cstdio>
#include <cstring> 
#include <map>
#include <fstream>
#include <algorithm>

using namespace std;

void LoadConfig(const char *fileName);
string GetConf(string key);
int GetIntConf(string key);

Definition:

char *strtrim(char *&str) {

        int len = strlen(str);
        char *ptr = str+len-1;
        while((*ptr == ' ' ||
                        *ptr == '\t' ||
                        *ptr == '\r' ||
                        *ptr == '\n') &&
                        str <= ptr) {
                ptr--;
        }
        *(ptr+1) = 0;

        ptr=str;
        while((*ptr == ' ' ||
                        *ptr == '\t' ||
                        *ptr == '\r' ||
                        *ptr == '\n') &&
                        ptr < str+len-1 &&
                        *ptr != 0) {
                ptr++;
        }

        str = ptr;
        return ptr;
}

char *strlower(char *str) {
        char *p=str;

        while(*str != 0) {

                if(*str >= 'A' &&
                                *str <= 'Z') {
                        *str -= 'A';
                        *str += 'a';
                }

                str++;
        }

        return p;
}

map<string, string> configuration;
void LoadConfig(const char* fileName) {

        char ltmp[1024];

        sprintf(ltmp, "Loading config file (%s)", fileName);
        log(ltmp);

        ifstream infile(fileName);

                        while(!infile.eof()) {

                                char tmp[2048];
                                infile.getline(tmp, sizeof(tmp));

                                if(tmp[0] == '#') {
                                        sprintf(ltmp, "skipping commnet (%s)", tmp);
                                        log(ltmp);
                                        continue;
                                }

                                char *ptmp = tmp;
                                strtrim(ptmp);
                                if(strlen(ptmp) <= 0) continue;
#define SEP "="

                                char key[1024];
                                char value[1024];
                                char *pstr=strtok(tmp, SEP);
                                strcpy(key,strlower(pstr));
                                string strKey = key;
                                pstr=strtok(NULL, SEP);
                                strcpy(value, strtrim(pstr));
                                string strValue = value;

                                sprintf(ltmp, "(%s) = (%s)", key, value);
                                log(ltmp);

                                configuration[strKey]=strValue;

                        }

                infile.close();
                configLoad = true;

                if(GetConf("DebugEnabled") == "true") {
                        debugEnabled = true;
                }
}

string GetConf(string key) {

        char tmp[1024];

        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        if(configuration.find(key) == configuration.end())
        {
                sprintf(tmp, "Key (%s) not found in configuration", key.c_str());
                log(tmp);

                return "";
        }
        return configuration[key];
}

int GetIntConf(string key) {

        char tmp[1024];

        std::transform(key.begin(), key.end(), key.begin(), ::tolower);
        if(configuration.find(key) == configuration.end())
        {
                sprintf(tmp, "Key (%s) not found in configuration", key.c_str());
                log(tmp);

                return 0;
        }
        return atoi(configuration[key].c_str());
}

for log, please see previous post minimal logging.

C++: Minimal log - but complete

Definition:

#include <cstdio>
#include <cstring>
#include <ctime>
#include <cmath>

using namespace std;

#define max(x,y) x>y?x:y
 
 #define log(msg) logmsg(__FUNCTION__, msg)

FILE *logFile=NULL;
void logmsg(const char *funcName, const char *msg) {

        if(!logFile) {
                logFile = fopen("log.txt", "a+");
                setbuf(logFile, NULL);
        }

        int szFunc = max(30, strlen(funcName));
        int szMsg = max(60, strlen(msg));

        time_t tm_t=time(NULL);
        tm t;
        memcpy(&t, localtime(&tm_t), sizeof(tm));

        fprintf(logFile, "[%04d%02d%02d%02d%02d%02d] [%-*s] [%-*s]\n",
        t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
                szFunc, funcName, szMsg, msg
   );
}

Calling:

char tmp[1024];

sprintf(tmp, "for key (%s) value (%d)", key, value);
log(tmp);


Thursday, October 20, 2011

OpenCV application on linux make and execute

After building openCV, copy OpenCV-2.3.1/include and OpenCV-2.3.1/lib to build directory.
find all the files in lib directory and add it to makefile
find all the files:>ls lib/*so
copy the result and paste in makefile, create make file as follows:

makefile:
LIBS=lib/libopencv_calib3d.so     lib/libopencv_flann.so    lib/libopencv_legacy.so     lib/libopencv_video.so lib/libopencv_contrib.so     lib/libopencv_gpu.so      lib/libopencv_ml.so lib/libopencv_core.so        lib/libopencv_highgui.so  lib/libopencv_objdetect.so lib/libopencv_features2d.so  lib/libopencv_imgproc.so  lib/libopencv_ts.so

all: main
main:   main.cpp
        $(CPP) main.cpp -o main -I include/opencv $(LIBS)
        #$(CPP) --static -I -o main main.cpp $(LIBS) -lpthread -lrt -ltiff

clean:
        rm main

to build, execute >make
to run, execute >./main

*when I run, I get error of opencv dynamically linked library not found, by default OpenCV copy the library files in /usr/local/lib, copy files from /usr/local/lib to /usr/lib, then it will run fine.

as root, execute >cp /usr/local/lib/*so /usr/lib

Build OpenCV on linux x86

download openCV from here
extract openCV in folder
execute > cmake CMakeLists.txt
execute > make
execute as root > make install

C++: Perfectly file close on unexpected system shutdown

Logging utility need this fuctionality, for cases of forget to shutdown file or unhandled exceptions:

I created a class CLogFile, holding file descriptor and in destructor I close the file descriptor, initialize the default destructor to stderr:

class CLogFile {
private:

FILE *FileDescriptor;

public:
CLogFile(){
this->FileDescriptor=stderr;
}

~CLogFile(){
if(NULL != this->FileDescriptor &&
stdout != this->FileDescriptor &&
stderr != this->FileDescriptor) {
fclose(this->FileDescriptor);
FileDescriptor=stdout;
}
}

void SetFileDescriptorToFile(char *filename) {
if(NULL != this->FileDescriptor &&
stdout != this->FileDescriptor &&
stderr != this->FileDescriptor) {
fclose(this->FileDescriptor);
}

........

if((this->FileDescriptor=fopen(name, "a+")) == NULL) {
this->FileDescriptor=stderr;
throw "Can not open logfile";
}

........

}

FILE *GetFD() {

........

return this->FileDescriptor;

}

Now, File is opened through CLogFile object method call SetFileDescriptorToFile(filename) and method call GetFD() gives the file descriptor without worry about closing the file or for any unhandled exception.

C++: Create per day log file, change file to next file in sequence at start of next day

There was a need that at start of next day, log messages get logged into next file in sequence which means one file on per day basis, keeping in mind not to compromise performance. The logic is that when ever a call to SetFileDescriptorToFile, i.e. open file, is made (which is usually once a day), store the base name of file in m_FileBaseName, find the epoch end of time of the day (that is time since Jan 1 1970 00:00:00 UTC/GMT) and store it in m_DayEndTime. Whenever a call to GetFD, i.e. give file descriptor to log message, is made, make a time() API call (give current time since epoch) and see, whether the current time epoch value exceed epoch end of time of the day, if no just return the current descriptor or else open the next file in sequence using base file name.

m_DayEndTime, i.e. epoch end of time of the day is calculated as follows:

time_t tLocal = time(NULL); // give current time in UTC/GMT timezone

tm tmGMT;
memcpy(&tmGMT, gmtime(&tLocal), sizeof(tm));
time_t tGMT = mktime(&tmGMT);

int timeZoneDiff = tLocal - tGMT; // get timezone difference of local timezone and UTC/GMT timezone
int tTimeZoneAdj = tLocal + timeZoneDiff; // get current time in local timezone
const int PER_DAY_TIME = 24*60*60;
int DayRemTime = PER_DAY_TIME-(tTimeZoneAdj % PER_DAY_TIME);
int m_DayEndTime = tLocal+DayRemTime;

tm *_tm=localtime(&tLocal);



File name in sequece using base file name is created as follow:

char name[255];
sprintf(name, "%s/%s%04d%02d%02d", DIR, (char *)m_FileBaseName.c_str(),
_tm->tm_year+1900,
_tm->tm_mon+1,
_tm->tm_mday);


The decision of whether to log message in current open file or should be logged into next file in sequence is made as follows:

time_t t = time(NULL);

if(t > m_DayEndTime) {
SetFileDescriptorToFile((char *)m_FileBaseName.c_str());
}



The whole code is as follows:

class CLogFile {
private:
string m_FileBaseName;
time_t m_DayEndTime;
FILE *FileDescriptor;


void SetFileDescriptorToFile(char *filename) {
if(NULL != this->FileDescriptor &&
stdout != this->FileDescriptor &&
stderr != this->FileDescriptor) {
fclose(this->FileDescriptor);
}

m_FileBaseName=filename;

time_t t = time(NULL);
#define PER_DAY_TIME 24*60*60
int remainingDaytime=PER_DAY_TIME-(t % PER_DAY_TIME);
m_DayEndTime=t+remainingDaytime;
tm *_tm=localtime(&t);

#define DIR "Logs"

if(_chdir(DIR) != 0) {
if(_mkdir(DIR) != 0) {
this->FileDescriptor=stderr;
cerr "Cannot create directory for Logs, using stderr" endl;
return;
}
} else {
_chdir("..");
}

char name[255];
sprintf(name, "%s/%s%04d%02d%02d", DIR,
(char *)m_FileBaseName.c_str(),
_tm->tm_year+1900,
_tm->tm_mon+1,
_tm->tm_mday);

if((this->FileDescriptor=fopen(name, "a+")) == NULL) {
this->FileDescriptor=stderr;
throw "Can not open logfile";
}
else {
setbuf(this->FileDescriptor, NULL);
}
}

FILE *GetFD() {
if(stdout == this->FileDescriptor ||
stderr == this->FileDescriptor) {
return this->FileDescriptor;
}

time_t t = time(NULL);

if(t > m_DayEndTime) {
SetFileDescriptorToFile((char *)m_FileBaseName.c_str());
}

return this->FileDescriptor;
}
};

C++: Instant append of log messages to the file

Logging utility required log messages to be appended at once at end of file so that the utilities like tail, can show the current/latest output.

In CLogFile::SetFileDescriptorToFile object method, setbuf API is used with argument of NULL, to set file output operation as unbuffered output just like stderr.

void SetFileDescriptorToFile(char *filename) {
if(NULL != this->FileDescriptor &&
stdout != this->FileDescriptor &&
stderr != this->FileDescriptor) {
fclose(this->FileDescriptor);
}

........

if((this->FileDescriptor=fopen(name, "a+")) == NULL) {
this->FileDescriptor=stderr;
throw "Can not open logfile";
}
else {
setbuf(this->FileDescriptor, NULL);
}

}

C++: Logging Completeness

The CLog class was created by considering completeness in mind. When ever any message is logged, along with message, its actual location should be printed somewhat as the case with printing exception objects in very high level languages - Java/C#.

CLog class provide static methods to log messages to file descriptor contained in static variable of CLogFile (see previous blogs for CLogFile), keep in mind the requirement was single thread logging.
Preprocessor Macros were used to log messages for avoiding logging file name, function name and line number and to simplify things. Preprocessor constants __FILE__ gives current file, __FUNCTION__ gives current function and __LINE__ gives current line.

The Macro is as follows:

#define Log(Msg,LogLevel) CLog::LogMessage(Msg,LogLevel,__FILE__,__FUNCTION__,__LINE__);

The class is as follows:

class CLog
{

private:
static CLogFile LogFile;

public:
static void LogInit(char* fileName);
static void LogMessage(char *msg, int logLevel, char *fileName, char *funcName, int lineNum);
static void LogMessageToFD(FILE *fd, char *msg, int logLevel, char *fileName, char *funcName, int lineNum);

};

void CLog::LogInit(char* fileName) {
LogFile.SetFileDescriptorToFile(filename);
}

void CLog::LogMessage(char *msg, int logLevel, char *fileName, char *funcName, int lineNum) {
LogMessageToFD(LogFile.GetFD(), msg, logLevel, fileName, funcName, lineNum);
}

void CLog::LogMessageToFD(FILE *fd, char *msg, int logLevel, char *fileName,
char *funcName, int lineNum,) {

..........................
// Print Log Message with time stamp
..........................

}

C++: Log Levels

Logging utility should have different log levels so that we may take decision on log levels while making logs. It may include, printing file name, function name and line number just for the case of error messages, Setting a global log level and only logging messages which are less than the global log levels, in this way, application in production can get rid of debugging log messages. We may initialize system with different log levels or changing log level at run time.

Basicly there should be at least 3 log levels (Error, Warn, Info or Debug) as follows:

enum LogLevel {LogLevelError=00,
LogLevelWarn=10,
LogLevelInfo=20,

LogLevelMaxLevel=9999 };

CLog class contains an static variable GlobalLogLevel of type LogLevel, which can be set during initialization or at runtime, as follows:

class CLog
{

...

public:
static LogLevel GlobalLogLevel;

...

};

Before logging message, it is tested that the message going to be logged should have less log level than the global log level, as follows:

void CLog::LogMessageToFD(FILE *fd, char *msg, int logLevel, char *fileName,
char *funcName, int lineNum,) {

.........................

if(logLevel <= CLog::GlobalLogLevel) {

// Print Log Message with time stamp

}

.........................

}

C++: Logging Format Consistency

Log message should be consistent and pleasant/easy to view along with compatible with text processing tools like awk/sed and perl for data mining.

During creating of logging it was observed that function fully qualified names (FQN) have average length of 30 and log message have average length of 60, so this width is selected for narrow width strings respectively.

Time stamp was generated as YYYYMMDDHHMMSS.
The log message format was [YYYYMMDDHHMMSS][Function FQN][Log_Level][Log Message][file name, line number]

e.g.: [20091005203159][ABCModInit ][info ][Initializing system... ][Abc.cpp,49]

The code is as follows:

void CLog::LogMessageToFD(FILE *fd, char *msg, int logLevel, char *fileName,
char *funcName, int lineNum,) {

.........................

if(logLevel <= CLog::GlobalLogLevel) {

int szFunc = max(30, strlen(funcName));
int szMsg = max(60, strlen(msg));

time_t tm_t=time(NULL);
tm t;
memcpy(&t, localtime(&tm_t), sizeof(tm));

fprintf(fd, "[%04d%02d%02d%02d%02d%02d][%-*s][%-5s][%-*s]",
t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
szFunc, funcName, CLog::GetLogLevelStr(logLevel), szMsg, msg
);

if(CLog::PrintFileNNum || logLevel == LogLevel::LogLevelError) {

fprintf(fd, "[%s,%d]", fileName, lineNum);

}

fprintf(fd, "\n");

}

.........................

}

GetLogLevelStr is an static method in CLog class as follows:

char *CLog::GetLogLevelStr(int logLevel) {
switch(logLevel) {
case LogLevelError:
return "error";
case LogLevelWarn:
return "warn";
case LogLevelInfo:
return "info";

default:
static char buf[255];
sprintf(buf, "Unknown:(%d)", logLevel);
return buf;
}
}

C++: Log Memory Dumps

It is necessary to log memory dumps for certain memory areas, such as binary file read, data received on socket and so on.

The macro for memory dump (for purpose of logging completeness) is as follows:

#define DumpArray(Msg,Buf,Len,LogLevel) CLog::ArrayDump(Msg,Buf,Len,LogLevel,__FILE__,__FUNCTION__,__LINE__);

The declaration is as follows:

class CLog
{

private:
static CLogFile LogFile;

public:
static void LogInit(char* fileName);
static void LogMessage(char *msg, int logLevel, char *fileName, char *funcName, int lineNum);
static void LogMessageToFD(FILE *fd, char *msg, int logLevel, char *fileName, char *funcName, int lineNum);

static void ArrayDumpToFD(FILE *fd, char *msg, unsigned char *data, int len, LogLevel logLevel, char *fileName, char *funcName,
int lineNum);
static void ArrayDump(char *msg, unsigned char *data, int len, LogLevel logLevel, char *fileName, char *funcName, int lineNum);



};

Logging array dump uses similar format used by many popular debuggers, i.e. eight digits of offset (from start address), followed 16 bytes hex values, followed by 16 printable characters or dot '.'. Each 16, hex values and characters, are seperated by a space character after 8th entry for easier readability.

The code is as follows:

void CLog::ArrayDump(char *msg, unsigned char *data, int len, LogLevel logLevel, char *fileName, char *funcName, int lineNum) {

ArrayDumpToFD(LogFile.GetFD(), msg, data, len, logLevel, fileName, funcName, lineNum);

}

void CLog::ArrayDumpToFD(FILE *fd, char *msg, unsigned char *data, int len, LogLevel logLevel, char *fileName,
char *funcName, int lineNum) {


if(logLevel <= CLog::GlobalLogLevel) {
time_t tm_t=time(NULL);
tm t;
memcpy(&t, localtime(&tm_t), sizeof(tm));
fprintf(fd, "[%04d%02d%02d%02d%02d%02d][%s][%s][Memory Dump (size=%d): %s\n",
t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec,
funcName, CLog::GetLogLevelStr(logLevel), len, msg);

int index=0;
while(data+index < data+len) {
//print offset
fprintf(fd, "%08d ", index);
//print hex
do{
if(index%8 == 0){
fprintf(fd, " ");
}
fprintf(fd, "%02X ",
(data+index
index++;
if(index%16 == 0 &&
data+index >= data+len) {
break;
}
}while(index%16 != 0);

index -= 16;
//print printable
fprintf(fd, " ");
do {
char ch='.';
if(data+index < data+len) {
if(isprint(*(data+index))) {
ch=*(data+index);
}
}

fprintf(fd, "%c", ch);
index++;
if(index %8 == 0) {
fprintf(fd, " ");
}

if(index%16 == 0 &&
data+index >= data+len) {
break;
}
}while(index%16 != 0);

fprintf(fd, "\n");
}

fprintf( fd, "]");
if(CLog::PrintFileNNum || logLevel == LogLevel::LogLevelError) {
fprintf(fd, "[%s,%d]", fileName, lineNum);
}
fprintf(fd, "\n");
}

}

C++: Data Buffer

We had a requirements of dynamic memory allocation, but the issue associated with dynamic memory allocation is memory leaks, forget to release memory or exception prevent the execution of memory allocation code. So a template based class is created to allocate memory dynamically in constructor and release it on destructor. The main thing is to create it's local variable on stack and supply the variable memory size in constructor, being the local variable on stack will guarantee cause execution of destructor (which will result in deallocation of memory) so no worries of releasing of memory or exceptions.

It is noted that we usually need char buffer, so the default template type is char.

To have deep copy in place, copy constructor and assignment operator are implemented.

The code is as follows:

#pragma once
#include "iostream"
using namespace std;

typedef unsigned char UCHAR8;
template
class CDataBuf
{
private:
int m_Len;
T *m_Buf;

void init(int len);
public:
CDataBuf(void);
CDataBuf(int len) { init(len); }
~CDataBuf(void);

CDataBuf(CDataBuf &dataBuf);
CDataBuf &operator=(CDataBuf &dataBuf);

T *GetBuf() { return m_Buf; }
int GetLen() { return m_Len; }
};

template
void CDataBuf::init(int len) {
try {
this->m_Len = len;
m_Buf = new T[len];
memset(m_Buf, 0, len * sizeof(T));
}
catch( exception *exc ) {
char tmpBuf[255];
sprintf(tmpBuf, "error can not alloc memory (%s)", exc->what());
Log(tmpBuf, LogLevelError);
throw tmpBuf;
}
catch( char *exc ) {
char tmpBuf[255];
sprintf(tmpBuf, "error can not alloc memory (%s)", exc);
Log(tmpBuf, LogLevelError);
throw tmpBuf;
}
catch( ... ) {
char *tmpBuf="unknown error occured";
Log(tmpBuf, LogLevelError);
throw tmpBuf;
}
}

template
CDataBuf::~CDataBuf(void) {
if(m_Buf) {
delete[] m_Buf;
m_Buf=0;
m_Len=0;
}
}

#include "StdAfx.h"
#include "DataBuf.h"

template
CDataBuf::CDataBuf(void) {
m_Buf=0;
m_Len=0;
}

template
CDataBuf::CDataBuf(CDataBuf &dataBuf) {
init(dataBuf.m_Len);
memcpy(this->m_Buf, dataBuf.m_Buf, dataBuf.m_Len * sizeof(T));
}

template
CDataBuf &CDataBuf::operator=(CDataBuf &dataBuf) {
if(m_Buf) {
delete[] m_Buf;
m_Len = 0;
}

init(dataBuf.m_Len);
memcpy(this->m_Buf, dataBuf.m_Buf, dataBuf.m_Len * sizeof(T));

return *this;
}