summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsebres <sebres@users.sourceforge.net>2017-01-10 22:15:36 (GMT)
committersebres <sebres@users.sourceforge.net>2017-01-10 22:15:36 (GMT)
commitc313bacc9f0d53d7090a7bc98b24b78ecb92d2f4 (patch)
tree541ebaaa7aa56442851f0531ae4bbaa094b9d5bc
parent3f06a6ec89b41434fe38bede870563e35b809019 (diff)
downloadtcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.zip
tcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.tar.gz
tcl-c313bacc9f0d53d7090a7bc98b24b78ecb92d2f4.tar.bz2
[temp-commit]: clock scan tokenizer logic ready (still needs many rules)
caching extended (currentYearCentury, yearOfCenturySwitch, lastBaseDate ...)
-rw-r--r--generic/tclClock.c69
-rw-r--r--generic/tclClockFmt.c218
-rw-r--r--generic/tclDate.c1
-rw-r--r--generic/tclDate.h88
-rw-r--r--generic/tclGetDate.y1
-rw-r--r--tests-perf/clock.perf.tcl98
6 files changed, 372 insertions, 103 deletions
diff --git a/generic/tclClock.c b/generic/tclClock.c
index e00d6a2..2e7b854 100644
--- a/generic/tclClock.c
+++ b/generic/tclClock.c
@@ -117,6 +117,8 @@ typedef struct ClockClientData {
Tcl_Obj **literals; /* Pool of object literals. */
/* Cache for current clock parameters, imparted via "configure" */
unsigned long LastTZEpoch;
+ int currentYearCentury;
+ int yearOfCenturySwitch;
Tcl_Obj *SystemTimeZone;
Tcl_Obj *SystemSetupTZData;
Tcl_Obj *GMTSetupTimeZone;
@@ -126,6 +128,9 @@ typedef struct ClockClientData {
Tcl_Obj *LastUnnormSetupTimeZone;
Tcl_Obj *LastSetupTimeZone;
Tcl_Obj *LastSetupTZData;
+ /* Cache for last base (fast convert if base/tz not changed) */
+ Tcl_Obj *lastBaseTimeZone;
+ TclDateFields lastBaseDate;
/*
/* [SB] TODO: back-port (from tclSE) the same date caching ...
* Cache for last date (fast convert if date parsed was the same) * /
@@ -325,6 +330,8 @@ TclClockInit(
Tcl_IncrRefCount(data->literals[i]);
}
data->LastTZEpoch = 0;
+ data->currentYearCentury = -1;
+ data->yearOfCenturySwitch = -1;
data->SystemTimeZone = NULL;
data->SystemSetupTZData = NULL;
data->GMTSetupTimeZone = NULL;
@@ -335,6 +342,8 @@ TclClockInit(
data->LastSetupTimeZone = NULL;
data->LastSetupTZData = NULL;
+ data->lastBaseTimeZone = NULL;
+
/*
* Install the commands.
*/
@@ -368,6 +377,8 @@ ClockConfigureClear(
ClockClientData *data)
{
data->LastTZEpoch = 0;
+ data->currentYearCentury = -1;
+ data->yearOfCenturySwitch = -1;
Tcl_UnsetObjRef(data->SystemTimeZone);
Tcl_UnsetObjRef(data->SystemSetupTZData);
Tcl_UnsetObjRef(data->GMTSetupTimeZone);
@@ -708,11 +719,16 @@ ClockCurrentYearCentury(
{
ClockClientData *dataPtr = clientData;
Tcl_Obj **literals = dataPtr->literals;
- int year = 2000;
+ int year = dataPtr->currentYearCentury;
- Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp,
- literals[LIT_CURRENTYEARCENTURY], NULL, TCL_LEAVE_ERR_MSG);
- Tcl_GetIntFromObj(NULL, yearObj, &year);
+ if (year == -1) {
+ Tcl_Obj * yearObj;
+ year = 2000;
+ yearObj = Tcl_ObjGetVar2(interp,
+ literals[LIT_CURRENTYEARCENTURY], NULL, TCL_LEAVE_ERR_MSG);
+ Tcl_GetIntFromObj(NULL, yearObj, &year);
+ dataPtr->currentYearCentury = year;
+ }
return year;
}
inline int
@@ -722,11 +738,16 @@ ClockGetYearOfCenturySwitch(
{
ClockClientData *dataPtr = clientData;
Tcl_Obj **literals = dataPtr->literals;
- int year = 37;
-
- Tcl_Obj * yearObj = Tcl_ObjGetVar2(interp,
- literals[LIT_YEAROFCENTURYSWITCH], NULL, TCL_LEAVE_ERR_MSG);
- Tcl_GetIntFromObj(NULL, yearObj, &year);
+ int year = dataPtr->yearOfCenturySwitch;
+
+ if (year == -1) {
+ Tcl_Obj * yearObj;
+ year = 37;
+ yearObj = Tcl_ObjGetVar2(interp,
+ literals[LIT_YEAROFCENTURYSWITCH], NULL, TCL_LEAVE_ERR_MSG);
+ Tcl_GetIntFromObj(NULL, yearObj, &year);
+ dataPtr->yearOfCenturySwitch = year;
+ }
return year;
}
@@ -2557,12 +2578,27 @@ ClockScanObjCmd(
if (yydate.tzData == NULL) {
goto done;
}
- yydate.seconds = baseVal;
- if (ClockGetDateFields(interp, &yydate, yydate.tzData, GREGORIAN_CHANGE_DATE)
- != TCL_OK) {
- goto done;
+ Tcl_SetObjRef(yydate.tzName, opts.timezoneObj);
+
+ /* check cached */
+ if ( dataPtr->lastBaseTimeZone == opts.timezoneObj
+ && dataPtr->lastBaseDate.seconds == baseVal) {
+ memcpy(&yydate, &dataPtr->lastBaseDate, ClockCacheableDateFieldsSize);
+ } else {
+ /* extact fields from base */
+ yydate.seconds = baseVal;
+ if (ClockGetDateFields(interp, &yydate, yydate.tzData, GREGORIAN_CHANGE_DATE)
+ != TCL_OK) {
+ goto done;
+ }
+ /* cache last base */
+ memcpy(&dataPtr->lastBaseDate, &yydate, ClockCacheableDateFieldsSize);
+ dataPtr->lastBaseTimeZone = opts.timezoneObj;
}
+ /* seconds are in localSeconds (relative base date), so reset time here */
+ yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24;
+
/* If free scan */
if (opts.formatObj == NULL) {
/* Use compiled version of FreeScan - */
@@ -2593,7 +2629,7 @@ ClockScanObjCmd(
else {
/* Use compiled version of Scan - */
- ret = ClockScan(clientData, interp, &yydate, objv[1], &opts);
+ ret = ClockScan(clientData, interp, info, objv[1], &opts);
}
if (ret != TCL_OK) {
@@ -2710,6 +2746,9 @@ ClockFreeScan(
if (yydate.tzData == NULL) {
goto done;
}
+
+ Tcl_SetObjRef(yydate.tzName, opts->timezoneObj);
+
}
/* on demand (lazy) assemble julianDay using new year, month, etc. */
@@ -2719,8 +2758,6 @@ ClockFreeScan(
* Assemble date, time, zone into seconds-from-epoch
*/
- Tcl_SetObjRef(yydate.tzName, opts->timezoneObj);
-
if (yyHaveTime == -1) {
yySeconds = 0;
}
diff --git a/generic/tclClockFmt.c b/generic/tclClockFmt.c
index 93416af..b074681 100644
--- a/generic/tclClockFmt.c
+++ b/generic/tclClockFmt.c
@@ -400,35 +400,48 @@ Tcl_GetClockFrmScnFromObj(
(tok) = (chain) + (tokCnt); \
(tokCnt) += CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE; \
} \
- *(tok) = NULL;
+ memset(tok, 0, sizeof(*(tok)));
const char *ScnSTokenMapChars =
"dmyYHMS";
-static ClockScanToken ScnSTokenMap[] = {
- {CTOKT_DIGIT, 1, 2, 0},
- {CTOKT_DIGIT, 1, 2, 0},
- {CTOKT_DIGIT, 1, 2, 0},
- {CTOKT_DIGIT, 1, 4, 0},
- {CTOKT_DIGIT, 1, 2, 0},
- {CTOKT_DIGIT, 1, 2, 0},
- {CTOKT_DIGIT, 1, 2, 0},
+static ClockScanTokenMap ScnSTokenMap[] = {
+ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.dayOfMonth),
+ NULL},
+ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.month),
+ NULL},
+ {CTOKT_DIGIT, CLF_DATE, 1, 2, TclOffset(DateInfo, date.year),
+ NULL},
+ {CTOKT_DIGIT, CLF_DATE, 1, 4, TclOffset(DateInfo, date.year),
+ NULL},
+ {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.hour),
+ NULL},
+ {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.minutes),
+ NULL},
+ {CTOKT_DIGIT, CLF_TIME, 1, 2, TclOffset(DateInfo, date.secondOfDay),
+ NULL},
};
const char *ScnSpecTokenMapChars =
- " %";
-static ClockScanToken ScnSpecTokenMap[] = {
- {CTOKT_SPACE, 1, 0xffff, 0},
+ " ";
+static ClockScanTokenMap ScnSpecTokenMap[] = {
+ {CTOKT_SPACE, 0, 1, 0xffff, 0,
+ NULL},
+};
+
+static ClockScanTokenMap ScnWordTokenMap = {
+ CTOKT_WORD, 0, 1, 0, 0,
+ NULL
};
/*
*----------------------------------------------------------------------
*/
-ClockScanToken **
+ClockScanToken *
ClockGetOrParseScanFormat(
Tcl_Interp *interp, /* Tcl interpreter */
Tcl_Obj *formatObj) /* Format container */
{
ClockFmtScnStorage *fss;
- ClockScanToken **tok;
+ ClockScanToken *tok;
if (formatObj->typePtr != &ClockFmtObjType) {
if (ClockFmtObj_SetFromAny(interp, formatObj) != TCL_OK) {
@@ -448,27 +461,30 @@ ClockGetOrParseScanFormat(
/* if first time scanning - tokenize format */
if (fss->scnTok == NULL) {
const char *strFmt;
- register const char *p, *e, *cp, *word_start = NULL;
+ register const char *p, *e, *cp;
Tcl_MutexLock(&ClockFmtMutex);
fss->scnTokC = CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE;
fss->scnTok =
tok = ckalloc(sizeof(*tok) * CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE);
- *tok = NULL;
+ memset(tok, 0, sizeof(*(tok)));
strFmt = TclGetString(formatObj);
for (e = p = strFmt, e += formatObj->length; p != e; p++) {
switch (*p) {
case '%':
if (p+1 >= e) {
- word_start = p;
- continue;
+ goto word_tok;
}
p++;
/* try to find modifier: */
switch (*p) {
case '%':
- word_start = p-1;
+ /* begin new word token - don't join with previous word token,
+ * because current mapping should be "...%%..." -> "...%..." */
+ tok->map = &ScnWordTokenMap;
+ tok->tokWord.start = tok->tokWord.end = p;
+ AllocTokenInChain(tok, fss->scnTok, fss->scnTokC);
continue;
break;
case 'E':
@@ -480,10 +496,24 @@ ClockGetOrParseScanFormat(
default:
cp = strchr(ScnSTokenMapChars, *p);
if (!cp || *cp == '\0') {
- word_start = p-1;
- continue;
+ p--;
+ goto word_tok;
+ }
+ tok->map = &ScnSTokenMap[cp - ScnSTokenMapChars];
+ /* calculate look ahead value by standing together tokens */
+ if (tok > fss->scnTok) {
+ ClockScanToken *prevTok = tok - 1;
+ unsigned int lookAhead = tok->map->minSize;
+
+ while (prevTok >= fss->scnTok) {
+ if (prevTok->map->type != tok->map->type) {
+ break;
+ }
+ prevTok->lookAhead += lookAhead;
+ prevTok--;
+ }
}
- *tok = &ScnSTokenMap[cp - ScnSTokenMapChars];
+ /* next token */
AllocTokenInChain(tok, fss->scnTok, fss->scnTokC);
break;
}
@@ -494,18 +524,33 @@ ClockGetOrParseScanFormat(
p--;
goto word_tok;
}
- *tok = &ScnSpecTokenMap[cp - ScnSpecTokenMapChars];
+ tok->map = &ScnSpecTokenMap[cp - ScnSpecTokenMapChars];
AllocTokenInChain(tok, fss->scnTok, fss->scnTokC);
break;
default:
word_tok:
-
- continue;
+ if (1) {
+ ClockScanToken *wordTok = tok;
+ if (tok > fss->scnTok && (tok-1)->map == &ScnWordTokenMap) {
+ wordTok = tok-1;
+ }
+ wordTok->tokWord.end = p;
+ if (wordTok == tok) {
+ wordTok->tokWord.start = p;
+ wordTok->map = &ScnWordTokenMap;
+ AllocTokenInChain(tok, fss->scnTok, fss->scnTokC);
+ }
+ continue;
+ }
}
continue;
+
ext_tok_E:
+ /*******************/
+ continue;
+
ext_tok_O:
/*******************/
@@ -527,23 +572,134 @@ int
ClockScan(
ClientData clientData, /* Client data containing literal pool */
Tcl_Interp *interp, /* Tcl interpreter */
- TclDateFields *date, /* Date fields used for converting */
+ register DateInfo *info, /* Date fields used for parsing & converting */
Tcl_Obj *strObj, /* String containing the time to scan */
ClockFmtScnCmdArgs *opts) /* Command options */
{
- ClockScanToken **tok;
+ ClockScanToken *tok;
+ ClockScanTokenMap *map;
+ register const char *p, *x, *end;
+ unsigned short int flags = 0;
+ int ret = TCL_ERROR;
if ((tok = ClockGetOrParseScanFormat(interp, opts->formatObj)) == NULL) {
return TCL_ERROR;
}
+
+ /* prepare parsing */
+
+ yyMeridian = MER24;
+
+ /* bypass spaces at begin of string */
+
+ p = TclGetString(strObj);
+ end = p + strObj->length;
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ info->dateStart = yyInput = p;
+
+ /* parse string */
+ for (; tok->map != NULL; yyInput = p, tok++) {
+ map = tok->map;
+ switch (map->type)
+ {
+ case CTOKT_DIGIT:
+ if (1) {
+ unsigned int val = 0;
+ int size = map->maxSize;
+ /* greedy find digits (look forward), corresponding pre-calculated lookAhead */
+ size += tok->lookAhead;
+ x = yyInput + size;
+ while (isdigit(UCHAR(*p)) && p < x) { p++; };
+ /* consider reserved (lookAhead) for next tokens */
+ p -= tok->lookAhead;
+ size = p - yyInput;
+ if (size < map->minSize) {
+ /* missing input -> error */
+ goto done;
+ }
+ /* string 2 number */
+ p = yyInput; x = p + size;
+ while (p < x) {
+ val = val * 10 + (*p++ - '0');
+ }
+ /* put number into info by offset */
+ *(time_t *)(((char *)info) + map->offs) = val;
+ flags |= map->flags;
+ }
+ break;
+ case CTOKT_SPACE:
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ break;
+ case CTOKT_WORD:
+ x = tok->tokWord.start;
+ if (x == tok->tokWord.end) { /* single char word */
+ if (*p != *x) {
+ /* no match -> error */
+ goto done;
+ }
+ p++;
+ continue;
+ }
+ /* multi-char word */
+ while (p < end && x < tok->tokWord.end && *p++ == *x++) {};
+ if (x < tok->tokWord.end) {
+ /* no match -> error */
+ goto done;
+ }
+ break;
+ }
+ }
- //***********************************
+ /* ignore spaces at end */
+ while (p < end && isspace(UCHAR(*p))) {
+ p++;
+ }
+ /* check end was reached */
+ if (p < end) {
+ /* something after last token - wrong format */
+ goto done;
+ }
- Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt)tok));
- return TCL_OK;
+ /* invalidate result */
+ if (flags & CLF_DATE) {
+ yydate.julianDay = CL_INVALIDATE;
+
+ if (yyYear < 100) {
+ if (yyYear >= ClockGetYearOfCenturySwitch(clientData, interp)) {
+ yyYear -= 100;
+ }
+ yyYear += ClockCurrentYearCentury(clientData, interp);
+ }
+ yydate.era = CE;
+ if (!(flags & CLF_TIME)) {
+ yydate.localSeconds = 0;
+ }
+ }
+
+ if (flags & CLF_TIME) {
+ yySeconds = ToSeconds(yyHour, yyMinutes,
+ yySeconds, yyMeridian);
+ } else {
+ yySeconds = yydate.localSeconds % 86400;
+ }
+
+ ret = TCL_OK;
+
+done:
+
+ if (ret != TCL_OK) {
+ Tcl_SetResult(interp,
+ "input string does not match supplied format", TCL_STATIC);
+ Tcl_SetErrorCode(interp, "CLOCK", "badInputString", NULL);
+ return ret;
+ }
- return TCL_ERROR;
+ return ret;
}
diff --git a/generic/tclDate.c b/generic/tclDate.c
index a47f43d..97d13b4 100644
--- a/generic/tclDate.c
+++ b/generic/tclDate.c
@@ -2691,7 +2691,6 @@ TclClockFreeScan(
yyHaveDate = 0;
yyHaveTime = 0;
- yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24;
yyHaveZone = 0;
yyTimezone = 0; yyDSTmode = DSTmaybe;
diff --git a/generic/tclDate.h b/generic/tclDate.h
index 49420a2..4b0b47e 100644
--- a/generic/tclDate.h
+++ b/generic/tclDate.h
@@ -22,60 +22,64 @@ typedef struct TclDateFields {
* epoch */
Tcl_WideInt localSeconds; /* Local time expressed in nominal seconds
* from the Posix epoch */
- int tzOffset; /* Time zone offset in seconds east of
+ time_t tzOffset; /* Time zone offset in seconds east of
* Greenwich */
+ time_t julianDay; /* Julian Day Number in local time zone */
+ enum {BCE=1, CE=0} era; /* Era */
+ time_t gregorian; /* Flag == 1 if the date is Gregorian */
+ time_t year; /* Year of the era */
+ time_t dayOfYear; /* Day of the year (1 January == 1) */
+ time_t month; /* Month number */
+ time_t dayOfMonth; /* Day of the month */
+ time_t iso8601Year; /* ISO8601 week-based year */
+ time_t iso8601Week; /* ISO8601 week number */
+ time_t dayOfWeek; /* Day of the week */
+ time_t hour; /* Hours of day (in-between time only calculation) */
+ time_t minutes; /* Minutes of day (in-between time only calculation) */
+ time_t secondOfDay; /* Seconds of day (in-between time only calculation) */
+
Tcl_Obj *tzName; /* Time zone name (if set the refCount is incremented) */
Tcl_Obj *tzData; /* Time zone data object (internally referenced) */
- int julianDay; /* Julian Day Number in local time zone */
- enum {BCE=1, CE=0} era; /* Era */
- int gregorian; /* Flag == 1 if the date is Gregorian */
- int year; /* Year of the era */
- int dayOfYear; /* Day of the year (1 January == 1) */
- int month; /* Month number */
- int dayOfMonth; /* Day of the month */
- int iso8601Year; /* ISO8601 week-based year */
- int iso8601Week; /* ISO8601 week number */
- int dayOfWeek; /* Day of the week */
- int hour; /* Hours of day (in-between time only calculation) */
- int minutes; /* Minutes of day (in-between time only calculation) */
- int secondOfDay; /* Seconds of day (in-between time only calculation) */
} TclDateFields;
+#define ClockCacheableDateFieldsSize \
+ TclOffset(TclDateFields, tzName)
+
/*
* Structure contains return parsed fields.
*/
typedef struct DateInfo {
+ const char *dateStart;
+ const char *dateInput;
TclDateFields date;
- int dateHaveDate;
+ time_t dateHaveDate;
- int dateMeridian;
- int dateHaveTime;
+ time_t dateMeridian;
+ time_t dateHaveTime;
time_t dateTimezone;
- int dateDSTmode;
- int dateHaveZone;
+ time_t dateDSTmode;
+ time_t dateHaveZone;
time_t dateRelMonth;
time_t dateRelDay;
time_t dateRelSeconds;
- int dateHaveRel;
+ time_t dateHaveRel;
time_t dateMonthOrdinalIncr;
time_t dateMonthOrdinal;
- int dateHaveOrdinalMonth;
+ time_t dateHaveOrdinalMonth;
time_t dateDayOrdinal;
time_t dateDayNumber;
- int dateHaveDay;
+ time_t dateHaveDay;
- const char *dateStart;
- const char *dateInput;
time_t *dateRelPointer;
- int dateDigitCount;
+ time_t dateDigitCount;
Tcl_Obj* messages; /* Error messages */
const char* separatrix; /* String separating messages */
@@ -140,8 +144,19 @@ typedef enum _MERIDIAN {
#define CLOCK_MIN_TOK_CHAIN_BLOCK_SIZE 12
+typedef struct ClockScanToken ClockScanToken;
+
+
+typedef int ClockScanTokenProc(
+ DateInfo *info,
+ ClockScanToken *tok);
+
+
+#define CLF_DATE (1 << 2)
+#define CLF_TIME (1 << 3)
+
typedef enum _CLCKTOK_TYPE {
- CTOKT_EOB=0, CTOKT_DIGIT, CTOKT_SPACE
+ CTOKT_DIGIT = 1, CTOKT_SPACE, CTOKT_WORD
} CLCKTOK_TYPE;
typedef struct ClockFmtScnStorage ClockFmtScnStorage;
@@ -150,18 +165,29 @@ typedef struct ClockFormatToken {
CLCKTOK_TYPE type;
} ClockFormatToken;
-typedef struct ClockScanToken {
+typedef struct ClockScanTokenMap {
unsigned short int type;
+ unsigned short int flags;
unsigned short int minSize;
unsigned short int maxSize;
unsigned short int offs;
+ ClockScanTokenProc *parser;
+} ClockScanTokenMap;
+
+typedef struct ClockScanToken {
+ ClockScanTokenMap *map;
+ unsigned int lookAhead;
+ struct {
+ const char *start;
+ const char *end;
+ } tokWord;
} ClockScanToken;
typedef struct ClockFmtScnStorage {
int objRefCount; /* Reference count shared across threads */
- ClockScanToken **scnTok;
+ ClockScanToken *scnTok;
unsigned int scnTokC;
- ClockFormatToken **fmtTok;
+ ClockFormatToken *fmtTok;
unsigned int fmtTokC;
#if CLOCK_FMT_SCN_STORAGE_GC_SIZE > 0
ClockFmtScnStorage *nextPtr;
@@ -191,8 +217,8 @@ MODULE_SCOPE ClockFmtScnStorage *
Tcl_Obj *objPtr);
MODULE_SCOPE int ClockScan(ClientData clientData, Tcl_Interp *interp,
- TclDateFields *date, Tcl_Obj *strObj,
- ClockFmtScnCmdArgs *opts);
+ register DateInfo *info,
+ Tcl_Obj *strObj, ClockFmtScnCmdArgs *opts);
/*
* Other externals.
diff --git a/generic/tclGetDate.y b/generic/tclGetDate.y
index 571b7df..54087ca 100644
--- a/generic/tclGetDate.y
+++ b/generic/tclGetDate.y
@@ -902,7 +902,6 @@ TclClockFreeScan(
yyHaveDate = 0;
yyHaveTime = 0;
- yyHour = 0; yyMinutes = 0; yySeconds = 0; yyMeridian = MER24;
yyHaveZone = 0;
yyTimezone = 0; yyDSTmode = DSTmaybe;
diff --git a/tests-perf/clock.perf.tcl b/tests-perf/clock.perf.tcl
index 969e279..93a78e4 100644
--- a/tests-perf/clock.perf.tcl
+++ b/tests-perf/clock.perf.tcl
@@ -25,19 +25,82 @@ proc _test_get_commands {lst} {
regsub -all {(?:^|\n)[ \t]*(\#[^\n]*)(?=\n\s*[\{\#])} $lst "\n{\\1}"
}
+proc _test_out_total {} {
+ upvar _ _
+
+ puts [string repeat ** 40]
+ puts [format "Total %d cases in %.2f sec.:" [llength $_(itcnt)] [expr {[llength $_(itcnt)] * $_(reptime) / 1000.0}]]
+ lset _(m) 0 [format %.6f [expr [join $_(ittm) +]]]
+ lset _(m) 2 [expr [join $_(itcnt) +]]
+ lset _(m) 4 [expr {[lindex $_(m) 2] / ([llength $_(itcnt)] * $_(reptime) / 1000.0)}]
+ puts $_(m)
+ puts "Average:"
+ lset _(m) 0 [format %.6f [expr {[lindex $_(m) 0] / [llength $_(itcnt)]}]]
+ lset _(m) 2 [expr {[lindex $_(m) 2] / [llength $_(itcnt)]}]
+ lset _(m) 4 [expr {[lindex $_(m) 2] * (1000 / $_(reptime))}]
+ puts $_(m)
+ puts [string repeat ** 40]
+ puts ""
+}
+
+proc _test_run {reptime lst {outcmd {puts {$_(r)}}}} {
+ upvar _ _
+ array set _ [list ittm {} itcnt {} itrate {} reptime $reptime]
+
+ foreach _(c) [_test_get_commands $lst] {
+ puts "% [regsub -all {\n[ \t]*} $_(c) {; }]"
+ if {[regexp {\s*\#} $_(c)]} continue
+ set _(r) [if 1 $_(c)]
+ if {$outcmd ne {}} $outcmd
+ puts [set _(m) [timerate $_(c) $reptime]]
+ lappend _(ittm) [lindex $_(m) 0]
+ lappend _(itcnt) [lindex $_(m) 2]
+ lappend _(itrate) [lindex $_(m) 4]
+ puts ""
+ }
+ _test_out_total
+}
+
proc test-scan {{reptime 1000}} {
- foreach _(c) [_test_get_commands {
- # Scan : date
+ _test_run $reptime {
+ # Scan : date (in gmt)
{clock scan "25.11.2015" -format "%d.%m.%Y" -base 0 -gmt 1}
+ # Scan : date (system time zone, with base)
+ {clock scan "25.11.2015" -format "%d.%m.%Y" -base 0}
+ # Scan : date (system time zone, without base)
+ {clock scan "25.11.2015" -format "%d.%m.%Y"}
+ # Scan : greedy match
+ {clock scan "111" -format "%d%m%y" -base 0 -gmt 1}
{clock scan "1111" -format "%d%m%y" -base 0 -gmt 1}
- {**STOP** : Wed Nov 25 01:00:00 CET 2015}
- # Scan : long format test (allock chain)
- {clock scan "25.11.2015" -format "%d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y" -base 0 -gmt 1}
- # Scan : dynamic, very long format test (create obj representation, allock chain, GC, etc):
- {clock scan "25.11.2015" -format [string repeat "[incr i] %d.%m.%Y %d.%m.%Y" 10] -base 0 -gmt 1}
- # Scan : again:
- {clock scan "25.11.2015" -format [string repeat "[incr i -1] %d.%m.%Y %d.%m.%Y" 10] -base 0 -gmt 1}
+ {clock scan "11111" -format "%d%m%y" -base 0 -gmt 1}
+ {clock scan "111111" -format "%d%m%y" -base 0 -gmt 1}
+
+ # Scan : date-time (in gmt)
+ {clock scan "25.11.2015 10:35:55" -format "%d.%m.%Y %H:%M:%S" -base 0 -gmt 1}
+ # Scan : date-time (system time zone with base)
+ {clock scan "25.11.2015 10:35:55" -format "%d.%m.%Y %H:%M:%S" -base 0}
+ # Scan : date-time (system time zone without base)
+ {clock scan "25.11.2015 10:35:55" -format "%d.%m.%Y %H:%M:%S"}
+ # Scan : dynamic format (cacheable)
+ {clock scan "25.11.2015 10:35:55" -format [string trim "%d.%m.%Y %H:%M:%S "] -base 0 -gmt 1}
+
+ # Scan : zone only
+ {clock scan "CET" -format "%z"}
+ {clock scan "EST" -format "%z"}
+ #{**STOP** : Wed Nov 25 01:00:00 CET 2015}
+
+ # # Scan : long format test (allock chain)
+ # {clock scan "25.11.2015" -format "%d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y %d.%m.%Y" -base 0 -gmt 1}
+ # # Scan : dynamic, very long format test (create obj representation, allock chain, GC, etc):
+ # {clock scan "25.11.2015" -format [string repeat "[incr i] %d.%m.%Y %d.%m.%Y" 10] -base 0 -gmt 1}
+ # # Scan : again:
+ # {clock scan "25.11.2015" -format [string repeat "[incr i -1] %d.%m.%Y %d.%m.%Y" 10] -base 0 -gmt 1}
+ } {puts [clock format $_(r) -locale en]}
+}
+
+proc test-freescan {{reptime 1000}} {
+ _test_run $reptime {
# FreeScan : relative date
{clock scan "5 years 18 months 385 days" -base 0 -gmt 1}
# FreeScan : relative date with relative weekday
@@ -74,17 +137,11 @@ proc test-scan {{reptime 1000}} {
{clock scan "19:18:30 MST" -base 148863600 -gmt 1
clock scan "19:18:30 EST" -base 148863600
}
- }] {
- puts "% [regsub -all {\n[ \t]*} $_(c) {; }]"
- if {[regexp {\s*\#} $_(c)]} continue
- puts [clock format [if 1 $_(c)] -locale en]
- puts [timerate $_(c) $reptime]
- puts ""
- }
+ } {puts [clock format $_(r) -locale en]}
}
proc test-other {{reptime 1000}} {
- foreach _(c) [_test_get_commands {
+ _test_run $reptime {
# Bad zone
{catch {clock scan "1 day" -timezone BAD_ZONE -locale en}}
**STOP**
@@ -92,18 +149,13 @@ proc test-other {{reptime 1000}} {
{set i 0; time { clock scan "[incr i] - 25.11.2015" -format "$i - %d.%m.%Y" -base 0 -gmt 1 } 50}
# Scan : test reusability of GC objects (format is dynamic, so tcl-obj removed with last reference)
{set i 50; time { clock scan "[incr i -1] - 25.11.2015" -format "$i - %d.%m.%Y" -base 0 -gmt 1 } 50}
- }] {
- puts "% [regsub -all {\n[ \t]*} $_(c) {; }]"
- if {[regexp {\s*\#} $_(c)]} continue
- puts [if 1 $_(c)]
- puts [timerate $_(c) $reptime]
- puts ""
}
}
proc test {{reptime 1000}} {
puts ""
test-scan $reptime
+ #test-freescan $reptime
test-other $reptime
puts \n**OK**