//======================================================================= // Copyright XashXT Group 2011 © // stdlib.c - internal stdlib //======================================================================= #include #include "common.h" void (*_Q_memcpy)( void *dest, const void *src, size_t size, const char *filename, int fileline ); void (*_Q_memset)( void *dest, int set, size_t size, const char *filename, int fileline ); void Q_strnupr( const char *in, char *out, size_t size_out ) { if( size_out == 0 ) return; while( *in && size_out > 1 ) { if( *in >= 'a' && *in <= 'z' ) *out++ = *in++ + 'A' - 'a'; else *out++ = *in++; size_out--; } *out = '\0'; } void Q_strnlwr( const char *in, char *out, size_t size_out ) { if( size_out == 0 ) return; while( *in && size_out > 1 ) { if( *in >= 'A' && *in <= 'Z' ) *out++ = *in++ + 'a' - 'A'; else *out++ = *in++; size_out--; } *out = '\0'; } qboolean Q_isdigit( const char *str ) { if( str && *str ) { while( isdigit( *str )) str++; if( !*str ) return true; } return false; } int Q_strlen( const char *string ) { int len; const char *p; if( !string ) return 0; len = 0; p = string; while( *p ) { p++; len++; } return len; } char Q_toupper( const char in ) { char out; if( in >= 'a' && in <= 'z' ) out = in + 'A' - 'a'; else out = in; return out; } char Q_tolower( const char in ) { char out; if( in >= 'A' && in <= 'Z' ) out = in + 'a' - 'A'; else out = in; return out; } size_t Q_strncat( char *dst, const char *src, size_t size ) { register char *d = dst; register const char *s = src; register size_t n = size; size_t dlen; if( !dst || !src || !size ) return 0; // find the end of dst and adjust bytes left but don't go past end while( n-- != 0 && *d != '\0' ) d++; dlen = d - dst; n = size - dlen; if( n == 0 ) return( dlen + Q_strlen( s )); while( *s != '\0' ) { if( n != 1 ) { *d++ = *s; n--; } s++; } *d = '\0'; return( dlen + ( s - src )); // count does not include NULL } size_t Q_strncpy( char *dst, const char *src, size_t size ) { register char *d = dst; register const char *s = src; register size_t n = size; if( !dst || !src || !size ) return 0; // copy as many bytes as will fit if( n != 0 && --n != 0 ) { do { if(( *d++ = *s++ ) == 0 ) break; } while( --n != 0 ); } // not enough room in dst, add NULL and traverse rest of src if( n == 0 ) { if( size != 0 ) *d = '\0'; // NULL-terminate dst while( *s++ ); } return ( s - src - 1 ); // count does not include NULL } char *_copystring( byte *mempool, const char *s, const char *filename, int fileline ) { char *b; if( !s ) return NULL; if( !mempool ) mempool = host.mempool; b = _Mem_Alloc( mempool, Q_strlen( s ) + 1, filename, fileline ); Q_strcpy( b, s ); return b; } int Q_atoi( const char *str ) { int val = 0; int c, sign; if( !str ) return 0; // check for empty charachters in string while( *str == ' ' && str ) str++; if( *str == '-' ) { sign = -1; str++; } else sign = 1; // check for hex if( str[0] == '0' && ( str[1] == 'x' || str[1] == 'X' )) { str += 2; while( 1 ) { c = *str++; if( c >= '0' && c <= '9' ) val = (val<<4) + c - '0'; else if( c >= 'a' && c <= 'f' ) val = (val<<4) + c - 'a' + 10; else if( c >= 'A' && c <= 'F' ) val = (val<<4) + c - 'A' + 10; else return val * sign; } } // check for character if( str[0] == '\'' ) return sign * str[1]; // assume decimal while( 1 ) { c = *str++; if( c < '0' || c > '9' ) return val * sign; val = val * 10 + c - '0'; } return 0; } float Q_atof( const char *str ) { double val = 0; int c, sign, decimal, total; if( !str ) return 0.0f; // check for empty charachters in string while( *str == ' ' && str ) str++; if( *str == '-' ) { sign = -1; str++; } else sign = 1; // check for hex if( str[0] == '0' && ( str[1] == 'x' || str[1] == 'X' )) { str += 2; while( 1 ) { c = *str++; if( c >= '0' && c <= '9' ) val = (val * 16) + c - '0'; else if( c >= 'a' && c <= 'f' ) val = (val * 16) + c - 'a' + 10; else if( c >= 'A' && c <= 'F' ) val = (val * 16) + c - 'A' + 10; else return val * sign; } } // check for character if( str[0] == '\'' ) return sign * str[1]; // assume decimal decimal = -1; total = 0; while( 1 ) { c = *str++; if( c == '.' ) { decimal = total; continue; } if( c < '0' || c > '9' ) break; val = val * 10 + c - '0'; total++; } if( decimal == -1 ) return val * sign; while( total > decimal ) { val /= 10; total--; } return val * sign; } void Q_atov( float *vec, const char *str, size_t siz ) { string buffer; char *pstr, *pfront; int j; Q_strncpy( buffer, str, sizeof( buffer )); Q_memset( vec, 0, sizeof( vec_t ) * siz ); pstr = pfront = buffer; for( j = 0; j < siz; j++ ) { vec[j] = Q_atof( pfront ); // valid separator is space while( *pstr && *pstr != ' ' ) pstr++; if( !*pstr ) break; pstr++; pfront = pstr; } } char *Q_strchr( const char *s, char c ) { int len = Q_strlen( s ); while( len-- ) { if( *++s == c ) return(char *)s; } return 0; } char *Q_strrchr( const char *s, char c ) { int len = Q_strlen( s ); s += len; while( len-- ) { if( *--s == c ) return (char *)s; } return 0; } int Q_strnicmp( const char *s1, const char *s2, int n ) { int c1, c2; if( s1 == NULL ) { if( s2 == NULL ) return 0; else return -1; } else if( s2 == NULL ) return 1; do { c1 = *s1++; c2 = *s2++; if( !n-- ) return 0; // strings are equal until end point if( c1 != c2 ) { if( c1 >= 'a' && c1 <= 'z' ) c1 -= ('a' - 'A'); if( c2 >= 'a' && c2 <= 'z' ) c2 -= ('a' - 'A'); if( c1 != c2 ) return c1 < c2 ? -1 : 1; } } while( c1 ); // strings are equal return 0; } int Q_strncmp( const char *s1, const char *s2, int n ) { int c1, c2; if( s1 == NULL ) { if( s2 == NULL ) return 0; else return -1; } else if( s2 == NULL ) return 1; do { c1 = *s1++; c2 = *s2++; // strings are equal until end point if( !n-- ) return 0; if( c1 != c2 ) return c1 < c2 ? -1 : 1; } while( c1 ); // strings are equal return 0; } static qboolean Q_starcmp( const char *pattern, const char *text ) { char c, c1; const char *p = pattern, *t = text; while(( c = *p++ ) == '?' || c == '*' ) { if( c == '?' && *t++ == '\0' ) return false; } if( c == '\0' ) return true; for( c1 = (( c == '\\' ) ? *p : c ); ; ) { if( Q_tolower( *t ) == c1 && Q_stricmpext( p - 1, t )) return true; if( *t++ == '\0' ) return false; } } qboolean Q_stricmpext( const char *pattern, const char *text ) { char c; while(( c = *pattern++ ) != '\0' ) { switch( c ) { case '?': if( *text++ == '\0' ) return false; break; case '\\': if( Q_tolower( *pattern++ ) != Q_tolower( *text++ )) return false; break; case '*': return Q_starcmp( pattern, text ); default: if( Q_tolower( c ) != Q_tolower( *text++ )) return false; } } return ( *text == '\0' ); } const char* Q_timestamp( int format ) { static string timestamp; time_t crt_time; const struct tm *crt_tm; string timestring; time( &crt_time ); crt_tm = localtime( &crt_time ); switch( format ) { case TIME_FULL: // Build the full timestamp (ex: "Apr03 2007 [23:31.55]"); strftime( timestring, sizeof( timestring ), "%b%d %Y [%H:%M.%S]", crt_tm ); break; case TIME_DATE_ONLY: // Build the date stamp only (ex: "Apr03 2007"); strftime( timestring, sizeof( timestring ), "%b%d %Y", crt_tm ); break; case TIME_TIME_ONLY: // Build the time stamp only (ex: "23:31.55"); strftime( timestring, sizeof( timestring ), "%H:%M.%S", crt_tm ); break; case TIME_NO_SECONDS: // Build the time stamp exclude seconds (ex: "13:46"); strftime( timestring, sizeof( timestring ), "%H:%M", crt_tm ); break; case TIME_YEAR_ONLY: // Build the date stamp year only (ex: "2006"); strftime( timestring, sizeof( timestring ), "%Y", crt_tm ); break; case TIME_FILENAME: // Build a timestamp that can use for filename (ex: "Nov2006-26 (19.14.28)"); strftime( timestring, sizeof( timestring ), "%b%Y-%d_%H.%M.%S", crt_tm ); break; default: return NULL; } Q_strncpy( timestamp, timestring, sizeof( timestamp )); return timestamp; } char *Q_strstr( const char *string, const char *string2 ) { int c, len; if( !string || !string2 ) return NULL; c = *string2; len = Q_strlen( string2 ); while( string ) { for( ; *string && *string != c; string++ ); if( *string ) { if( !Q_strncmp( string, string2, len )) break; string++; } else return NULL; } return (char *)string; } char *Q_stristr( const char *string, const char *string2 ) { int c, len; if( !string || !string2 ) return NULL; c = Q_tolower( *string2 ); len = Q_strlen( string2 ); while( string ) { for( ; *string && Q_tolower( *string ) != c; string++ ); if( *string ) { if( !Q_strnicmp( string, string2, len )) break; string++; } else return NULL; } return (char *)string; } int Q_vsnprintf( char *buffer, size_t buffersize, const char *format, va_list args ) { size_t result; result = _vsnprintf( buffer, buffersize, format, args ); if( result < 0 || result >= buffersize ) { buffer[buffersize - 1] = '\0'; return -1; } return result; } int Q_snprintf( char *buffer, size_t buffersize, const char *format, ... ) { va_list args; int result; va_start( args, format ); result = Q_vsnprintf( buffer, buffersize, format, args ); va_end( args ); return result; } int Q_sprintf( char *buffer, const char *format, ... ) { va_list args; int result; va_start( args, format ); result = Q_vsnprintf( buffer, 99999, format, args ); va_end( args ); return result; } char *Q_pretifymem( float value, int digitsafterdecimal ) { static char output[8][32]; static int current; float onekb = 1024.0f; float onemb = onekb * onekb; char suffix[8]; char *out = output[current]; char val[32], *i, *o, *dot; int pos; current = ( current + 1 ) & ( 8 - 1 ); // first figure out which bin to use if( value > onemb ) { value /= onemb; Q_sprintf( suffix, " Mb" ); } else if( value > onekb ) { value /= onekb; Q_sprintf( suffix, " Kb" ); } else Q_sprintf( suffix, " bytes" ); // clamp to >= 0 digitsafterdecimal = max( digitsafterdecimal, 0 ); // if it's basically integral, don't do any decimals if( fabs( value - (int)value ) < 0.00001 ) { Q_sprintf( val, "%i%s", (int)value, suffix ); } else { char fmt[32]; // otherwise, create a format string for the decimals Q_sprintf( fmt, "%%.%if%s", digitsafterdecimal, suffix ); Q_sprintf( val, fmt, value ); } // copy from in to out i = val; o = out; // search for decimal or if it was integral, find the space after the raw number dot = Q_strstr( i, "." ); if( !dot ) dot = Q_strstr( i, " " ); pos = dot - i; // compute position of dot pos -= 3; // don't put a comma if it's <= 3 long while( *i ) { // if pos is still valid then insert a comma every third digit, except if we would be // putting one in the first spot if( pos >= 0 && !( pos % 3 )) { // never in first spot if( o != out ) *o++ = ','; } pos--; // count down comma position *o++ = *i++; // copy rest of data as normal } *o = 0; // terminate return out; } /* ============ va does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functions. ============ */ char *va( const char *format, ... ) { va_list argptr; static char string[256][1024], *s; static int stringindex = 0; s = string[stringindex]; stringindex = (stringindex + 1) & 255; va_start( argptr, format ); Q_vsnprintf( s, sizeof( string[0] ), format, argptr ); va_end( argptr ); return s; } // crt safe version void crt_memcpy( void *dest, const void *src, size_t count, const char *filename, int fileline ) { if( src == NULL || count <= 0 ) return; // nothing to copy if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline ); memcpy( dest, src, count ); } void mmx_memcpy8B( void *dest, const void *src, size_t count, const char *filename, int fileline ) { if( src == NULL || count <= 0 ) return; // nothing to copy if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline ); _asm { mov esi, src mov edi, dest mov ecx, count shr ecx, 3 // 8 bytes per iteration loop1: movq mm1, [ESI] // read in source data movntq [EDI], mm1 // non-temporal stores add esi, 8 add edi, 8 dec ecx jnz loop1 emms } } void mmx_memcpy64B( void *dest, const void *src, size_t count, const char *filename, int fileline ) { if( src == NULL || count <= 0 ) return; // nothing to copy if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline ); _asm { mov esi, src mov edi, dest mov ecx, count shr ecx, 6 // 64 bytes per iteration loop1: prefetchnta 64[ESI] // prefetch next loop, non-temporal prefetchnta 96[ESI] movq mm1, [ESI] // read in source data movq mm2, 8[ESI] movq mm3, 16[ESI] movq mm4, 24[ESI] movq mm5, 32[ESI] movq mm6, 40[ESI] movq mm7, 48[ESI] movq mm0, 56[ESI] movntq [EDI], mm1 // non-temporal stores movntq 8[EDI], mm2 movntq 16[EDI], mm3 movntq 24[EDI], mm4 movntq 32[EDI], mm5 movntq 40[EDI], mm6 movntq 48[EDI], mm7 movntq 56[EDI], mm0 add esi, 64 add edi, 64 dec ecx jnz loop1 emms } } void mmx_memcpy2kB( void *dest, const void *src, size_t count, const char *filename, int fileline ) { byte buf[2048]; byte *tbuf = &buf[0]; if( src == NULL || count <= 0 ) return; // nothing to copy if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline ); __asm { push ebx mov esi, src mov ebx, count shr ebx, 11 // 2048 bytes at a time mov edi, dest loop2k: push edi // copy 2k into temporary buffer mov edi, tbuf mov ecx, 32 loopMemToL1: prefetchnta 64[ESI] // prefetch next loop, non-temporal prefetchnta 96[ESI] movq mm1, [ESI] // read in source data movq mm2, 8[ESI] movq mm3, 16[ESI] movq mm4, 24[ESI] movq mm5, 32[ESI] movq mm6, 40[ESI] movq mm7, 48[ESI] movq mm0, 56[ESI] movq [EDI], mm1 // store into L1 movq 8[EDI], mm2 movq 16[EDI], mm3 movq 24[EDI], mm4 movq 32[EDI], mm5 movq 40[EDI], mm6 movq 48[EDI], mm7 movq 56[EDI], mm0 add esi, 64 add edi, 64 dec ecx jnz loopMemToL1 pop edi // now copy from L1 to system memory push esi mov esi, tbuf mov ecx, 32 loopL1ToMem: movq mm1, [ESI] // read in source data from L1 movq mm2, 8[ESI] movq mm3, 16[ESI] movq mm4, 24[ESI] movq mm5, 32[ESI] movq mm6, 40[ESI] movq mm7, 48[ESI] movq mm0, 56[ESI] movntq [EDI], mm1 // Non-temporal stores movntq 8[EDI], mm2 movntq 16[EDI], mm3 movntq 24[EDI], mm4 movntq 32[EDI], mm5 movntq 40[EDI], mm6 movntq 48[EDI], mm7 movntq 56[EDI], mm0 add esi, 64 add edi, 64 dec ecx jnz loopL1ToMem pop esi // do next 2k block dec ebx jnz loop2k pop ebx emms } } void mmx_memcpy( void *dest, const void *src, size_t size, const char *filename, int fileline ) { if( src == NULL || size <= 0 ) return; // nothing to copy if( dest == NULL ) Sys_Error( "memcpy: dest == NULL (called at %s:%i)\n", filename, fileline ); // if copying more than 16 bytes and we can copy 8 byte aligned if( size > 16 && !(((int)dest ^ (int)src) & 7 )) { byte *dest_p = (byte *)dest; byte *src_p = (byte *)src; int count = ((int)dest_p) & 7; // copy up to the first 8 byte aligned boundary crt_memcpy( dest_p, src_p, count, filename, fileline ); dest_p += count; src_p += count; count = size - count; // if there are multiple blocks of 2kB if( count & ~4095 ) { mmx_memcpy2kB( dest_p, src_p, count, filename, fileline ); src_p += (count & ~2047); dest_p += (count & ~2047); count &= 2047; } // if there are blocks of 64 bytes if( count & ~63 ) { mmx_memcpy64B( dest_p, src_p, count, filename, fileline ); src_p += (count & ~63); dest_p += (count & ~63); count &= 63; } // if there are blocks of 8 bytes if( count & ~7 ) { mmx_memcpy8B( dest_p, src_p, count, filename, fileline ); src_p += (count & ~7); dest_p += (count & ~7); count &= 7; } // copy any remaining bytes crt_memcpy( dest_p, src_p, count, filename, fileline ); } else { // use the regular one if we cannot copy 8 byte aligned crt_memcpy( dest, src, size, filename, fileline ); } } void crt_memset( void *dest, int set, size_t count, const char *filename, int fileline ) { if( dest == NULL ) Sys_Error( "memset: dest == NULL (called at %s:%i)\n", filename, fileline ); memset( dest, set, count ); } void mmx_memset( void *dest, int set, size_t size, const char *filename, int fileline ) { union { byte bytes[8]; WORD words[4]; DWORD dwords[2]; } dat; byte *dst = (byte *)dest; int count = size; if( dest == NULL ) Sys_Error( "memset: dest == NULL (called at %s:%i)\n", filename, fileline ); while( count > 0 && (((int)dst) & 7) ) { *dst = set; dst++; count--; } if( !count ) return; dat.bytes[0] = set; dat.bytes[1] = set; dat.words[1] = dat.words[0]; dat.dwords[1] = dat.dwords[0]; if( count >= 64 ) { __asm { mov edi, dst mov ecx, count shr ecx, 6 // 64 bytes per iteration movq mm1, dat // Read in source data movq mm2, mm1 movq mm3, mm1 movq mm4, mm1 movq mm5, mm1 movq mm6, mm1 movq mm7, mm1 movq mm0, mm1 loop1: movntq 0[EDI], mm1 // Non-temporal stores movntq 8[EDI], mm2 movntq 16[EDI], mm3 movntq 24[EDI], mm4 movntq 32[EDI], mm5 movntq 40[EDI], mm6 movntq 48[EDI], mm7 movntq 56[EDI], mm0 add edi, 64 dec ecx jnz loop1 } dst += ( count & ~63 ); count &= 63; } if ( count >= 8 ) { __asm { mov edi, dst mov ecx, count shr ecx, 3 // 8 bytes per iteration movq mm1, dat // Read in source data loop2: movntq 0[EDI], mm1 // Non-temporal stores add edi, 8 dec ecx jnz loop2 } dst += (count & ~7); count &= 7; } while( count > 0 ) { *dst = set; dst++; count--; } __asm { emms } } void CRT_Init( void ) { if( Sys_CheckMMX( )) { // use fast MMX version _Q_memcpy = mmx_memcpy; _Q_memset = mmx_memset; } else { // default crt version _Q_memcpy = crt_memcpy; _Q_memset = crt_memset; } Memory_Init(); }