From 0bb3f8fe2e93799a98a7e3d562fbaa3c476bc7f3 Mon Sep 17 00:00:00 2001 From: David Freese Date: Sat, 21 Mar 2009 18:41:36 -0500 Subject: [PATCH] Modification to logbook file processing * Performance enhancement to file reading and processing * original, 33 seconds to process 22200 records * new, 0.55 seconds to process 22200 records --- src/include/adif_def.h | 2 + src/include/adif_io.h | 2 +- src/include/field_def.h | 30 ++++--- src/include/qso_db.h | 1 + src/logbook/adif_io.cxx | 169 +++++++++++++++++++++++----------------- src/logbook/qso_db.cxx | 26 ++++--- 6 files changed, 132 insertions(+), 98 deletions(-) diff --git a/src/include/adif_def.h b/src/include/adif_def.h index dd3f3a56..5db2781f 100644 --- a/src/include/adif_def.h +++ b/src/include/adif_def.h @@ -7,10 +7,12 @@ struct FIELD { int type; const char *name; + int len; int size; Fl_Check_Button **btn; }; extern FIELD fields[]; +extern int numfields; #endif diff --git a/src/include/adif_io.h b/src/include/adif_io.h index 21f9052c..db50c5d6 100644 --- a/src/include/adif_io.h +++ b/src/include/adif_io.h @@ -16,7 +16,7 @@ private: FILE *adiFile; void fillfield(int, char *); public: - cAdifIO () {}; + cAdifIO (); ~cAdifIO () {}; int readAdifRec () {return 0;}; int writeAdifRec () {return 0;}; diff --git a/src/include/field_def.h b/src/include/field_def.h index 333d2566..13ff3a35 100644 --- a/src/include/field_def.h +++ b/src/include/field_def.h @@ -14,42 +14,40 @@ enum ADIF_FIELD_POS { COUNTRY, CQZ, DXCC, + EXPORT, // flag used internally in fldigi's logbook FREQ, GRIDSQUARE, + IOTA, + ITUZ, MODE, + MYXCHG, // contest exchange field #3 NAME, NOTES, + OPERATOR, + PFX, + PROP_MODE, QSLRDATE, QSLSDATE, + QSL_MSG, QSL_RCVD, QSL_SENT, + QSL_VIA, QSO_DATE, QTH, RST_RCVD, RST_SENT, - STATE, - STX, - TIME_OFF, - TIME_ON, - TX_PWR, -// additional for 2.0 - IOTA, - ITUZ, - OPERATOR, - PFX, - PROP_MODE, - QSL_MSG, - QSL_VIA, RX_PWR, SAT_MODE, SAT_NAME, SRX, + STATE, + STX, TEN_TEN, + TIME_OFF, + TIME_ON, + TX_PWR, VE_PROV, -// following fields are specific to fldigi's logbook XCHG1, // contest exchange field - MYXCHG, // contest exchange field #3 - EXPORT, // flag used internally in fldigi's logbook NUMFIELDS // counter for number of fields in enum }; diff --git a/src/include/qso_db.h b/src/include/qso_db.h index 9b62e1b1..16f1fc2f 100644 --- a/src/include/qso_db.h +++ b/src/include/qso_db.h @@ -27,6 +27,7 @@ public: cQsoRec (); ~cQsoRec (); void putField (int, const char *); + void putField (int, const char *, int); void addtoField (int, const char *); char *getField (int); void trimFields(); diff --git a/src/logbook/adif_io.cxx b/src/logbook/adif_io.cxx index 2b58ea32..29ce584f 100644 --- a/src/logbook/adif_io.cxx +++ b/src/logbook/adif_io.cxx @@ -4,6 +4,7 @@ #include #include +#include #include "adif_io.h" #include "config.h" @@ -21,106 +22,129 @@ static const char *szEOL = "\n"; FIELD fields[] = { // TYPE, NAME, SIZE - {ADDRESS, "ADDRESS", 40, NULL}, // contacted stations mailing address - {AGE, "AGE", 3, NULL}, // contacted operators age in years - {ARRL_SECT, "ARRL_SECT", 12, NULL}, // contacted stations ARRL section - {BAND, "BAND", 6, &btnSelectBand}, // QSO band - {CALL, "CALL", 10, &btnSelectCall}, // contacted stations CALLSIGN - {CNTY, "CNTY", 20, NULL}, // secondary political subdivision, ie: county - {COMMENT, "COMMENT", 80, NULL}, // comment field for QSO - {CONT, "CONT", 10, NULL}, // contacted stations continent - {CONTEST_ID, "CONTEST_ID", 6, NULL}, // QSO contest identifier - {COUNTRY, "COUNTRY", 20, &btnSelectCountry}, // contacted stations DXCC entity name - {CQZ, "CQZ", 8, NULL}, // contacted stations CQ Zone - {DXCC, "DXCC", 8, &btnSelectDXCC}, // contacted stations Country Code - {FREQ, "FREQ", 10, &btnSelectFreq}, // QSO frequency in Mhz - {GRIDSQUARE, "GRIDSQUARE", 6, &btnSelectLOC}, // contacted stations Maidenhead Grid Square - {MODE, "MODE", 8, &btnSelectMode}, // QSO mode - {NAME, "NAME", 18, &btnSelectName}, // contacted operators NAME - {NOTES, "NOTES", 80, &btnSelectNotes}, // QSO notes - {QSLRDATE, "QSLRDATE", 8, &btnSelectQSLrcvd}, // QSL received date - {QSLSDATE, "QSLSDATE", 8, &btnSelectQSLsent}, // QSL sent date - {QSL_RCVD, "QSL_RCVD", 1, NULL}, // QSL received status - {QSL_SENT, "QSL_SENT", 1, NULL}, // QSL sent status - {QSO_DATE, "QSO_DATE", 8, &btnSelectQSOdate}, // QSO data - {QTH, "QTH", 30, &btnSelectQth}, // contacted stations city - {RST_RCVD, "RST_RCVD", 3, &btnSelectRSTrcvd}, // received signal report - {RST_SENT, "RST_SENT", 3, &btnSelectRSTsent}, // sent signal report - {STATE, "STATE", 2, &btnSelectState}, // contacted stations STATE - {STX, "STX", 8, &btnSelectSerialOUT}, // QSO transmitted serial number - {TIME_OFF, "TIME_OFF", 4, &btnSelectTimeOFF}, // HHMM or HHMMSS in UTC - {TIME_ON, "TIME_ON", 4, &btnSelectTimeON}, // HHMM or HHMMSS in UTC - {TX_PWR, "TX_PWR", 4, &btnSelectTX_pwr}, // power transmitted by this station -// new fields - {IOTA, "IOTA", 6, &btnSelectIOTA}, // Islands on the air - {ITUZ, "ITUZ", 6, NULL}, // ITU zone - {OPERATOR, "OPERATOR", 10, NULL}, // Callsign of person loggin the QSO - {PFX, "PFX", 5, NULL}, // WPA prefix - {PROP_MODE, "PROP_MODE", 5, NULL}, // propogation mode - {QSL_MSG, "QSL_MSG", 80, NULL}, // personal message to appear on qsl card - {QSL_VIA, "QSL_VIA", 30, NULL}, - {RX_PWR, "RX_PWR", 4, NULL}, // power of other station in watts - {SAT_MODE, "SAT_MODE", 8, NULL}, // satellite mode - {SAT_NAME, "SAT_NAME", 12, NULL}, // satellite name - {SRX, "SRX", 5, &btnSelectSerialIN}, // received serial number for a contest QSO - {TEN_TEN, "TEN_TEN", 10, NULL}, // ten ten # of other station - {VE_PROV, "VE_PROV", 2, &btnSelectProvince}, // 2 letter abbreviation for Canadian Province -// fldigi specific fields - {XCHG1, "XCHG1", 20, &btnSelectXchgIn}, // contest exchange #1 / free1 in xlog - {MYXCHG, "MYXCHG", 20, &btnSelectMyXchg}, // contest exchange sent - {EXPORT, "EXPORT", 1, NULL} // used to indicate record is to be exported + {ADDRESS, "ADDRESS", 0, 40, NULL}, // contacted stations mailing address + {AGE, "AGE", 0, 3, NULL}, // contacted operators age in years + {ARRL_SECT, "ARRL_SECT", 0, 12, NULL}, // contacted stations ARRL section + {BAND, "BAND", 0, 6, &btnSelectBand}, // QSO band + {CALL, "CALL", 0, 10, &btnSelectCall}, // contacted stations CALLSIGN + {CNTY, "CNTY", 0, 20, NULL}, // secondary political subdivision, ie: county + {COMMENT, "COMMENT", 0, 80, NULL}, // comment field for QSO + {CONT, "CONT", 0, 10, NULL}, // contacted stations continent + {CONTEST_ID, "CONTEST_ID", 0, 6, NULL}, // QSO contest identifier + {COUNTRY, "COUNTRY", 0, 20, &btnSelectCountry}, // contacted stations DXCC entity name + {CQZ, "CQZ", 0, 8, NULL}, // contacted stations CQ Zone + {DXCC, "DXCC", 0, 8, &btnSelectDXCC}, // contacted stations Country Code + {EXPORT, "EXPORT", 0, 1, NULL}, // used to indicate record is to be exported + {FREQ, "FREQ", 0, 10, &btnSelectFreq}, // QSO frequency in Mhz + {GRIDSQUARE, "GRIDSQUARE", 0, 6, &btnSelectLOC}, // contacted stations Maidenhead Grid Square + {IOTA, "IOTA", 0, 6, &btnSelectIOTA}, // Islands on the air + {ITUZ, "ITUZ", 0, 6, NULL}, // ITU zone + {MODE, "MODE", 0, 8, &btnSelectMode}, // QSO mode + {MYXCHG, "MYXCHG", 0, 20, &btnSelectMyXchg}, // contest exchange sent + {NAME, "NAME", 0, 18, &btnSelectName}, // contacted operators NAME + {NOTES, "NOTES", 0, 80, &btnSelectNotes}, // QSO notes + {OPERATOR, "OPERATOR", 0, 10, NULL}, // Callsign of person loggin the QSO + {PFX, "PFX", 0, 5, NULL}, // WPA prefix + {PROP_MODE, "PROP_MODE", 0, 5, NULL}, // propogation mode + {QSLRDATE, "QSLRDATE", 0, 8, &btnSelectQSLrcvd}, // QSL received date + {QSLSDATE, "QSLSDATE", 0, 8, &btnSelectQSLsent}, // QSL sent date + {QSL_MSG, "QSL_MSG", 0, 80, NULL}, // personal message to appear on qsl card + {QSL_RCVD, "QSL_RCVD", 0, 1, NULL}, // QSL received status + {QSL_SENT, "QSL_SENT", 0, 1, NULL}, // QSL sent status + {QSL_VIA, "QSL_VIA", 0, 30, NULL}, + {QSO_DATE, "QSO_DATE", 0, 8, &btnSelectQSOdate}, // QSO data + {QTH, "QTH", 0, 30, &btnSelectQth}, // contacted stations city + {RST_RCVD, "RST_RCVD", 0, 3, &btnSelectRSTrcvd}, // received signal report + {RST_SENT, "RST_SENT", 0, 3, &btnSelectRSTsent}, // sent signal report + {RX_PWR, "RX_PWR", 0, 4, NULL}, // power of other station in watts + {SAT_MODE, "SAT_MODE", 0, 8, NULL}, // satellite mode + {SAT_NAME, "SAT_NAME", 0, 12, NULL}, // satellite name + {SRX, "SRX", 0, 5, &btnSelectSerialIN}, // received serial number for a contest QSO + {STATE, "STATE", 0, 2, &btnSelectState}, // contacted stations STATE + {STX, "STX", 0, 8, &btnSelectSerialOUT}, // QSO transmitted serial number + {TEN_TEN, "TEN_TEN", 0, 10, NULL}, // ten ten # of other station + {TIME_OFF, "TIME_OFF", 0, 4, &btnSelectTimeOFF}, // HHMM or HHMMSS in UTC + {TIME_ON, "TIME_ON", 0, 4, &btnSelectTimeON}, // HHMM or HHMMSS in UTC + {TX_PWR, "TX_PWR", 0, 4, &btnSelectTX_pwr}, // power transmitted by this station + {VE_PROV, "VE_PROV", 0, 2, &btnSelectProvince}, // 2 letter abbreviation for Canadian Province + {XCHG1, "XCHG1", 0, 20, &btnSelectXchgIn} // contest exchange #1 / free1 in xlog }; +int numfields = sizeof(fields) / sizeof(FIELD); + +void initfields() +{ + for (int i = 0; i < numfields; i++) + fields[i].len = strlen(fields[i].name); +} + int fieldnbr (const char *s) { - for (int i = 0; i < EXPORT; i++) + for (int i = 0; i < numfields; i++) if (strncasecmp( fields[i].name, s, fields[i].size) == 0) { - if (i == COMMENT) i = NOTES; - return i; + if (fields[i].type == COMMENT) return(NOTES); + return fields[i].type; } return -1; } -int findfield (char *p) { - for (int i=0; i < EXPORT; i++) - if (strncasecmp (p, fields[i].name, strlen(fields[i].name)) == 0) { - if (i == COMMENT) i = NOTES; - return i; - } +int findfield( char *p ) +{ + int low = 0; + int high = numfields - 1; + int middle; + int test; + if (strncasecmp (p, "EOR>", 4) == 0) return -1; - return -2; + + while( low <= high ) { + middle = ( low + high ) / 2; + if ( (test = strncasecmp( p, + fields[middle].name, + fields[middle].len ) ) == 0 ) { //match + if (fields[middle].type == COMMENT) return(NOTES); + return fields[middle].type; + } + else if( test < 0 ) + high = middle - 1; //search low end of array + else + low = middle + 1; //search high end of array + } + return -2; //search key not found +} + +cAdifIO::cAdifIO () +{ + initfields(); } void cAdifIO::fillfield (int fieldnum, char *buff){ -char *p = buff; -char numeral[8]; +const char *p = buff; int n, fldsize; - memset (numeral, 0, 8); n = 0; while (*p != ':' && n < 11) {p++; n++;} if (n == 11) return; // bad ADIF specifier ---> no ':' after field name // found first ':' p++; n = 0; - while (*p >= '0' && *p <= '9' && n < 8) { - numeral[n++] = *p; + fldsize = 0; + const char *p2 = strchr(buff,'>'); + if (!p2) return; + while (p != p2) { + if (*p >= '0' && *p <= '9' && n < 8) { + fldsize = fldsize * 10 + *p - '0'; + n++; + } p++; } - fldsize = atoi(numeral); - p = strchr(buff,'>'); // end of specifier +1 == > start of data - if (!p) return; - p++; - char *flddata = new char[fldsize+1]; - memset (flddata, 0, fldsize + 1); - strncpy (flddata, p, fldsize); - adifqso.putField (fieldnum, (const char *)flddata); - delete [] flddata; + adifqso.putField (fieldnum, p2+1, fldsize); } void cAdifIO::readFile (const char *fname, cQsoDb *db) { long filesize = 0; char *buff; int found; + // open the adif file adiFile = fopen (fname, "r"); if (!adiFile) @@ -162,6 +186,7 @@ void cAdifIO::readFile (const char *fname, cQsoDb *db) { p2 = strchr(p1,'<'); // find first ADIF specifier adifqso.clearRec(); + while (p2) { found = findfield(p2+1); // -2 ==> not found; -1 0 ...N field # // if (found == -2 ) return; // unknown field diff --git a/src/logbook/qso_db.cxx b/src/logbook/qso_db.cxx index 00ab7591..a277c8ab 100644 --- a/src/logbook/qso_db.cxx +++ b/src/logbook/qso_db.cxx @@ -18,7 +18,7 @@ bool cQsoDb::reverse = false; cQsoRec::cQsoRec() { for (int i=0;i < NUMFIELDS; i++) { qsofield[i] = new char [fields[i].size + 1]; - memset (qsofield[i],0, fields[i].size + 1); + memset (qsofield[i], 0, fields[i].size + 1); } } @@ -77,7 +77,15 @@ void cQsoRec::checkBand() { void cQsoRec::putField (int n, const char *s){ if (n < 0 || n >= NUMFIELDS) return; - strncpy( qsofield[n], s, fields[n].size); + memset(qsofield[n], 0, fields[n].size); + strcpy( qsofield[n], s); +} + +void cQsoRec::putField (int n, const char *s, int len) { + if (n < 0 || n >= NUMFIELDS) return; + if (len > fields[n].size) len = fields[n].size; + strncpy(qsofield[n], s, len); + qsofield[n][len] = 0; } void cQsoRec::addtoField (int n, const char *s){ @@ -246,10 +254,12 @@ int i, j, max; //====================================================================== // class cQsoDb +#define MAXRECS 8192 + cQsoDb::cQsoDb() { nbrrecs = 0; - maxrecs = 1; - qsorec = new cQsoRec[1]; + maxrecs = MAXRECS; + qsorec = new cQsoRec[maxrecs]; compby = COMPDATE; dirty = 0; } @@ -260,9 +270,9 @@ cQsoDb::~cQsoDb() { void cQsoDb::deleteRecs() { delete [] qsorec; - qsorec = new cQsoRec[1]; nbrrecs = 0; - maxrecs = 1; + maxrecs = MAXRECS; + qsorec = new cQsoRec[maxrecs]; dirty = 0; } @@ -278,13 +288,11 @@ int cQsoDb::qsoFindRec(cQsoRec *rec) { } void cQsoDb::qsoNewRec (cQsoRec *nurec) { - nurec->trimFields(); - if (qsoFindRec(nurec) > -1) return; if (nbrrecs == maxrecs) { maxrecs *= 2; cQsoRec *atemp = new cQsoRec[maxrecs]; for (int i = 0; i < nbrrecs; i++) - atemp[i] = qsorec[i]; + atemp[i] = qsorec[i]; delete [] qsorec; qsorec = atemp; }