diff options
Diffstat (limited to 'project/dclib/dclib-numeric.c')
| -rw-r--r-- | project/dclib/dclib-numeric.c | 3194 |
1 files changed, 3070 insertions, 124 deletions
diff --git a/project/dclib/dclib-numeric.c b/project/dclib/dclib-numeric.c index 50fd266..0b21975 100644 --- a/project/dclib/dclib-numeric.c +++ b/project/dclib/dclib-numeric.c @@ -14,16 +14,16 @@ * * *************************************************************************** * * - * Copyright (c) 2012-2021 by Dirk Clemens <wiimm@wiimm.de> * + * Copyright (c) 2012-2022 by Dirk Clemens <wiimm@wiimm.de> * * * *************************************************************************** * * - * This program is free software; you can redistribute it and/or modify * + * This library is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * - * This program is distributed in the hope that it will be useful, * + * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * @@ -36,10 +36,12 @@ #include <string.h> #include <unistd.h> +#include <stddef.h> #include <time.h> #include <stdio.h> #include <errno.h> #include <sys/time.h> +#include <sys/resource.h> #include <fcntl.h> #include "dclib-basics.h" @@ -966,6 +968,213 @@ int CheckIndexCEnd ( int max, int * p_begin, int count ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// lt/eq/gt /////////////// +/////////////////////////////////////////////////////////////////////////////// + +char * ScanCompareOp ( compare_operator_t *dest_op, ccp arg ) +{ + compare_operator_t op = COP_FALSE; + ccp ptr = arg; + if (ptr) + { + while ( *ptr > 0 && *ptr <= ' ' ) + ptr++; + + if ( (uchar)ptr[0] == 0xe2 && (uchar)ptr[1] == 0x89 ) + { + switch ((uchar)ptr[2]) + { + case 0xa0: op = COP_NE; ptr += 3; break; // UTF8 ≠ + case 0xa4: op = COP_LE; ptr += 3; break; // UTF8 ≤ + case 0xa5: op = COP_GE; ptr += 3; break; // UTF8 ≥ + } + } + else if ( *ptr == '!' ) + { + if ( *++ptr == '=' ) + { + op = COP_NE; + ptr++; + } + } + else + switch (*ptr) + { + case '=': + op = COP_EQ; + if ( *++ptr == '=' ) + ptr++; + break; + + case '<': + op = COP_LT; + switch (*++ptr) + { + case '=': + op = COP_LE; + ptr++; + break; + case '>': + op = COP_NE; + ptr++; + break; + } + break; + + case '>': + op = COP_GT; + if ( *++ptr == '=' ) + { + op = COP_GE; + ptr++; + } + break; + } + } + + if (dest_op) + *dest_op = op; + return op == COP_FALSE ? (char*)arg : (char*)ptr; +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp GetCompareOpName ( compare_operator_t op ) +{ + switch (op) + { + case COP_EQ: return "=="; + case COP_LT: return "<"; + case COP_GT: return ">"; + case COP_NE: return "!="; + case COP_LE: return "<="; + case COP_GE: return ">="; + case COP_TRUE: return "*"; + default: return "%"; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpStat ( int stat, compare_operator_t op ) +{ + switch (op) + { + case COP_EQ: return stat == 0; + case COP_LT: return stat < 0; + case COP_GT: return stat > 0; + case COP_NE: return stat != 0; + case COP_LE: return stat <= 0; + case COP_GE: return stat >= 0; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpINT ( int a, compare_operator_t op, int b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpUINT ( uint a, compare_operator_t op, uint b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpI64 ( s64 a, compare_operator_t op, s64 b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpU64 ( u64 a, compare_operator_t op, u64 b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpFLT ( float a, compare_operator_t op, float b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool CompareByOpDBL ( double a, compare_operator_t op, double b ) +{ + switch (op) + { + case COP_EQ: return a == b; + case COP_LT: return a < b; + case COP_GT: return a > b; + case COP_NE: return a != b; + case COP_LE: return a <= b; + case COP_GE: return a >= b; + case COP_TRUE: return true; + default: return false; + } +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// encoding/decoding /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -990,7 +1199,7 @@ ccp GetEncodingName ( EncodeMode_t em ) uint GetEscapedSize ( - // returns the needed buffer size for PrintEscapedString() + // returns the needed buffer size for PrintEscapedString() ccp source, // NULL or string to print int src_len, // length of string. if -1, str is null terminated @@ -1435,7 +1644,7 @@ uint EncodeBase64 // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -1564,7 +1773,7 @@ uint EncodeBase64ml // ml: multi line // returns the number of scanned bytes of 'source' char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 const void *source, // NULL or data to encode int source_len, // length of 'source'; if <0: use strlen(source) const char encode64[64+1], // encoding table; if NULL: use TableEncode64default @@ -1985,7 +2194,7 @@ uint EncodeByMode // returns the number of valid bytes in 'buf'. Result is NULL-terminated. char *buf, // valid destination buffer - uint buf_size, // size of 'buf', >= 4 + uint buf_size, // size of 'buf' >= 4 ccp source, // string to encode int slen, // length of string. if -1, str is null terminated EncodeMode_t emode // encoding mode @@ -2172,7 +2381,7 @@ char * EscapeString buf[0] = '$'; buf[1] = buf[len+2] = quote_char; buf[len+3] = 0; - + } else if ( quote_mode == 2 ) { @@ -2190,9 +2399,132 @@ char * EscapeString // /////////////////////////////////////////////////////////////////////////////// +/////////////// struct Escape_t /////////////// +/////////////////////////////////////////////////////////////////////////////// +// https://en.wikipedia.org/wiki/ANSI_escape_code + +const char ecape_stat_name[ESCST__N+1][5] = +{ + "-", // ESCST_NONE + "CHAR", // ESCST_CHAR + "FE", // ESCST_FE + "SS2", // ESCST_SS2 + "SS3", // ESCST_SS3 + "CSI", // ESCST_CSI + "OSC", // ESCST_OSC + "", // ESCST__N +}; + +/////////////////////////////////////////////////////////////////////////////// + +EscapeStat_t CheckEscape ( Escape_t *esc, cvp source, cvp end, bool check_esc ) +{ + DASSERT(esc); + memset(esc,0,sizeof(*esc)); + if (!source) + return 0; + + ccp src = source; + if (!end) + end = src + strlen(src); + if ( src >= (ccp)end ) + return 0; + + ccp beg = src; + if (check_esc) + { + if ( *src != '\e' ) + { + esc->status = ESCST_CHAR; + esc->code = *src++; + goto term; + } + + src++; + if ( src >= (ccp)end ) + return 0; + } + esc->code = *src++; + + int term_wd = 0; + switch (esc->code) + { + case 'N': //--- SS2: Single Shift 2 --- + if ( src < (ccp)end ) + { + esc->status = ESCST_SS2; + esc->code = *src++; + } + break; + + case 'O': //--- SS3: Single Shift 3 --- + if ( src < (ccp)end ) + { + esc->status = ESCST_SS3; + esc->code = *src++; + } + break; + + case '[': //--- CSI --- + while ( src < (ccp)end && *src >= 0x30 && *src <= 0x3f ) + src++; + while ( src < (ccp)end && *src >= 0x20 && *src <= 0x2f ) + src++; + if ( src < (ccp)end && *src >= 0x40 && *src <= 0x7e ) + { + esc->status = ESCST_CSI; + esc->code = *src++; + } + break; + + + case ']': //--- OSC --- + // terminate by ST (ESC \ == 0x9c) or BEL (7) + for ( ; src < (ccp)end; src++ ) + { + if ( *src == 7 || (u8)*src == 0x9c ) + { + src++; + term_wd = 1; + esc->status = ESCST_OSC; + break; + } + if ( src+1 < (ccp)end && src[0] == '\e' && src[1] == '\\' ) + { + src += 2; + term_wd = 2; + esc->status = ESCST_OSC; + break; + } + } + break; + + default: //--- single char --- + esc->status = ESCST_CHAR; + break; + } + + term:; + if ( esc->status != ESCST_NONE ) + { + esc->scanned_len = src - (ccp)source; + if ( esc->status > ESCST_CHAR ) + { + esc->esc.ptr = beg; + esc->esc.len = src - beg - term_wd; + } + } + + return esc->status; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// time /////////////// /////////////////////////////////////////////////////////////////////////////// +ccp micro_second_char = "u"; + s64 timezone_adjust_sec = -1; s64 timezone_adjust_usec = -1; s64 timezone_adjust_nsec = -1; @@ -2285,6 +2617,24 @@ struct timespec GetClockTime ( bool localtime ) return ts; } +//----------------------------------------------------------------------------- + +struct timespec GetClockTimer(void) +{ + struct timespec ts; + + #if HAVE_CLOCK_GETTIME + if (!clock_gettime(CLOCK_MONOTONIC,&ts)) + return ts; + #endif + + // fall back + struct timeval tval = GetTimeOfDay(false); + ts.tv_sec = tval.tv_sec; + ts.tv_nsec = tval.tv_usec * NSEC_PER_USEC; + return ts; +} + /////////////////////////////////////////////////////////////////////////////// DayTime_t GetDayTime ( bool localtime ) @@ -2346,50 +2696,114 @@ u_nsec_t GetTimeNSec ( bool localtime ) return (u_nsec_t)(ts.tv_sec) * NSEC_PER_SEC + ts.tv_nsec; } +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +CurrentTime_t current_time = {0}; + +CurrentTime_t GetCurrentTime ( bool localtime ) +{ + struct timeval tval = GetTimeOfDay(localtime); + + CurrentTime_t ct; + ct.sec = tval.tv_sec; + ct.usec = tval.tv_sec * USEC_PER_SEC + tval.tv_usec; + ct.msec = ct.usec / 1000; + return ct; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// timer /////////////// /////////////////////////////////////////////////////////////////////////////// -static time_t time_base = 0; +u_sec_t GetTimerSec() +{ + struct timespec ts = GetClockTimer(); + return ts.tv_sec; +} /////////////////////////////////////////////////////////////////////////////// u_msec_t GetTimerMSec() { - struct timeval tv; - gettimeofday(&tv,NULL); - if (!time_base) - time_base = tv.tv_sec; - - return (u_msec_t)( tv.tv_sec - time_base ) * MSEC_PER_SEC - + tv.tv_usec/USEC_PER_MSEC; + struct timespec ts = GetClockTimer(); + return ts.tv_sec * MSEC_PER_SEC + ts.tv_nsec / NSEC_PER_MSEC; } /////////////////////////////////////////////////////////////////////////////// u_usec_t GetTimerUSec() { - struct timeval tv; - gettimeofday(&tv,NULL); - if (!time_base) - time_base = tv.tv_sec; + struct timespec ts = GetClockTimer(); + return ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; +} - return (u_usec_t)( tv.tv_sec - time_base ) * USEC_PER_SEC + tv.tv_usec; +/////////////////////////////////////////////////////////////////////////////// + +u_nsec_t GetTimerNSec() +{ + struct timespec ts = GetClockTimer(); + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; } /////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintRefTime +( -u_usec_t GetTimerNSec() + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +) { - struct timespec ts = GetClockTime(false); - if (!time_base) - time_base = ts.tv_sec; + u_sec_t ref; + if (!ref_tim) + ref = GetTimeSec(false); + else + { + if (!*ref_tim) + *ref_tim = GetTimeSec(false); + ref = *ref_tim; + } - return (u_usec_t)( ts.tv_sec - time_base ) * NSEC_PER_SEC + ts.tv_nsec; + ccp format = tim >= ref - 10*SEC_PER_HOUR && tim <= ref + 10*SEC_PER_HOUR + ? " %k:%M:%S" + : tim >= ref - 12*SEC_PER_DAY && tim <= ref + 10*SEC_PER_DAY + ? " %e. %k:%M" + : "%F"; + return PrintTimeByFormat(format,tim); +} + +//----------------------------------------------------------------------------- + +ccp PrintRefTimeUTC +( + + u_sec_t tim, // seconds since epoch; 0 is replaced by time() + u_sec_t *ref_tim // NULL or ref_tim; if *ref_tim==0: write current time + // it is used to select format +) +{ + u_sec_t ref; + if (!ref_tim) + ref = GetTimeSec(false); + else + { + if (!*ref_tim) + *ref_tim = GetTimeSec(false); + ref = *ref_tim; + } + + ccp format = tim >= ref - 10*SEC_PER_HOUR && tim <= ref + 10*SEC_PER_HOUR + ? " %k:%M:%S" + : tim >= ref - 12*SEC_PER_DAY && tim <= ref + 10*SEC_PER_DAY + ? " %e. %k:%M" + : "%F"; + return PrintTimeByFormatUTC(format,tim); } -/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// ccp PrintTimeByFormat @@ -2397,14 +2811,12 @@ ccp PrintTimeByFormat // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ) { - if (!tim) - tim = time(0); - char buf[100]; - struct tm *tm = localtime(&tim); + const time_t reftime = tim ? tim : time(0); + struct tm *tm = localtime(&reftime); const uint len = strftime(buf,sizeof(buf),format,tm); return CopyCircBuf(buf,len+1); } @@ -2416,14 +2828,12 @@ ccp PrintTimeByFormatUTC // returns temporary buffer by GetCircBuf(); ccp format, // format string for strftime() - time_t tim // seconds since epoch; 0 is replaced by time() + u_sec_t tim // seconds since epoch; 0 is replaced by time() ) { - if (!tim) - tim = time(0); - char buf[100]; - struct tm *tm = gmtime(&tim); + const time_t reftime = tim ? tim : time(0); + struct tm *tm = gmtime(&reftime); const uint len = strftime(buf,sizeof(buf),format,tm); return CopyCircBuf(buf,len+1); } @@ -2484,6 +2894,90 @@ ccp PrintNSecByFormatUTC return CopyCircBuf(buf,len+1); } +//----------------------------------------------------------------------------- + +ccp PrintNTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +) +{ + return PrintNSecByFormat(format,ntime/NSEC_PER_SEC,ntime%NSEC_PER_SEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintNTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_nsec_t ntime +) +{ + return PrintNSecByFormatUTC(format,ntime/NSEC_PER_SEC,ntime%NSEC_PER_SEC); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintUTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +) +{ + return PrintNSecByFormat(format,utime/USEC_PER_SEC,utime%USEC_PER_SEC*NSEC_PER_USEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintUTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_usec_t utime +) +{ + return PrintNSecByFormatUTC(format,utime/USEC_PER_SEC,utime%USEC_PER_SEC*NSEC_PER_USEC); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintMTimeByFormat +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +) +{ + return PrintNSecByFormat(format,mtime/MSEC_PER_SEC,mtime%MSEC_PER_SEC*NSEC_PER_MSEC); +} + +//----------------------------------------------------------------------------- + +ccp PrintMTimeByFormatUTC +( + // returns temporary buffer by GetCircBuf(); + + ccp format, // format string for strftime() + // 1-9 '@' in row replaced by digits of 'usec' + u_msec_t mtime +) +{ + return PrintNSecByFormatUTC(format,mtime/MSEC_PER_SEC,mtime%MSEC_PER_SEC*NSEC_PER_MSEC); +} + /////////////////////////////////////////////////////////////////////////////// ccp PrintTimevalByFormat @@ -2811,13 +3305,16 @@ ccp PrintTimer3 u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 3 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { if (!buf) buf = GetCircBuf( buf_size = 4 ); + const bool aligned = mode & DC_SFORM_ALIGN; - if ( !sec && usec >= 0 && usec <= 999999 ) + if ( !sec && !usec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( !sec && usec >= 0 && usec <= 999999 ) { if (!usec) StringCopyS(buf,buf_size,aligned?" 0":"0"); @@ -2868,13 +3365,16 @@ ccp PrintTimer4 u64 sec, // seconds to print int usec, // 0...999999: usec fraction, // otherwise suppress ms/us output - bool aligned // true: print aligned 4 character output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_DASH ) { if (!buf) buf = GetCircBuf( buf_size = 5 ); + const bool aligned = mode & DC_SFORM_ALIGN; - if ( sec < 10 && usec >= 0 && usec <= 999999 ) + if ( !sec && !usec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( sec < 10 && usec >= 0 && usec <= 999999 ) { if (sec) snprintf(buf,buf_size,"%llu.%us",sec,usec/100000); @@ -2915,38 +3415,53 @@ ccp PrintTimer4 /////////////////////////////////////////////////////////////////////////////// -// [[nsec]] -> PrintTimer6U(), PrintTimer6N() - -ccp PrintTimer6 +ccp PrintTimer6N ( char * buf, // result buffer (>6 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL u64 sec, // seconds to print - int usec, // 0...999999: usec fraction, - // otherwise suppress ms/us output - bool aligned // true: print aligned 6 character output + int nsec, // 0...999999999: nsec fraction, + // otherwise suppress ms/us/ns output + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_CENTER | DC_SFORM_DASH ) { + if ( mode & DC_SFORM_CENTER ) + { + char buf2[20]; + PrintTimer6N(buf2,sizeof(buf2),sec,nsec,mode&~(DC_SFORM_CENTER|DC_SFORM_ALIGN)); + return StringCenterS(buf,buf_size,buf2,6); + } + + char temp[20]; if (!buf) - buf = GetCircBuf( buf_size = 7 ); + { + buf = temp; + buf_size = sizeof(temp); + } - if (aligned) + const bool nsec_valid = nsec >= 0 && nsec <= 999999999; + + if ( mode & DC_SFORM_ALIGN ) { - if ( sec < 10 && usec >= 0 && usec <= 999999 ) + if ( !sec && !nsec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size," -"); + else if ( sec < 10 && nsec_valid ) { - usec += 1000000 * sec; - if (!usec) + u_nsec_t sum = sec *NSEC_PER_SEC + nsec; + if (!sum) StringCopyS(buf,buf_size," 0"); - else if ( usec < 10000 ) - snprintf(buf,buf_size,"%4uus",usec); + else if ( sum < 10000 ) + snprintf(buf,buf_size,"%4lluns",sum); + else if ( sum < 10000000 ) + snprintf(buf,buf_size,"%4lluus",sum/1000); else - snprintf(buf,buf_size,"%4ums",usec/1000); + snprintf(buf,buf_size,"%4llums",sum/1000000); } else if ( sec < 100 ) { - if ( usec >= 0 && usec <= 999999 ) - snprintf(buf,buf_size,"%2llu.%02us",sec,usec/10000); + if (nsec_valid ) + snprintf(buf,buf_size,"%2llu.%02us",sec,nsec/10000000); else snprintf(buf,buf_size,"%5llus",sec); } @@ -2975,21 +3490,25 @@ ccp PrintTimer6 } else { - if ( sec < 10 ) + if ( !sec && !nsec && mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size,"-"); + else if ( sec < 10 && nsec_valid ) { - usec += 1000000 * sec; - if (!usec) + u_nsec_t sum = sec *NSEC_PER_SEC + nsec; + if (!sum) StringCopyS(buf,buf_size,"0"); - else if ( usec < 10000 ) - snprintf(buf,buf_size,"%uus",usec); + else if ( sum < 10000 ) + snprintf(buf,buf_size,"%lluns",sum); + else if ( sum < 10000000 ) + snprintf(buf,buf_size,"%lluus",sum/1000); else - snprintf(buf,buf_size,"%ums",usec/1000); + snprintf(buf,buf_size,"%llums",sum/1000000); } else if ( sec < 100 ) { - if ( usec >= 0 && usec <= 999999 ) + if ( nsec_valid ) { - const uint val2 = usec/10000; + const uint val2 = nsec/10000000; if (val2) snprintf(buf,buf_size,"%llu.%02us",sec,val2); else @@ -3052,7 +3571,7 @@ ccp PrintTimer6 } } - return buf; + return buf == temp ? CopyCircBuf0(buf,strlen(buf)) : buf; } /////////////////////////////////////////////////////////////////////////////// @@ -3065,14 +3584,19 @@ ccp PrintTimerUSec4s // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { if ( !buf || buf_size < 2 ) buf = GetCircBuf( buf_size = 5 ); if (!usec) - StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } else { char sign; @@ -3084,7 +3608,7 @@ ccp PrintTimerUSec4s else sign = mode & DC_SFORM_PLUS ? '+' : ' '; - PrintTimerUSec3(buf+1,buf_size-1,usec,mode&DC_SFORM_ALIGN); + PrintTimerUSec3(buf+1,buf_size-1,usec,mode&~DC_SFORM_PLUS); *buf = ' '; char *ptr = buf+1; @@ -3097,22 +3621,25 @@ ccp PrintTimerUSec4s /////////////////////////////////////////////////////////////////////////////// -// [[nsec]] -> PrintTimer7Us(), PrintTimer7Ns() - ccp PrintTimerUSec7s ( char * buf, // result buffer (>7 bytes) // If NULL, a local circulary static buffer is used size_t buf_size, // size of 'buf', ignored if buf==NULL s_usec_t usec, // microseconds to print - sizeform_mode_t mode // support of DC_SFORM_ALIGN, DC_SFORM_PLUS + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH ) { if ( !buf || buf_size < 2 ) buf = GetCircBuf( buf_size = 8 ); if (!usec) - StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } else { char sign; @@ -3135,6 +3662,123 @@ ccp PrintTimerUSec7s return buf; } +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintTimerNSec7s +( + char * buf, // result buffer (>7 bytes) + // If NULL, a local circulary static buffer is used + size_t buf_size, // size of 'buf', ignored if buf==NULL + s_nsec_t nsec, // nansoseconds to print + sizeform_mode_t mode // DC_SFORM_ALIGN | DC_SFORM_PLUS | DC_SFORM_DASH +) +{ + if ( !buf || buf_size < 2 ) + buf = GetCircBuf( buf_size = 8 ); + + if (!nsec) + { + if ( mode & DC_SFORM_DASH ) + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " -" : "-" ); + else + StringCopyS(buf,buf_size, mode & DC_SFORM_ALIGN ? " 0" : "0" ); + } + else + { + char sign; + if ( nsec < 0 ) + { + nsec = -nsec; + sign = '-'; + } + else + sign = mode & DC_SFORM_PLUS ? '+' : ' '; + + PrintTimerNSec6(buf+1,buf_size-1, nsec, mode & DC_SFORM_ALIGN ); + *buf = ' '; + + char *ptr = buf+1; + while ( *ptr == ' ' ) + ptr++; + ptr[-1] = sign; + } + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge3Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 ? " +" : PrintTimer3(0,0,now,-1,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge4Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 + ? PrintTimerSec4s(0,0,now,DC_SFORM_ALIGN) + : PrintTimer4(0,0,now,-1,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge6Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + now -= time; + return now < 0 ? " +" : PrintTimerSec6(0,0,now,DC_SFORM_ALIGN); +} + +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintAge7Sec +( + u_sec_t now, // NULL or current time by GetTimeSec(false) + u_sec_t time // time to print +) +{ + if (!time) + return " -"; + + if (!now) + now = GetTimeSec(false); + + return PrintTimerSec7s(0,0,now-time,DC_SFORM_ALIGN); +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// scan date & time /////////////// @@ -3211,7 +3855,7 @@ char * ScanIntervalUSec { struct timespec ts; char *res = ScanIntervalTS(&ts,source); - + if (res_usec) *res_usec = ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; return res; @@ -3227,7 +3871,7 @@ char * ScanIntervalNSec { struct timespec ts; char *res = ScanIntervalTS(&ts,source); - + if (res_nsec) *res_nsec = ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; return res; @@ -3657,6 +4301,1821 @@ time_t year2time ( int year ) // /////////////////////////////////////////////////////////////////////////////// +/////////////// usage counter /////////////// +/////////////////////////////////////////////////////////////////////////////// + +int usage_count_enabled = 0; + +void EnableUsageCount() +{ + usage_count_enabled++; + UpdateUsageByMgr(0); +} + +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_UsageCount[] = +{ + #undef SRT_NAME + #define SRT_NAME UsageCount_t + + DEF_SRT_UINT( par.enabled, "enabled" ), + DEF_SRT_UINT( par.used, "used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].count, "count" ), + DEF_SRT_UINT( entry[0].elapsed_nsec, "elapsed" ), + DEF_SRT_UINT( entry[0].time_sec, "time" ), + DEF_SRT_FLOAT( entry[0].top1s, "top1s" ), + DEF_SRT_FLOAT( entry[0].top5s, "top5s" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +void ClearUsageCount ( UsageCount_t *uc, int count ) +{ + if (!uc) + return; + + uint index = CheckIndex1(uc->par.used,count); + if ( index >= uc->par.used - 1 ) + return; + + uc->par.used = index; + if (!index) + { + const bool enabled = uc->par.enabled; + memset( uc, 0, sizeof(*uc) ); + uc->par.enabled = enabled; + } + else + { + UsageCountEntry_t *e = uc->entry + index; + memset( e, 0, PTR_DISTANCE((u8*)uc->entry+sizeof(uc->entry),e) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[UpdateUsageCount]] + +void UpdateUsageCount ( UsageCount_t *uc, u_nsec_t timer_nsec ) +{ + if (!uc) + return; + + if ( !uc->par.enabled || usage_count_enabled <= 0 ) + { + uc->ref.elapsed_nsec = 0; + uc->ref.count = 0; + uc->par.dirty = false; + return; + } + + if ( !uc->par.is_active ) + { + if ( !uc->par.used && !uc->ref.count ) + return; + uc->par.is_active = true; + } + + UsageCountEntry_t cur = {0}; + cur.elapsed_nsec = timer_nsec ? timer_nsec : GetTimerNSec(); + + uc->par.update_nsec = uc->ref.elapsed_nsec + NSEC_PER_SEC; + if ( cur.elapsed_nsec >= uc->par.update_nsec ) + { + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( uc->par.used > USAGE_COUNT_ENTRIES ) + uc->par.used = USAGE_COUNT_ENTRIES; + + cur.time_sec = GetTimeSec(false); + + if (uc->ref.elapsed_nsec) + { + //--- find index and move data + + int index; + if ( uc->par.used < USAGE_COUNT_FIRST_ADD ) + index = uc->par.used + 1; + else + { + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + for ( index = USAGE_COUNT_FIRST_ADD; + index < uc->par.used; + index++, min = USAGE_COUNT_NEXT_MIN(min) + ) + { + UsageCountEntry_t *e = uc->entry + index; + if ( e->elapsed_nsec < min ) + { + AddUsageCountEntry(e,e-1); + index--; + break; + } + } + if ( ++index > USAGE_COUNT_ENTRIES ) + index = USAGE_COUNT_ENTRIES; + } + + if ( index > 1 ) + memmove( uc->entry+1, uc->entry, (index-1) * sizeof(*uc->entry) ); + + if ( uc->par.used < index ) + uc->par.used = index; + + + //--- update first entry + + UsageCountEntry_t *e = uc->entry; + *e = uc->ref; + e->elapsed_nsec = cur.elapsed_nsec - e->elapsed_nsec; + + + //--- update top value (1s) + + if (e->elapsed_nsec) + e->top1s = (double)( NSEC_PER_SEC * e->count ) / e->elapsed_nsec; + + + //--- update top value (5s) + + u_nsec_t elapsed5 = 0, count5 = 0; + UsageCountEntry_t *e5 = uc->entry; + for ( int i = 0; i < uc->par.used && elapsed5 < 5*NSEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_nsec; + count5 += e5->count; + } + + if (elapsed5) + { + if ( count5 == e->count ) + elapsed5 = 5*NSEC_PER_SEC; + e->top5s = (double)( NSEC_PER_SEC * count5 ) / elapsed5; + } + } + + uc->ref = cur; + uc->par.update_nsec = uc->ref.elapsed_nsec + NSEC_PER_SEC; + } + + uc->par.force_update_nsec = uc->ref.elapsed_nsec + NSEC_PER_MIN; + uc->par.dirty = uc->ref.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageCountIncrement ( UsageCount_t *uc ) +{ + DASSERT(uc); + uc->par.is_active = true; + UpdateUsageCount(uc,0); + if ( usage_count_enabled > 0 ) + { + uc->ref.count++; + uc->par.dirty = true; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageCountAdd ( UsageCount_t *uc, int add ) +{ + DASSERT(uc); + if ( add > 0 ) + { + uc->par.is_active = true; + UpdateUsageCount(uc,0); + if ( usage_count_enabled > 0 ) + { + uc->ref.count += add; + uc->par.dirty = true; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddUsageCountEntry ( UsageCountEntry_t *dest, const UsageCountEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->count += add->count; + dest->elapsed_nsec += add->elapsed_nsec; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top1s < add->top1s ) + dest->top1s = add->top1s; + if ( dest->top5s < add->top5s ) + dest->top5s = add->top5s; +} + +/////////////////////////////////////////////////////////////////////////////// + +UsageCountEntry_t GetUsageCount ( const UsageCount_t *uc, s_nsec_t nsec ) +{ + DASSERT(uc); + UsageCountEntry_t res = {0}; + const UsageCountEntry_t *e = uc->entry; + for ( int i = 0; i < uc->par.used && nsec > 0; i++, e++ ) + { + nsec -= e->elapsed_nsec; + AddUsageCountEntry(&res,e); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +PointerList_t usage_count_mgr = { .used = 0, .grow = 10 }; + +/////////////////////////////////////////////////////////////////////////////// + +u_nsec_t MaintainUsageByMgr ( u_nsec_t now_nsec ) +{ + // returns delta time to next maintenance based on GetTimerNSec() + + if (!now_nsec) + now_nsec = GetTimerNSec(); + u_nsec_t next_nsec = now_nsec + NSEC_PER_MIN; + + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci = usage_count_mgr.list[i]; + if (!uci) + continue; + + const UsageParam_t *par = GetUsageParam(uci); + if (par) + { + if ( par->dirty && par->update_nsec && par->update_nsec <= now_nsec + || par->force_update_nsec && par->force_update_nsec <= now_nsec ) + { + UpdateByUCI(uci,now_nsec); + } + if ( par->dirty && par->update_nsec && next_nsec > par->update_nsec ) + next_nsec = par->update_nsec; + } + } + + return next_nsec; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateByUCI ( const UsageCtrl_t *uci, u_nsec_t now_nsec ) +{ + if (uci) + { + if (uci->update_func) + uci->update_func(uci,now_nsec); + else if (uci->uc) + UpdateUsageCount(uci->uc,now_nsec); + else if (uci->ud) + UpdateUsageDuration(uci->ud,now_nsec); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageByMgr ( u_nsec_t timer_nsec ) +{ + if (!timer_nsec) + timer_nsec = GetTimerNSec(); + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci = usage_count_mgr.list[i]; + if (uci) + { + if (uci->uc) + UpdateUsageCount(uci->uc,timer_nsec); + else if (uci->ud) + UpdateUsageDuration(uci->ud,timer_nsec); + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if 0 + static int sort_usage_count ( const UsageCtrl_t *a, const UsageCtrl_t *b ) + { + DASSERT( a && b ); + return strcmp(a->key1,b->key1); + } +#endif + +//----------------------------------------------------------------------------- + +void AddToUsageMgr ( const UsageCtrl_t *uci ) +{ + if ( !uci || !uci->key1 ) + return; + + // no duplicates! + for ( int i = 0; i < usage_count_mgr.used; i++ ) + { + const UsageCtrl_t *uci2 = usage_count_mgr.list[i]; + if ( uci2 && ( uci2->uc && uci2->uc == uci->uc || uci2->ud && uci2->ud == uci->ud )) + return; + } + + AddToPointerMgr(&usage_count_mgr,uci); + + #if 0 + qsort( usage_count_mgr.list, usage_count_mgr.used, + sizeof(*usage_count_mgr.list), (qsort_func)sort_usage_count ); + #endif +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +void SaveCurrentStateByUsageCountMgr ( FILE *f, uint fw_name ) +{ + DASSERT(f); + const u_nsec_t timer_nsec = GetTimerNSec(); + void **ucip = usage_count_mgr.list; + for ( int i = 0; i < usage_count_mgr.used; i++, ucip++ ) + { + UsageCtrl_t *uci = *ucip; + if (uci) + { + UpdateUsageCount(uci->uc,timer_nsec); + if (uci->srt_prefix) + { + if (uci->uc) + SaveCurrentStateByTable(f,uci->uc,SRT_UsageCount,uci->srt_prefix,fw_name); + else if (uci->ud) + SaveCurrentStateByTable(f,uci->ud,SRT_UsageDuration,uci->srt_prefix,fw_name); + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void RestoreStateByUsageCountMgr ( RestoreState_t *rs ) +{ + DASSERT(rs); + void **ucip = usage_count_mgr.list; + for ( int i = 0; i < usage_count_mgr.used; i++, ucip++ ) + { + UsageCtrl_t *uci = *ucip; + if (uci) + { + if (uci->uc) + { + UsageCount_t *uc = uci->uc; + if (uci->srt_load) + RestoreStateByTable(rs,uc,SRT_UsageCount,uci->srt_load); + if (uci->srt_prefix) + RestoreStateByTable(rs,uc,SRT_UsageCount,uci->srt_prefix); + + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( uc->par.used > USAGE_COUNT_ENTRIES ) + uc->par.used = USAGE_COUNT_ENTRIES; + } + else if (uci->ud) + { + UsageDuration_t *ud = uci->ud; + if (uci->srt_load) + RestoreStateByTable(rs,ud,SRT_UsageDuration,uci->srt_load); + if (uci->srt_prefix) + RestoreStateByTable(rs,ud,SRT_UsageDuration,uci->srt_prefix); + + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( ud->par.used > USAGE_COUNT_ENTRIES ) + ud->par.used = USAGE_COUNT_ENTRIES; + } + } + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// wait counter /////////////// +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_UsageDuration[] = +{ + #undef SRT_NAME + #define SRT_NAME UsageDuration_t + + DEF_SRT_UINT( par.enabled, "enabled" ), + DEF_SRT_UINT( par.used, "used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].elapsed_nsec, "elapsed" ), + DEF_SRT_UINT( entry[0].total_nsec, "total" ), + DEF_SRT_UINT( entry[0].top_nsec, "top" ), + DEF_SRT_UINT( entry[0].count, "count" ), + DEF_SRT_UINT( entry[0].time_sec, "time" ), + DEF_SRT_FLOAT( entry[0].top_cnt_1s, "top-c1" ), + DEF_SRT_FLOAT( entry[0].top_cnt_5s, "top-c5" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +/////////////////////////////////////////////////////////////////////////////// + +// n_rec<0: clear from end, n_rec>=0: clear from index +void ClearUsageDuration ( UsageDuration_t *ud, int count ) +{ + if (!ud) + return; + + uint index = CheckIndex1(ud->par.used,count); + if ( index >= ud->par.used - 1 ) + return; + + ud->par.used = index; + if (!index) + { + const bool enabled = ud->par.enabled; + memset( ud, 0, sizeof(*ud) ); + ud->par.enabled = enabled; + } + else + { + UsageDurationEntry_t *e = ud->entry + index; + memset( e, 0, PTR_DISTANCE((u8*)ud->entry+sizeof(ud->entry),e) ); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// [[UpdateUsageDuration]] + +// TIMER_NSEC: 0 or GetTimerNSec(), but not GetTime*() +void UpdateUsageDuration ( UsageDuration_t *ud, u_nsec_t timer_nsec ) +{ + if (!ud) + return; + + if ( !ud->par.enabled || usage_count_enabled <= 0 ) + { + ud->ref.elapsed_nsec = 0; + ud->ref.count = 0; + ud->par.dirty = false; + return; + } + + if ( !ud->par.is_active ) + { + if ( !ud->par.used && !ud->ref.count ) + return; + ud->par.is_active = true; + } + + UsageDurationEntry_t cur = {0}; + cur.elapsed_nsec = timer_nsec ? timer_nsec : GetTimerNSec(); + + ud->par.update_nsec = ud->ref.elapsed_nsec + NSEC_PER_SEC; + if ( cur.elapsed_nsec >= ud->par.update_nsec ) + { + // needed for the case that USAGE_COUNT_ENTRIES was reduced after restart! + if ( ud->par.used > USAGE_COUNT_ENTRIES ) + ud->par.used = USAGE_COUNT_ENTRIES; + + cur.time_sec = GetTimeSec(false); + + if (ud->ref.elapsed_nsec) + { + //--- find index and move data + + int index; + if ( ud->par.used < USAGE_COUNT_FIRST_ADD ) + index = ud->par.used + 1; + else + { + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + for ( index = USAGE_COUNT_FIRST_ADD; + index < ud->par.used; + index++, min = USAGE_COUNT_NEXT_MIN(min) + ) + { + UsageDurationEntry_t *e = ud->entry + index; + if ( e->elapsed_nsec < min ) + { + AddUsageDurationEntry(e,e-1); + index--; + break; + } + } + if ( ++index > USAGE_COUNT_ENTRIES ) + index = USAGE_COUNT_ENTRIES; + } + + if ( index > 1 ) + memmove( ud->entry+1, ud->entry, (index-1) * sizeof(*ud->entry) ); + + if ( ud->par.used < index ) + ud->par.used = index; + + + //--- update first entry + + UsageDurationEntry_t *e = ud->entry; + *e = ud->ref; + e->elapsed_nsec = cur.elapsed_nsec - e->elapsed_nsec; + + + //--- update top value (1s) + + if (e->elapsed_nsec) + e->top_cnt_1s = (double)( NSEC_PER_SEC * e->count ) / e->elapsed_nsec; + + + //--- update top value (5s) + + u_nsec_t elapsed5 = 0, count5 = 0; + UsageDurationEntry_t *e5 = ud->entry; + for ( int i = 0; i < ud->par.used && elapsed5 < 5*NSEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_nsec; + count5 += e5->count; + } + + if (elapsed5) + { + if ( count5 == e->count ) + elapsed5 = 5*NSEC_PER_SEC; + e->top_cnt_5s = (double)( NSEC_PER_SEC * count5 ) / elapsed5; + } + } + + ud->ref = cur; + ud->par.update_nsec = ud->ref.elapsed_nsec + NSEC_PER_SEC; + } + + ud->par.force_update_nsec = ud->ref.elapsed_nsec + NSEC_PER_MIN; + ud->par.dirty = ud->ref.count > 0; +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageDurationAdd + ( UsageDuration_t *ud, int add, u_nsec_t wait_nsec, u_nsec_t top_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + UpdateUsageDuration(ud,0); + + if ( add > 0 && usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count += add; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < top_nsec ) + ud->ref.top_nsec = top_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UpdateUsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + UpdateUsageDuration(ud,0); + if ( usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count++; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < wait_nsec ) + ud->ref.top_nsec = wait_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +void UsageDurationIncrement ( UsageDuration_t *ud, u_nsec_t wait_nsec ) +{ + DASSERT(ud); + ud->par.is_active = true; + if ( usage_count_enabled > 0 ) + { + ud->par.dirty = true; + ud->ref.count++; + ud->ref.total_nsec += wait_nsec; + if ( ud->ref.top_nsec < wait_nsec ) + ud->ref.top_nsec = wait_nsec; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +UsageDurationEntry_t GetUsageDuration ( const UsageDuration_t *ud, s_nsec_t nsec ) +{ + DASSERT(ud); + UsageDurationEntry_t res = {0}; + const UsageDurationEntry_t *e = ud->entry; + for ( int i = 0; i < ud->par.used && nsec > 0; i++, e++ ) + { + nsec -= e->elapsed_nsec; + AddUsageDurationEntry(&res,e); + } + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +void AddUsageDurationEntry ( UsageDurationEntry_t *dest, const UsageDurationEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->count += add->count; + dest->elapsed_nsec += add->elapsed_nsec; + dest->total_nsec += add->total_nsec; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top_nsec < add->top_nsec ) + dest->top_nsec = add->top_nsec; + + if ( dest->top_cnt_1s < add->top_cnt_1s ) + dest->top_cnt_1s = add->top_cnt_1s; + if ( dest->top_cnt_5s < add->top_cnt_5s ) + dest->top_cnt_5s = add->top_cnt_5s; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// list usage count/wait /////////////// +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_usage_count_val ( double val, const float *color_val ) +{ + if ( val <= 0 ) + return " -"; + + static const float color_val0[] = { 100, 200, 300, 500, 1000, 2000 }; + if (!color_val) + color_val = color_val0; + + if ( val > color_val[0] || val >= 9999.9994 ) + { + ccp col = val < color_val[2] + ? ( val < color_val[1] ? colout->mark : colout->info ) + : val < color_val[4] + ? ( val < color_val[3] ? colout->hint : colout->warn ) + : ( val < color_val[5] ? colout->err : colout->bad ); + + return val < 9999.9994 + ? PrintCircBuf("%s%8.3f%s",col,val,colout->reset) + : val < 999999.94 + ? PrintCircBuf("%s%8.1f%s",col,val,colout->reset) + : PrintCircBuf("%s%8.0f%s",col,val,colout->reset); + } + + char *res = PrintCircBuf("%8.3f",val); + if ( val < 1.0 && res[3] == '0' ) + res[3] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_count + ( u32 n_wait, u_nsec_t elapsed_nsec, const float *color_val ) +{ + return elapsed_nsec + ? print_usage_count_val( (double)( NSEC_PER_SEC * n_wait ) / elapsed_nsec, color_val ) + : " ×"; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_dur_val ( u_nsec_t dur, const u_nsec_t *color_dur ) +{ + if (!dur) + return " -"; + + ccp res = PrintTimerNSec6(0,0,dur,DC_SFORM_ALIGN); + + static const u_nsec_t color_dur0[] = + { + 10 * NSEC_PER_MSEC, // mark + 50 * NSEC_PER_MSEC, // info + 200 * NSEC_PER_MSEC, // hint + NSEC_PER_SEC, // warn + 8 * NSEC_PER_SEC, // err + NSEC_PER_MIN, // bad + }; + if (!color_dur) + color_dur = color_dur0; + + if ( dur > color_dur[0] ) + { + ccp col = dur < color_dur[2] + ? ( dur < color_dur[1] ? colout->mark : colout->info ) + : dur < color_dur[4] + ? ( dur < color_dur[3] ? colout->hint : colout->warn ) + : ( dur < color_dur[5] ? colout->err : colout->bad ); + + return PrintCircBuf("%s%s%s",col,res,colout->reset); + } + + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_usage_dur + ( u_nsec_t total, u64 count, const u_nsec_t *color_dur ) +{ + return count + ? print_usage_dur_val( total/count, color_dur ) + : " -"; +} + +// +/////////////////////////////////////////////////////////////////////////////// + +void ListUsageCount +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + if (!uci) + return; + + if (uci->list_func) + { + uci->list_func(p_pm,uci,print_mode,limit_nsec); + return; + } + + if (!uci->uc) + { + if (uci->ud) + ListUsageDuration(p_pm,uci,print_mode,limit_nsec); + return; + } + + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + if ( limit_nsec <= 0 ) + limit_nsec = S_NSEC_MAX; + + UsageCount_t *uc = uci->uc; + fprintf(pm.fout,"%s%s%*s %s%s (%u record%s%s%s)%s%s", + pm.compact ? "" : pm.eol, pm.prefix, pm.indent, "", + pm.cout->caption, uci->title, + uc->par.used, uc->par.used == 1 ? "" : "s", + usage_count_enabled > 0 ? "" : ", global disabled", + uc->par.enabled ? "" : ", disabled", + pm.cout->reset, pm.eol ); + + if (!uc->par.used) + return; + + + //--- print table + + UsageCountEntry_t sum = {0}; + u_sec_t reftime = 0; + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + fprintf(pm.fout, + "%s%*s%s╔════════╦════════╤═════════╤══════════╦═══════════╤═══════════╗%s" + "%s%*s%s║ range ║ period │ counter │ count/s ║ top~1s /s │ top~5s /s ║%s" + "%s%*s%s╠════════╬════════╪═════════╪══════════╬═══════════╪═══════════╣%s%s" + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,colout->reset ,pm.eol + ); + + const IntervalInfo_t *interval = interval_info; + const UsageCountEntry_t *e = uc->entry; + for ( int i = 0; i < uc->par.used; i++, e++ ) + { + AddUsageCountEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_nsec >= limit_nsec; + if ( sum.elapsed_nsec < interval->nsec ) + { + is_last = i+1 == uc->par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_nsec < interval->nsec ) + name = "*"; + else + { + while ( sum.elapsed_nsec > interval->nsec ) + interval++; + name = interval[-1].name; + } + + fprintf(pm.fout, + "%s%*s%s║%s %-6.6s %s║%s %s %s│%s %s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s║%s" + ,print_usage_count_val(sum.top1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(sum.top5s,uci->color_val) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + + fprintf(pm.fout, + "%s ╚════════╩════════╧═════════╧══════════╩═══════════╧═══════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } + else + { + fprintf(pm.fout, + "%s%*s%s╔═══════════╦═════════════════════════════╦══════════╦══════════╦═════════════════════════════╗%s" + "%s%*s%s║ ║ Single Record ║ top 1s ║ top 5s ║ Summed Values ║%s" + "%s%*s%s║ real time ║ period │ counter │ count/s ║ count/s ║ count/s ║ period │ counter │ count/s ║%s" + "%s%*s%s╠═══════════╬════════╪═════════╪══════════╬══════════╬══════════╬════════╪═════════╪══════════╣%s%s" + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,pm.eol + ,pm.prefix ,pm.indent,"" ,colout->heading ,colout->reset ,pm.eol + ); + + const IntervalInfo_t *interval = interval_info+1; + const UsageCountEntry_t *e = uc->entry; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= uc->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #else + for ( int i = 0; i < uc->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #endif + { + AddUsageCountEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼──────────╫" + "──────────╫──────────╫" + "────────┼─────────┼──────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s%s %s║%s %s %s│%s %s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,e->elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,e->count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(e->count,e->elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s║%s %s %s║%s" + ,print_usage_count_val(e->top1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(e->top5s,uci->color_val) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s│%s %s %s║%s" + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ); + + if (pm.debug) + { + if ( pm.debug >= 2 && i >= USAGE_COUNT_FIRST_ADD ) + { + fprintf(pm.flog," %2u %s %s%4llu%%%s %s%5llu%%%s%s", + i, + PrintTimerNSec6(0,0,min,DC_SFORM_ALIGN|DC_SFORM_DASH), + e->elapsed_nsec < min ? "" + : e->elapsed_nsec < min*2 ? colout->hint : colout->warn, + 100 * e->elapsed_nsec / min, colout->reset, + sum.elapsed_nsec < min ? colout->info : "", + 100 * sum.elapsed_nsec / min, colout->reset, pm.eol ); + min = USAGE_COUNT_NEXT_MIN(min); + } + else + fprintf(pm.flog," %2u%s",i,pm.eol); + } + else + fputs(pm.eol,pm.fout); + } + + fprintf(pm.fout, + "%s ╚═══════════╩════════╧═════════╧══════════╩" + "══════════╩══════════╩" + "════════╧═════════╧══════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// + +void ListUsageDuration +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // NULL or object to list + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + if (!uci) + return; + + if (uci->list_func) + { + uci->list_func(p_pm,uci,print_mode,limit_nsec); + return; + } + + if (!uci->ud) + { + if (uci->uc) + ListUsageCount(p_pm,uci,print_mode,limit_nsec); + return; + } + + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + if ( limit_nsec <= 0 ) + limit_nsec = S_NSEC_MAX; + + UsageDuration_t *ud = uci->ud; + fprintf(pm.fout,"%s%s%*s %s%s (%u record%s%s%s)%s%s", + pm.compact ? "" : pm.eol, pm.prefix, pm.indent, "", + pm.cout->caption, uci->title, + ud->par.used, ud->par.used == 1 ? "" : "s", + usage_count_enabled > 0 ? "" : ", global disabled", + ud->par.enabled ? "" : ", disabled", + pm.cout->reset, pm.eol ); + + if (!ud->par.used) + return; + + + //--- print table + + UsageDurationEntry_t sum = {0}; + u_sec_t reftime = 0; + u_nsec_t min = USAGE_COUNT_NEXT_MIN(NSEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + printf( + "%s ╔════════╦═════════════════════════════════════╦═════════╤═════════╤════════╗%s" + "%s ║ ║ Summed Record ║ top 1s │ top 5s │ top ║%s" + "%s ║ range ║ period │ counter │ count/s │ Ø time ║ count/s │ count/s │ time ║%s" + "%s ╠════════╬════════╪═════════╪═════════╪════════╬═════════╪═════════╪════════╣%s%s" + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading ,colout->reset, pm.eol + ); + + const IntervalInfo_t *interval = interval_info; + const UsageDurationEntry_t *e = ud->entry; + for ( int i = 0; i < ud->par.used; i++, e++ ) + { + AddUsageDurationEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_nsec >= limit_nsec; + if ( sum.elapsed_nsec < interval->nsec ) + { + is_last = i+1 == ud->par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_nsec < interval->nsec ) + name = "*"; + else + { + while ( sum.elapsed_nsec > interval->nsec ) + interval++; + name = interval[-1].name; + } + + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼─────────┼────────╫" + "─────────┼────────╫─────────┼────────╫" + "────────┼─────────┼─────────┼────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s %-6.6s %s║%s %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(sum.total_nsec,sum.count,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + "%s %s│%s%s %s│%s %s %s║%s" + ,print_usage_count_val(sum.top_cnt_1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(sum.top_cnt_5s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur_val(sum.top_nsec,uci->color_dur) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + + fprintf(pm.fout, + "%s ╚════════╩════════╧═════════╧═════════╧════════╩" + "═════════╧═════════╧════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } + else + { + printf( + "%s ╔═══════════╦═════════════════════════════════════╦═════════╤═════════╤════════╦═════════════════════════════════════╗%s" + "%s ║ ║ Single Record ║ top 1s │ top 5s │ top ║ Summed Values ║%s" + "%s ║ real time ║ period │ counter │ count/s │ Ø time ║ count/s │ count/s │ time ║ period │ counter │ count/s │ Ø time ║%s" + "%s ╠═══════════╬════════╪═════════╪═════════╪════════╬═════════╪═════════╪════════╬════════╪═════════╪═════════╪════════╣%s%s" + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading, pm.eol + ,colout->heading ,colout->reset, pm.eol + ); + + const IntervalInfo_t *interval = interval_info+1; + const UsageDurationEntry_t *e = ud->entry; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= ud->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #else + for ( int i = 0; i < ud->par.used && sum.elapsed_nsec < limit_nsec; i++, e++ ) + #endif + { + AddUsageDurationEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_nsec >= interval->nsec || i == USAGE_COUNT_FIRST_ADD )) + { + fprintf(pm.fout, + "%s ╟───────────╫────────┼─────────┼─────────┼────────╫" + "─────────┼─────────┼────────╫" + "────────┼─────────┼─────────┼────────╢%s%s", + colout->heading, colout->reset, pm.eol ); + while ( sum.elapsed_nsec >= interval->nsec ) + interval++; + } + + fprintf(pm.fout, + "%s%*s%s║%s%s %s║%s %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,pm.prefix ,pm.indent,"" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerNSec6(0,0,e->elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,e->count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(e->count,e->elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(e->total_nsec,e->count,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + "%s %s│%s%s %s│%s %s %s║%s" + ,print_usage_count_val(e->top_cnt_1s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_count_val(e->top_cnt_5s,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur_val(e->top_nsec,uci->color_dur) + ,colout->heading, colout->reset + ); + + fprintf(pm.fout, + " %s %s│%s %s %s│%s%s %s│%s %s %s║%s" + ,PrintTimerNSec6(0,0,sum.elapsed_nsec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,PrintNumberU7(0,0,sum.count,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_usage_count(sum.count,sum.elapsed_nsec,uci->color_val) + ,colout->heading, colout->reset + ,print_usage_dur(sum.total_nsec,sum.count,uci->color_dur) + ,colout->heading, colout->reset + ); + + if (pm.debug) + { + if ( pm.debug >= 2 && i >= USAGE_COUNT_FIRST_ADD ) + { + fprintf(pm.flog," %2u %s %s%4llu%%%s %s%5llu%%%s%s", + i, + PrintTimerNSec6(0,0,min,DC_SFORM_ALIGN|DC_SFORM_DASH), + e->elapsed_nsec < min ? "" + : e->elapsed_nsec < min*2 ? colout->hint : colout->warn, + 100 * e->elapsed_nsec / min, colout->reset, + sum.elapsed_nsec < min ? colout->info : "", + 100 * sum.elapsed_nsec / min, colout->reset, pm.eol ); + min = USAGE_COUNT_NEXT_MIN(min); + } + else + fprintf(pm.flog," %2u%s",i,pm.eol); + } + else + fputs(pm.eol,pm.fout); + } + + fprintf(pm.fout, + "%s ╚═══════════╩════════╧═════════╧═════════╧════════╩" + "═════════╧═════════╧════════╩" + "════════╧═════════╧═════════╧════════╝%s%s" + ,colout->heading ,colout->reset ,pm.eol ); + } +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// cpu usage /////////////// +/////////////////////////////////////////////////////////////////////////////// + +CpuUsage_t cpu_usage = { .par.used=0, .par.enabled = CPU_USAGE_ENABLED }; + +const UsageCtrl_t cpu_usage_ctrl = +{ + .par = &cpu_usage.par, + .update_func = UpdateCpuUsageByUCI, + .list_func = ListCpuUsage, + .title = "CPU usage (extern)", + .key1 = "CPU", +}; + +/////////////////////////////////////////////////////////////////////////////// + +static ccp print_cpu_percent ( double val ) +{ + if (!val) + return " -"; + + //static const double collist[] = { 40, 60, 80, 95 }; + static const double collist[] = { 25, 50, 75, 95 }; + + if ( val >= collist[0] ) + { + ccp col = val < collist[2] + ? ( val < collist[1] ? colout->info : colout->hint ) + : ( val < collist[3] ? colout->warn : colout->err ); + return PrintCircBuf("%s%6.2f%s", col, val, colout->reset ); + } + + char *res = PrintCircBuf("%6.2f",val); + if ( val < 1.0 && res[2] == '0' ) + res[2] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_seconds ( u_usec_t val ) +{ + return !val + ? " -" + : val < USEC_PER_SEC + ? PrintCircBuf("%8llu",val) + : val < 10*USEC_PER_SEC + ? PrintCircBuf("%llu.%06llu",val/USEC_PER_SEC,val%USEC_PER_SEC) + : PrintCircBuf("%4llu.%03llu",val/USEC_PER_SEC,val%USEC_PER_SEC/1000); +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_wait_val ( double val ) +{ + static const double collist[] = { 100, 150, 200, 250, 300, 350 }; + if ( val > collist[0] ) + { + if ( val >= 999.0 ) + return PrintCircBuf(" %s%6.0f%s",colout->fatal,val,colout->reset); + + ccp col = val < collist[2] + ? ( val < collist[1] ? colout->mark : colout->info ) + : val < collist[4] + ? ( val < collist[3] ? colout->hint : colout->warn ) + : ( val < collist[5] ? colout->err : colout->bad ); + + return PrintCircBuf("%s%7.2f%s",col,val,colout->reset); + } + + if ( val <= 0 ) + return " -"; + + char * res = PrintCircBuf("%7.2f",val); + if ( val < 1.0 && res[3] == '0' ) + res[3] = ' '; + return res; +} + +//----------------------------------------------------------------------------- + +static ccp print_cpu_wait ( u32 n_wait, u_usec_t elapsed_usec ) +{ + return elapsed_usec + ? print_cpu_wait_val( (double)( USEC_PER_SEC * n_wait ) / elapsed_usec ) + : " ×"; +} + +//----------------------------------------------------------------------------- + +static void print_cpu_debug + ( int idx, u_usec_t *min, u_usec_t elapsed1, u_usec_t elapsed2 ) +{ + DASSERT(min); + + if ( idx >= CPU_USAGE_FIRST_ADD && *min ) + { + printf(" %2u %s %s%4llu%%%s %s%4llu%%%s\n", + idx, + PrintTimerUSec6(0,0,*min,DC_SFORM_ALIGN|DC_SFORM_DASH), + elapsed1 < *min ? "" : elapsed1 < *min*2 ? colout->hint : colout->warn, + 100 * elapsed1 / *min, colout->reset, + elapsed2 < *min ? colout->info : "", 100 * elapsed2 / *min, colout->reset ); + *min = CPU_USAGE_NEXT_MIN(*min); + } + else + printf(" %2u\n",idx); +} + +/////////////////////////////////////////////////////////////////////////////// + +void ListCpuUsage +( + PrintMode_t *p_pm, // NULL or print mode + const UsageCtrl_t *uci, // ignored! + int print_mode, // see LUC_PM_* + s_nsec_t limit_nsec // >0: limit output until # is reached +) +{ + //--- setup + + PrintMode_t pm = {0}; + if (p_pm) + pm = *p_pm; + SetupPrintMode(&pm); + + const u_usec_t limit_usec = limit_nsec > 0 ? limit_nsec/NSEC_PER_USEC : S_USEC_MAX; + + + //--- print header + + const ccp empty_line = pm.compact ? "" : "\n"; + printf("%s%s CPU Usage (%u record%s%s)%s\n", + empty_line, colout->caption, + cpu_usage.par.used, cpu_usage.par.used == 1 ? "" : "s", + cpu_usage.par.enabled ? "" : ", disabled", colout->reset ); + + if (!cpu_usage.par.used) + return; + + + //--- print table + + CpuUsageEntry_t sum = {0}; + u_sec_t reftime = 0; + u_usec_t min = CPU_USAGE_NEXT_MIN(USEC_PER_SEC); + + if ( print_mode == LUC_PM_SUMMARY ) + { + printf( + "%s ╔════════╦═════════════════════════════════════════╦════════════════╦════════════════╗\n" + "%s ║ ║ Summed Values ║ top 1s ║ top 5s ║\n" + "%s ║ range ║ period │ user%% │system%%│total%% │ wait/s ║total%% │ wait/s ║total%% │ wait/s ║\n" + "%s ╠════════╬════════╪═══════╪═══════╪═══════╪════════╬═══════╪════════╬═══════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info; + const CpuUsageEntry_t *e = cpu_usage.entry; + for ( int i = 0; i < cpu_usage.par.used; i++, e++ ) + { + AddCpuEntry(&sum,e); + bool is_last = false; + const bool is_limit = sum.elapsed_usec >= limit_usec; + if ( sum.elapsed_usec < interval->usec ) + { + is_last = i+1 == cpu_usage.par.used; + if ( !is_last && !is_limit ) + continue; + } + + ccp name; + if ( is_last || sum.elapsed_usec < interval->usec ) + name = "*"; + else + { + while ( sum.elapsed_usec > interval->usec ) + interval++; + name = interval[-1].name; + } + + const double elapsed = 100.0 / sum.elapsed_usec; + printf(" %s║%s %-6.6s %s║%s %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,name + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( sum.utime_usec + sum.stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + printf("%s %s│%s%s %s║%s%s %s│%s%s %s║%s" + ,print_cpu_percent(sum.top1s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(sum.topw1s) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.top5s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(sum.topw5s) + ,colout->heading, colout->reset + ); + + if (pm.debug) + fprintf(pm.flog," %2u%s",i,pm.eol); + else + fputs(pm.eol,pm.fout); + + if ( is_limit || !interval->name ) + break; + } + printf( + "%s ╚════════╩════════╧═══════╧═══════╧═══════╧════════╩" + "═══════╧════════╩═══════╧════════╝%s%s%s", + colout->heading ,colout->reset, pm.eol, empty_line ); + } + else if ( print_mode == LUC_PM_RAW ) + { + printf( + "%s ╔═══════════╦══════════════════════════════════════════════════╦══════════════════════════════════════════════════╗\n" + "%s ║ ║ Single Record (µs) ║ Summed Values (µs) ║\n" + "%s ║ real time ║ period │ user │ system │ total │ wait/s ║ period │ user │ system │ total │ wait/s ║\n" + "%s ╠═══════════╬════════╪══════════╪══════════╪══════════╪════════╬════════╪══════════╪══════════╪══════════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info+1; + for ( int i = 0; i < cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + { + const CpuUsageEntry_t *e = cpu_usage.entry+i; + AddCpuEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_usec >= interval->usec || i == CPU_USAGE_FIRST_ADD )) + { + printf( + "%s ╟───────────╫────────┼──────────┼──────────┼──────────┼────────╫" + "────────┼──────────┼──────────┼──────────┼────────╢%s\n", + colout->heading, colout->reset ); + while ( sum.elapsed_usec >= interval->usec ) + interval++; + } + + printf(" %s║%s%s %s║%s %s %s│%s %s %s│%s %s %s│%s %s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,e->elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_seconds(e->utime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds(e->stime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds( e->utime_usec + e->stime_usec ) + ,colout->heading, colout->reset + ,print_cpu_wait(e->n_wait,e->elapsed_usec) + ,colout->heading, colout->reset + ); + + printf(" %s %s│%s %s %s│%s %s %s│%s %s %s│%s%s %s║%s" + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_seconds(sum.utime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds(sum.stime_usec) + ,colout->heading, colout->reset + ,print_cpu_seconds( sum.utime_usec + sum.stime_usec ) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + if (pm.debug) + print_cpu_debug(i,&min,e->elapsed_usec,sum.elapsed_usec); + else + putchar('\n'); + } + + printf( + "%s ╚═══════════╩════════╧══════════╧══════════╧══════════╧════════╩" + "════════╧══════════╧══════════╧══════════╧════════╝%s\n%s", + colout->heading, colout->reset, empty_line ); + } + else + { + printf( + "%s ╔═══════════╦═════════════════════════════════════════╦════════════════╦════════════════╦═════════════════════════════════════════╗\n" + "%s ║ ║ Single Record ║ top 1s ║ top 5s ║ Summed Values ║\n" + "%s ║ real time ║ period │ user%% │system%%│total%% │ wait/s ║total%% │ wait/s ║total%% │ wait/s ║ period │ user%% │system%%│total%% │ wait/s ║\n" + "%s ╠═══════════╬════════╪═══════╪═══════╪═══════╪════════╬═══════╪════════╬═══════╪════════╬════════╪═══════╪═══════╪═══════╪════════╣%s\n" + ,colout->heading + ,colout->heading + ,colout->heading + ,colout->heading ,colout->reset + ); + + const IntervalInfo_t *interval = interval_info+1; + #if TEST_USAGE_COUNT + for ( int i = 0; i <= cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + #else + for ( int i = 0; i < cpu_usage.par.used && sum.elapsed_usec < limit_usec; i++ ) + #endif + { + const CpuUsageEntry_t *e = cpu_usage.entry+i; + AddCpuEntry(&sum,e); + if ( i > 0 && ( sum.elapsed_usec >= interval->usec || i == CPU_USAGE_FIRST_ADD )) + { + printf( + "%s ╟───────────╫────────┼───────┼───────┼───────┼────────╫" + "───────┼────────╫───────┼────────╫" + "────────┼───────┼───────┼───────┼────────╢%s\n", + colout->heading, colout->reset ); + while ( sum.elapsed_usec >= interval->usec ) + interval++; + } + + double elapsed = 100.0 / e->elapsed_usec; + printf(" %s║%s%s %s║%s %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,colout->heading, colout->reset + ,PrintRefTime(e->time_sec,&reftime) + ,colout->heading, colout->reset + ,PrintTimerUSec6(0,0,e->elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(e->utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(e->stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( e->utime_usec + e->stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(e->n_wait,e->elapsed_usec) + ,colout->heading, colout->reset + ); + + printf("%s %s│%s%s %s║%s%s %s│%s%s %s║%s" + ,print_cpu_percent(e->top1s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(e->topw1s) + ,colout->heading, colout->reset + ,print_cpu_percent(e->top5s) + ,colout->heading, colout->reset + ,print_cpu_wait_val(e->topw5s) + ,colout->heading, colout->reset + ); + + elapsed = 100.0 / sum.elapsed_usec; + printf(" %s %s│%s%s %s│%s%s %s│%s%s %s│%s%s %s║%s" + ,PrintTimerUSec6(0,0,sum.elapsed_usec,DC_SFORM_ALIGN|DC_SFORM_DASH) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.utime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(sum.stime_usec * elapsed) + ,colout->heading, colout->reset + ,print_cpu_percent(( sum.utime_usec + sum.stime_usec) * elapsed) + ,colout->heading, colout->reset + ,print_cpu_wait(sum.n_wait,sum.elapsed_usec) + ,colout->heading, colout->reset + ); + + if (pm.debug) + print_cpu_debug(i,&min,e->elapsed_usec,sum.elapsed_usec); + else + putchar('\n'); + } + + printf( + "%s ╚═══════════╩════════╧═══════╧═══════╧═══════╧════════╩" + "═══════╧════════╩═══════╧════════╩" + "════════╧═══════╧═══════╧═══════╧════════╝%s%s", + colout->heading, colout->reset, empty_line ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +const SaveRestoreTab_t SRT_CpuUsage[] = +{ + #undef SRT_NAME + #define SRT_NAME CpuUsage_t + + DEF_SRT_COMMENT("cpu_usage"), + DEF_SRT_SEPARATOR(), + + DEF_SRT_INT( par.enabled, "cpu-enabled" ), + DEF_SRT_UINT( par.used, "cpu-used" ), + DEF_SRT_ARRAY_A(entry), + DEF_SRT_UINT( entry[0].elapsed_usec, "cpu-elapsed" ), + DEF_SRT_UINT( entry[0].utime_usec, "cpu-utime" ), + DEF_SRT_UINT( entry[0].stime_usec, "cpu-stime" ), + DEF_SRT_UINT( entry[0].time_sec, "cpu-rtime" ), + DEF_SRT_FLOAT( entry[0].top1s, "cpu-top1s" ), + DEF_SRT_FLOAT( entry[0].top5s, "cpu-top5s" ), + DEF_SRT_UINT( entry[0].n_wait, "cpu-nwait" ), + DEF_SRT_FLOAT( entry[0].topw1s, "cpu-topw1s" ), + DEF_SRT_FLOAT( entry[0].topw5s, "cpu-topw5s" ), + DEF_SRT_ARRAY_END(), + + DEF_SRT_SEPARATOR(), + DEF_SRT_TERM() +}; + +//----------------------------------------------------------------------------- +// [[UpdateCpuUsage]] + +void UpdateCpuUsageByUCI ( const UsageCtrl_t *uci, u_nsec_t now_nsec ) +{ + UpdateCpuUsage(); +} + +//----------------------------------------------------------------------------- + +void UpdateCpuUsage() +{ + if (!cpu_usage.par.enabled) + { + cpu_usage.ref.elapsed_usec = 0; + cpu_usage.ref.n_wait = 0; + return; + } + cpu_usage.par.is_active = true; + + CpuUsageEntry_t cur = {0}; + cur.elapsed_usec = GetTimerUSec(); + + if ( cur.elapsed_usec >= cpu_usage.ref.elapsed_usec + USEC_PER_SEC ) + { + // needed for the case that CPU_USAGE_ENTRIES was reduced after restart! + if ( cpu_usage.par.used > CPU_USAGE_ENTRIES ) + cpu_usage.par.used = CPU_USAGE_ENTRIES; + + struct rusage ru; + if (!getrusage(RUSAGE_SELF,&ru)) + { + cur.time_sec = GetTimeSec(false); + cur.utime_usec = ru.ru_utime.tv_sec * USEC_PER_SEC + ru.ru_utime.tv_usec; + cur.stime_usec = ru.ru_stime.tv_sec * USEC_PER_SEC + ru.ru_stime.tv_usec; + + if (cpu_usage.ref.elapsed_usec) + { + //--- find index and move data + + int index; + if ( cpu_usage.par.used < CPU_USAGE_FIRST_ADD ) + index = cpu_usage.par.used + 1; + else + { + u_usec_t min = CPU_USAGE_NEXT_MIN(USEC_PER_SEC); + for ( index = CPU_USAGE_FIRST_ADD; + index < cpu_usage.par.used; + index++, min = CPU_USAGE_NEXT_MIN(min) + ) + { + CpuUsageEntry_t *e = cpu_usage.entry + index; + if ( e->elapsed_usec < min ) + { + AddCpuEntry(e,e-1); + index--; + break; + } + } + if ( ++index > CPU_USAGE_ENTRIES ) + index = CPU_USAGE_ENTRIES; + } + + if ( index > 1 ) + memmove( cpu_usage.entry+1, cpu_usage.entry, + (index-1) * sizeof(*cpu_usage.entry) ); + + if ( cpu_usage.par.used < index ) + cpu_usage.par.used = index; + + + //--- update first entry + + CpuUsageEntry_t *e = cpu_usage.entry; + *e = cpu_usage.ref; + e->elapsed_usec = cur.elapsed_usec - cpu_usage.ref.elapsed_usec; + e->utime_usec = cur.utime_usec - cpu_usage.ref.utime_usec; + e->stime_usec = cur.stime_usec - cpu_usage.ref.stime_usec; + + + //--- update top values (1s) + + if (e->elapsed_usec) + { + e->top1s = 100.0 * ( e->utime_usec + e->stime_usec ) / e->elapsed_usec; + e->topw1s = (double)( USEC_PER_SEC * e->n_wait ) / e->elapsed_usec; + } + + + //--- update top values (5s) + + u_usec_t elapsed5 = 0, total5 = 0, wait5 = 0; + CpuUsageEntry_t *e5 = cpu_usage.entry; + for ( int i = 0; i < cpu_usage.par.used && elapsed5 < 5*USEC_PER_SEC; i++, e5++ ) + { + elapsed5 += e5->elapsed_usec; + total5 += e5->utime_usec + e5->stime_usec; + wait5 += e5->n_wait; + } + + if (elapsed5) + { + if ( wait5 == e->n_wait && total5 == e->utime_usec + e->stime_usec ) + elapsed5 = 5*NSEC_PER_SEC; + e->top5s = 100.0 * total5 / elapsed5; + e->topw5s = (double)( USEC_PER_SEC * wait5 ) / elapsed5; + } + } + + cpu_usage.ref = cur; + } + } +} + +//----------------------------------------------------------------------------- + +void UpdateCpuUsageIncrement() +{ + UpdateCpuUsage(); + cpu_usage.ref.n_wait++; +} + +//----------------------------------------------------------------------------- + +void AddCpuEntry ( CpuUsageEntry_t *dest, const CpuUsageEntry_t *add ) +{ + DASSERT(dest); + DASSERT(add); + dest->elapsed_usec += add->elapsed_usec; + dest->utime_usec += add->utime_usec; + dest->stime_usec += add->stime_usec; + dest->n_wait += add->n_wait; + + if ( !dest->time_sec || dest->time_sec > add->time_sec ) + dest->time_sec = add->time_sec; + + if ( dest->top1s < add->top1s ) + dest->top1s = add->top1s; + if ( dest->top5s < add->top5s ) + dest->top5s = add->top5s; + + if ( dest->topw1s < add->topw1s ) + dest->topw1s = add->topw1s; + if ( dest->topw5s < add->topw5s ) + dest->topw5s = add->topw5s; +} + +//----------------------------------------------------------------------------- + +CpuUsageEntry_t GetCpuUsage ( s_usec_t usec ) +{ + CpuUsageEntry_t res = {0}; + for ( int i = 0; i < cpu_usage.par.used && usec > 0; i++ ) + { + const CpuUsageEntry_t *e = cpu_usage.entry + i; + usec -= e->elapsed_usec; + AddCpuEntry(&res,e); + } + return res; +} + +//----------------------------------------------------------------------------- + +uint GetCpuUsagePercent ( s_usec_t usec ) +{ + CpuUsageEntry_t e = GetCpuUsage(usec); + return GetCpuEntryPercent(&e); +} + +/////////////////////////////////////////////////////////////////////////////// + +double get_rusage_percent (void) +{ + static u_nsec_t prev_time = 0; + static u_usec_t prev_rusage = 0; + static double prev_percent = 0.0; + + const u_nsec_t cur_time = GetTimerNSec(); + if ( cur_time < prev_time + 10*NSEC_PER_MSEC ) + return prev_percent; + + u_usec_t cur_rusage = prev_rusage; + struct rusage ru; + if (!getrusage(RUSAGE_SELF,&ru)) + { + cur_rusage = ru.ru_utime.tv_sec * USEC_PER_SEC + ru.ru_utime.tv_usec + + ru.ru_stime.tv_sec * USEC_PER_SEC + ru.ru_stime.tv_usec; + } + + double percent = 0.0; + if ( prev_time && cur_time > prev_time ) + percent = (double)( 100 * NSEC_PER_USEC * ( cur_rusage - prev_rusage )) + / ( cur_time - prev_time ); + + prev_time = cur_time; + prev_rusage = cur_rusage; + + return prev_percent = percent; +} + +// +/////////////////////////////////////////////////////////////////////////////// /////////////// urandom support /////////////// /////////////////////////////////////////////////////////////////////////////// @@ -3718,7 +6177,7 @@ const u32 RANDOM32_C_ADD = 2 * 197731421; // 2 Primzahlen const u32 RANDOM32_COUNT_BASE = 4294967; // Primzahl == ~UINT_MAX32/1000; static int random32_a_index = -1; // Index in die a-Tabelle -static u32 random32_count = 1; // Abwärts-Zähler bis zum Wechsel von a,c +static u32 random32_count = 1; // Abwärts-Zähler bis zum Wechsel von a,c static u32 random32_a, random32_c, random32_X; // Die letzten Werte @@ -3750,7 +6209,7 @@ u32 MyRandom ( u32 max ) if (!--random32_count) { - // Neue Berechnung von random32_a und random32_c fällig + // Neue Berechnung von random32_a und random32_c fällig if ( random32_a_index < 0 ) { @@ -4295,8 +6754,23 @@ char * ScanS32 while ( *src > 0 && *src <= ' ' ) src++; } + #if 0 // 0b and 0d can be hex numbers => so this doeasn't work + uint base = default_base; + if ( src[0] == '0' ) + { + switch(tolower((int)src[1])) + { + case 'x': base = 16; break; + case 'd': base = 10; src+=2; break; + case 'o': base = 8; src+=2; break; + case 'b': base = 2; src+=2; break; + } + } + #else const uint base = src[0] == '0' && ( src[1] == 'x' || src[1] == 'X' ) ? 16 : default_base; + #endif + char *end; const s32 num = strtoul( src, &end, base ); if ( (ccp)end > src ) @@ -5734,14 +8208,27 @@ char * ScanSizeRange u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { + if (!opt) + opt = RAOPT__DEFAULT; + int count = 0; - double d1, d2; + double d1 = 0, d2; + + bool have_d1 = false; + if ( opt & RAOPT_ASSUME_0 ) + { + have_d1 = *source == ':' && opt & RAOPT_COLON + || *source == '#' && opt & RAOPT_HASH + || *source == ',' && opt & RAOPT_COMMA; + } - char *end = ScanSize(&d1,source,default_factor,default_factor_add,force_base); - if ( end == source ) + char *end = have_d1 ? (char*)source + : ScanSize(&d1,source,default_factor,default_factor_add,force_base); + if ( !have_d1 && end == source ) { d1 = d2 = 0.0; count = 0; @@ -5750,13 +8237,33 @@ char * ScanSizeRange { count++; d2 = d1; - if ( *end == '#' || *end == ':' ) + + const bool have_range = *end == ':' && opt & RAOPT_COLON + || *end == '-' && opt & RAOPT_MINUS; + const bool have_size = *end == '#' && opt & RAOPT_HASH + || *end == ',' && opt & RAOPT_COMMA; + + if ( have_range || have_size ) { + if ( d1 < 0 && max_value > 0 && opt & RAOPT_NEG ) + { + d1 += max_value; + if ( d1 < 0 ) + d1 = 0; + } + source = end; end = ScanSize(&d2,source+1,default_factor,default_factor_add,force_base); + if ( have_range && d2 < 0 && max_value > 0 && opt & RAOPT_NEG ) + { + d2 += max_value; + if ( d2 < 0 ) + d2 = 0; + } + if ( end == source+1 ) { - if ( *source == ':' && max_value > 0.0 ) + if ( have_range && max_value > 0.0 ) { count++; d2 = max_value; @@ -5772,11 +8279,14 @@ char * ScanSizeRange else { count++; - if ( *source == '#' ) + if ( have_size ) d2 += d1-1; } if ( d2 < d1 ) + { + count = 0; d2 = d1; + } } } @@ -5802,16 +8312,21 @@ char * ScanSizeRangeU32 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { double d1, d2; char * end = ScanSizeRange(stat,&d1,&d2,source, default_factor,default_factor_add,force_base, - max_value>0 ? max_value : U32_MAX ); + max_value>0 ? max_value : U32_MAX, opt ); if ( d1 < 0 || d1 > U32_MAX || d2 < 0 || d2 > U32_MAX ) + { end = (char*)source; + if (stat) + *stat = 0; + } else { if (num1) @@ -5826,6 +8341,46 @@ char * ScanSizeRangeU32 /////////////////////////////////////////////////////////////////////////////// +char * ScanSizeRangeS32 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s32 *num1, // not NULL: store 'from' result + s32 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : S32_MAX, opt ); + + if ( d1 < S32_MIN || d1 > S32_MAX || d2 < S32_MIN || d2 > U32_MAX ) + { + end = (char*)source; + if (stat) + *stat = 0; + } + else + { + if (num1) + *num1 = (s32)d1; + if (num2) + *num2 = (s32)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// + char * ScanSizeRangeU64 ( int *stat, // if not NULL: store result @@ -5837,16 +8392,21 @@ char * ScanSizeRangeU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { double d1, d2; char * end = ScanSizeRange(stat,&d1,&d2,source, default_factor,default_factor_add,force_base, - max_value>0 ? max_value : U64_MAX ); + max_value>0 ? max_value : U64_MAX, opt ); if ( d1 < 0 || d1 > U64_MAX || d2 < 0 || d2 > U64_MAX ) + { end = (char*)source; + if (stat) + *stat = 0; + } else { if (num1) @@ -5860,6 +8420,46 @@ char * ScanSizeRangeU64 } /////////////////////////////////////////////////////////////////////////////// + +char * ScanSizeRangeS64 +( + int *stat, // if not NULL: store result + // 0:none, 1:single, 2:range + s64 *num1, // not NULL: store 'from' result + s64 *num2, // not NULL: store 'to' result + + ccp source, // source text + u64 default_factor, // use this factor if number hasn't one + u64 default_factor_add, // use this factor for summands + int force_base, // if 1000|1024: force multiple of this + s64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT +) +{ + double d1, d2; + char * end = ScanSizeRange(stat,&d1,&d2,source, + default_factor,default_factor_add,force_base, + max_value>0 ? max_value : S64_MAX, opt ); + + if ( d1 < S64_MIN || d1 > S64_MAX || d2 < S64_MIN || d2 > U64_MAX ) + { + end = (char*)source; + if (stat) + *stat = 0; + } + else + { + if (num1) + *num1 = (s64)d1; + if (num2) + *num2 = (s64)d2; + } + + return end; + +} + +/////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// char * ScanSizeRangeList @@ -5873,7 +8473,8 @@ char * ScanSizeRangeList u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - double max_value // >0: max value for open ranges + double max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5888,7 +8489,8 @@ char * ScanSizeRangeList int stat; ptr = ScanSizeRange( &stat, num, num+1, ptr, - default_factor, default_factor_add, force_base, max_value ); + default_factor, default_factor_add, + force_base, max_value, opt ); if (!stat) break; num += 2; @@ -5929,7 +8531,8 @@ char * ScanSizeRangeListU32 u32 default_factor, // use this factor if number hasn't one u32 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u32 max_value // >0: max value for open ranges + u32 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5940,7 +8543,7 @@ char * ScanSizeRangeListU32 char *end = ScanSizeRangeList( n_range, d, max_range, source, default_factor, default_factor_add, - force_base, max_value ); + force_base, max_value, opt ); uint i; for ( i = 0; i < 2*max_range; i++ ) @@ -5964,7 +8567,8 @@ char * ScanSizeRangeListU64 u64 default_factor, // use this factor if number hasn't one u64 default_factor_add, // use this factor for summands int force_base, // if 1000|1024: force multiple of this - u64 max_value // >0: max value for open ranges + u64 max_value, // >0: max value for open ranges + range_opt_t opt // options, if 0 then use RAOPT__DEFAULT ) { DASSERT(num); @@ -5975,7 +8579,7 @@ char * ScanSizeRangeListU64 char *end = ScanSizeRangeList( n_range, d, max_range, source, default_factor, default_factor_add, - force_base, max_value ); + force_base, max_value, opt ); uint i; for ( i = 0; i < 2*max_range; i++ ) @@ -6021,7 +8625,7 @@ enumError ScanSizeOpt err = ERR_SYNTAX; if (print_err) ERROR0(ERR_SYNTAX, - "Illegal number for option --%s: %s\n", + "Invalid number for option --%s: %s\n", opt_name, source ); } else if ( min > 0 && d < min ) @@ -6219,6 +8823,7 @@ char * ScanSIFactor ) { DASSERT(source); + double i = 0.0; switch (*(uchar*)source++) { case 'y': default_factor = 1e-24; break; @@ -6227,26 +8832,27 @@ char * ScanSIFactor case 'f': default_factor = 1e-15; break; case 'p': default_factor = 1e-12; break; case 'n': default_factor = 1e-9; break; - case 0xb5: // µ as ANSI + case 0xb5: // µ as ASCII case 'u': default_factor = 1e-6; break; case 'm': default_factor = 1e-3; break; case 'c': default_factor = 1e-2; break; case 'd': default_factor = 1e-1; break; case 'h': default_factor = 1e2; break; - case 'k': default_factor = 1e3; break; - case 'M': default_factor = 1e6; break; - case 'G': default_factor = 1e9; break; - case 'T': default_factor = 1e12; break; - case 'P': default_factor = 1e15; break; - case 'E': default_factor = 1e18; break; - case 'Z': default_factor = 1e21; break; - case 'Y': default_factor = 1e24; break; + case 'k': + case 'K': default_factor = 1e3; i = 1024.0; break; + case 'M': default_factor = 1e6; i = 1048576.0; break; + case 'G': default_factor = 1e9; i = 1073741824.0; break; + case 'T': default_factor = 1e12; i = 1099511627776.0; break; + case 'P': default_factor = 1e15; i = 1125899906842624.0; break; + case 'E': default_factor = 1e18; i = 1152921504606846976.0; break; + case 'Z': default_factor = 1e21; i = 1180591620717411303424.0; break; + case 'Y': default_factor = 1e24; i = 1208925819614629174706176.0; break; case 0xc2: if ( (uchar)*source == 0xb5 ) { - // µ as UTF-8 + // µ as UTF-8 source++; default_factor = 1e-6; break; @@ -6258,6 +8864,13 @@ char * ScanSIFactor default: source--; } + + if ( *source == 'i' && i ) + { + source++; + default_factor = i; + } + if (num) *num = default_factor; return (char*)source; @@ -6270,13 +8883,13 @@ double GetDurationFactor ( char ch ) switch (ch) { case 's': return 1.0; break; - case 'm': return 60.0; break; - case 'h': return 3600.0; break; + case 'm': return SEC_PER_MIN; break; + case 'h': return SEC_PER_HOUR; break; case 't': - case 'd': return 86400.0; break; - case 'w': return 7*86400.0; break; + case 'd': return SEC_PER_DAY; break; + case 'w': return SEC_PER_WEEK; break; case 'j': - case 'y': return 365*86400.0; break; + case 'y': return SEC_PER_YEAR; break; default: return 0.0; } } @@ -6285,7 +8898,10 @@ double GetDurationFactor ( char ch ) char * ScanDuration ( - double *num, // not NULL: store result + // Returns a pointer to the the first not used character. + // On error, it is 'source' and '*seconds' is set to 0.0. + + double *seconds, // not NULL: store result (number of seconds) ccp source, // source text double default_factor, // default factor if no SI unit found ScanDuration_t mode @@ -6293,8 +8909,9 @@ char * ScanDuration { if (!source) return 0; + ccp src, done; + done = src = source; - ccp src = source; if ( mode & SDUMD_SKIP_BLANKS ) while ( *src == ' ' || *src == '\t' ) src++; @@ -6318,7 +8935,7 @@ char * ScanDuration } else sum += val; - src = end; + done = src = end; valid = true; if ( mode & SDUMD_ALLOW_SPACE_SEP ) @@ -6333,9 +8950,9 @@ char * ScanDuration } mode |= SDUMD_SKIP_BLANKS; } - if (num) - *num = sum; - return (char*)( valid ? src : source ); + if (seconds) + *seconds = sum; + return (char*)( valid ? done : source ); } @@ -6379,8 +8996,11 @@ char * ScanDuration double si_factor; end = ScanSIFactor(&si_factor,src,1.0); + char factor = *end; + if ( mode & SDUMD_ALLOW_CAPITALS ) + factor = tolower((int)factor); + double unit = GetDurationFactor(factor); - double unit = GetDurationFactor(*end); if (unit) { val *= unit * si_factor; @@ -6388,7 +9008,10 @@ char * ScanDuration } else { - unit = GetDurationFactor(*src); + char factor = *src; + if ( mode & SDUMD_ALLOW_CAPITALS ) + factor = tolower((int)factor); + unit = GetDurationFactor(factor); if (unit) { val *= unit; @@ -6401,24 +9024,199 @@ char * ScanDuration } } } - if (num) - *num = val; + if (seconds) + *seconds = val; return (char*)src; } - if (num) - *num = 0.0; + if (seconds) + *seconds = 0.0; return (char*)source; } +/////////////////////////////////////////////////////////////////////////////// + +double str2duration ( ccp src, char **endptr, double default_factor ) +{ + double dur = 0.0; + char *end = ScanDuration(&dur,src,default_factor,SDUMD_M_DEFAULT); + if (endptr) + *endptr = end; + return dur; +} + +// +/////////////////////////////////////////////////////////////////////////////// +/////////////// ScanIP4 /////////////// +/////////////////////////////////////////////////////////////////////////////// + +ipv4_class_t ScanIP4 +( + ipv4_t *ret_ip4, // not NULL: return numeric IPv4 here + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +) +{ + //--- param check + + if (!addr) + { + error: + if (ret_ip4) + *ret_ip4 = 0; + return CIP4_ERROR; + } + + if ( addr_len < 0 ) + addr_len = strlen(addr); + + ccp addr_end = addr + addr_len; + + + //--- skip leading blanks + + while ( addr < addr_end && isblank((int)*addr) ) + addr++; + + + //--- scan input + + int count = 0; + bool trailing_pt = false; + uint num[4] = {0,0,0,0}; + while ( addr < addr_end ) + { + if ( *addr < '0' || *addr > '9' ) + break; + + addr = ScanNumber ( num+count++, addr, addr_end, *addr == '0' ? 8 : 10, 3 ); + if ( count == 4 || *addr != '.' ) + { + trailing_pt = false; + break; + } + addr++; + trailing_pt = true; + } + + //printf("N=%u .=%u [%u,%u,%u,%u] addr=%s\n",count,trailing_pt,num[0],num[1],num[2],num[3],addr); + + if ( !count || addr < addr_end ) + goto error; + + + //--- calculate IPv4 based on class-X + + ipv4_class_t res = CIP4_INVALID; + u32 ipv4 = 0; + + switch ( trailing_pt ? 4 : count ) + { + case 1: + ipv4 = num[0]; + res = CIP4_NUMERIC; + break; + + case 2: + if ( num[0] <= 0xff && num[1] <= 0xffffff ) + { + ipv4 = num[0] << 24 | num[1]; + res = CIP4_CLASS_A; + } + break; + + case 3: + if ( num[0] <= 0xff && num[1] <= 0xff && num[2] <= 0xffff ) + { + ipv4 = num[0] << 24 | num[1] << 16 | num[2]; + res = CIP4_CLASS_B; + } + break; + + case 4: + if ( num[0] <= 0xff && num[1] <= 0xff && num[2] <= 0xff && num[3] <= 0xff ) + { + ipv4 = num[0] << 24 | num[1] << 16 | num[2] << 8 | num[3]; + res = CIP4_CLASS_C; + } + break; + } + + + //--- return status + + if (ret_ip4) + *ret_ip4 = ipv4; + + return res; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * NormalizeIP4 +( + // Return a pointer to dest if valid IPv4, + // Otherwise return 0 and buf is not modifieid. + + char *buf, // result buffer, can be 'addr' + // if NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + bool c_notation, // TRUE: force lass-C notation (a.b.c.d) + + ccp addr, // address to analyze + int addr_len // length of addr; if <0: use strlen(addr) +) +{ + ipv4_t ip4; + ipv4_class_t stat = ScanIP4(&ip4,addr,-1); + if ( stat <= CIP4_INVALID ) + return 0; + + return PrintIP4ByMode( buf, buf_size, ip4, c_notation ? CIP4_CLASS_C : stat, -1 ); +} + +/////////////////////////////////////////////////////////////////////////////// + +int dclib_aton ( ccp addr, struct in_addr *inp ) +{ + ipv4_t ip4; + ipv4_class_t stat = ScanIP4(&ip4,addr,-1); + if (inp) + inp->s_addr = htonl(ip4); + return stat > CIP4_INVALID; +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// PrintIP4*() /////////////// /////////////////////////////////////////////////////////////////////////////// +char * PrintIP4N +( + // print as unsigned number (CIP4_NUMERIC) + + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + s32 port // 0..0xffff: add ':port' +) +{ + if (!buf) + buf = GetCircBuf( buf_size = FW_IPV4_A_PORT+1 ); + + if ( port >= 0 && port <= 0xffff ) + snprintf(buf,buf_size,"%u:%u", ip4, port ); + else + snprintf(buf,buf_size,"%u",ip4); + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + char * PrintIP4A ( - // print in A-notation + // print in A-notation (CIP4_CLASS_A) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -6446,7 +9244,7 @@ char * PrintIP4A char * PrintIP4B ( - // print in A-notation + // print in B-notation (CIP4_CLASS_B) char *buf, // result buffer // NULL: use a local circulary static buffer @@ -6599,6 +9397,28 @@ char * PrintAlignedIP4 return buf; } +/////////////////////////////////////////////////////////////////////////////// + +char * PrintIP4ByMode +( + char *buf, // result buffer + // NULL: use a local circulary static buffer + size_t buf_size, // size of 'buf', ignored if buf==NULL + u32 ip4, // IP4 to print + ipv4_class_t mode, // CIP4_NUMERIC | CIP4_CLASS_A | CIP4_CLASS_B: + // force numeric or class-A/B output; otherwise class-C output + s32 port // 0..0xffff: add ':port' +) +{ + switch (mode) + { + case CIP4_NUMERIC: return PrintIP4N(buf,buf_size,ip4,port); + case CIP4_CLASS_A: return PrintIP4A(buf,buf_size,ip4,port); + case CIP4_CLASS_B: return PrintIP4B(buf,buf_size,ip4,port); + default: return PrintIP4 (buf,buf_size,ip4,port); + } +} + // /////////////////////////////////////////////////////////////////////////////// /////////////// CRC16 /////////////// @@ -6735,6 +9555,132 @@ char * ScanNiceID4 ( int * res, ccp arg ) return (char*)arg; }; +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNiceID5 ( uint id ) +{ + if (!id) + return "-----"; + + char *buf = GetCircBuf(6); + DASSERT(buf); + + buf[0] = 'A' + id / 26000 % 26; + buf[1] = 'A' + id / 1000 % 26; + buf[2] = '0' + id / 100 % 10; + buf[3] = '0' + id / 10 % 10; + buf[4] = '0' + id % 10; + buf[5] = 0; + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNiceID5 ( int * res, ccp arg ) +{ + DASSERT(res); + *res = -1; + + if (!arg) + return 0; + + char ch = tolower((int)arg[0]); + if ( ch >= 'a' && ch <= 'z' ) + { + int num = ch - 'a'; + ch = tolower((int)arg[1]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = arg[2]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[3]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[4]; + if ( ch >= '0' && ch <= '9' ) + { + *res = 10 * num + ch - '0'; + return (char*)arg+5; + } + } + } + } + } + return (char*)arg; +}; + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +ccp PrintNiceID6 ( uint id ) +{ + if (!id) + return "------"; + + char *buf = GetCircBuf(7); + DASSERT(buf); + + buf[0] = 'A' + id / 676000 % 26; + buf[1] = 'A' + id / 26000 % 26; + buf[2] = 'A' + id / 1000 % 26; + buf[3] = '0' + id / 100 % 10; + buf[4] = '0' + id / 10 % 10; + buf[5] = '0' + id % 10; + buf[6] = 0; + + return buf; +} + +/////////////////////////////////////////////////////////////////////////////// + +char * ScanNiceID6 ( int * res, ccp arg ) +{ + DASSERT(res); + *res = -1; + + if (!arg) + return 0; + + char ch = tolower((int)arg[0]); + if ( ch >= 'a' && ch <= 'z' ) + { + int num = ch - 'a'; + ch = tolower((int)arg[1]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = tolower((int)arg[2]); + if ( ch >= 'a' && ch <= 'z' ) + { + num = 26 * num + ch - 'a'; + ch = arg[3]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[4]; + if ( ch >= '0' && ch <= '9' ) + { + num = 10 * num + ch - '0'; + ch = arg[5]; + if ( ch >= '0' && ch <= '9' ) + { + *res = 10 * num + ch - '0'; + return (char*)arg+6; + } + } + } + } + } + } + return (char*)arg; +}; + // /////////////////////////////////////////////////////////////////////////////// /////////////// END /////////////// |
