Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
/*+**************************************************************************/
/*** ***/
/*** This file is distributed under a BSD license. ***/
/*** See LICENSE.txt for details. ***/
/*** ***/
/**************************************************************************+*/
/****************************************************************************/
/*** ***/
/*** (C) 2005 Dierk Ohlerich, all rights reserved ***/
/*** ***/
/****************************************************************************/
#include "base/types.hpp"
#include "base/types2.hpp"
#include "base/system.hpp"
#include "base/graphics.hpp"
#if sPLATFORM==sPLAT_WINDOWS && sCONFIG_64BIT
// for _controlfp87
#include <float.h>
#endif
/****************************************************************************/
#define sCFG_MEMDBG_LOCKING 0 // enable for sMemDbgLock/sMemDbgUnlock debugging
/****************************************************************************/
/* this is most likely never used
#if sCONFIG_PLATFORM_LINUX
void __debugbreak() {};
#endif
*/
static sChar emptyString[] = L"";
static sInt sLogPrefixLength = 8; // length of sLogF and sLog prefix indentation "[???] ..."
/****************************************************************************/
/*** ***/
/*** Basic Types and Functions ***/
/*** ***/
/****************************************************************************/
sInt sFindLowerPower(sInt x)
{
sInt y = 0;
while((2<<y)<=x) y++;
return y;
};
sInt sFindHigherPower(sInt x)
{
sInt y = 0;
while((1<<y)<x) y++;
return y;
};
sU32 sMakeMask(sU32 max)
{
sU32 mask = max;
mask |= mask>>1;
mask |= mask>>2;
mask |= mask>>4;
mask |= mask>>8;
mask |= mask>>16;
return mask;
}
sU64 sMakeMask(sU64 max)
{
sU64 mask = max;
mask |= mask>>1;
mask |= mask>>2;
mask |= mask>>4;
mask |= mask>>8;
mask |= mask>>16;
mask |= mask>>32;
return mask;
}
sU32 sPart1By1(sU32 x) // "inserts" a 0 bit between each of the low 16 bits of x
{
// x = ---- ---- ---- ---- fedc ba98 7654 3210 (bits)
x = (x ^ (x << 8)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
x = (x ^ (x << 4)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
x = (x ^ (x << 2)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
x = (x ^ (x << 1)) & 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
return x;
}
sU32 sPart1By2(sU32 x) // "inserts" two 0 bits between each of the low 10 bits of x
{
// x = ---- ---- ---- ---- ---- --98 7654 3210 (bits)
x = (x ^ (x << 16)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
x = (x ^ (x << 8)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
x = (x ^ (x << 4)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
x = (x ^ (x << 2)) & 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0
return x;
}
sU32 sCompact1By1(sU32 x) // inverse of sPart1By1 - "delete" all odd-indexed bits
{
x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0 (bits)
x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
return x;
}
sU32 sCompact1By2(sU32 x) // inverse of sPart1By2 - "delete" two bits after every bit
{
x &= 0x09249249; // x = ---- 9--8 --7- -6-- 5--4 --3- -2-- 1--0 (bits)
x = (x ^ (x >> 2)) & 0x030c30c3; // x = ---- --98 ---- 76-- --54 ---- 32-- --10
x = (x ^ (x >> 4)) & 0x0300f00f; // x = ---- --98 ---- ---- 7654 ---- ---- 3210
x = (x ^ (x >> 8)) & 0xff0000ff; // x = ---- --98 ---- ---- ---- ---- 7654 3210
x = (x ^ (x >> 16)) & 0x000003ff; // x = ---- ---- ---- ---- ---- --98 7654 3210
return x;
}
/****************************************************************************/
sU64 sHashStringFNV(const sChar *text)
{
sU64 hash = 0x84222325cbf29ce4ULL;
const sU64 prime = 0x00000100000001b3ULL;
while(*text)
{
sU16 tmp = (sU16) *text++;
hash ^= (tmp >> 0) & 0xff;
hash *= prime;
hash ^= (tmp >> 8) & 0xff;
hash *= prime;
}
return hash;
}
static const sInt Adler32_Base = 65521;
static sU32 Adler32_s1=1;
static sU32 Adler32_s2=0;
sU32 sChecksumAdler32(const sU8 *data,sInt size)
{
// this can be optimised, see wikipedia...
sU32 s1 = 1;
sU32 s2 = 0;
const sU8 *end = data+size;
while(data<end)
{
s1 = (s1+(*data++)) % Adler32_Base;
s2 = (s2+s1) % Adler32_Base;
}
return (s2<<16) + s1;
}
void sChecksumAdler32Begin()
{
Adler32_s1 = 1;
Adler32_s2 = 0;
}
void sChecksumAdler32Add(const sU8 *data,sInt size)
{
const sU8 *end = data+size;
while(data<end)
{
Adler32_s1 = (Adler32_s1+(*data++)) % Adler32_Base;
Adler32_s2 = (Adler32_s2+Adler32_s1) % Adler32_Base;
}
}
sU32 sChecksumAdler32End()
{
return (Adler32_s2<<16) + Adler32_s1;
}
/****************************************************************************/
static sU32 sCRCTable[256] =
{
0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL
};
sU32 sChecksumCRC32(const sU8 *data,sInt size)
{
sU32 crc = 0xffffffff;
const sU8 *end = data+size;
while(data<end)
crc = sCRCTable[(crc ^ *data++) & 0xff] ^ (crc>>8);
return crc ^ 0xfffffffe;
}
sU32 sChecksumRandomByte(const sU8 *data,sInt size)
{
sU32 csum = 0;
const sU8 *end = data+size;
while(data<end)
csum = sCRCTable[*data++] ^ csum;
return csum;
}
sU32 sChecksumMurMur(const sU32 *data,sInt words)
{
const sU32 m = 0x5bd1e995;
sU32 h = m;
sU32 k;
for(sInt i=0;i<words;i++)
{
k = data[i];
k *= m;
k ^= k>>24;
k *= m;
h *= m;
h ^= k;
}
return h;
}
/****************************************************************************/
void sChecksumMD5::Block(const sU8 *data)
{
sU32 a = Hash[0];
sU32 b = Hash[1];
sU32 c = Hash[2];
sU32 d = Hash[3];
f( a, b, c, d, 0, 7, 0xd76aa478,(sU32 *)data );
f( d, a, b, c, 1, 12, 0xe8c7b756,(sU32 *)data );
f( c, d, a, b, 2, 17, 0x242070db,(sU32 *)data );
f( b, c, d, a, 3, 22, 0xc1bdceee,(sU32 *)data );
f( a, b, c, d, 4, 7, 0xf57c0faf,(sU32 *)data );
f( d, a, b, c, 5, 12, 0x4787c62a,(sU32 *)data );
f( c, d, a, b, 6, 17, 0xa8304613,(sU32 *)data );
f( b, c, d, a, 7, 22, 0xfd469501,(sU32 *)data );
f( a, b, c, d, 8, 7, 0x698098d8,(sU32 *)data );
f( d, a, b, c, 9, 12, 0x8b44f7af,(sU32 *)data );
f( c, d, a, b, 10, 17, 0xffff5bb1,(sU32 *)data );
f( b, c, d, a, 11, 22, 0x895cd7be,(sU32 *)data );
f( a, b, c, d, 12, 7, 0x6b901122,(sU32 *)data );
f( d, a, b, c, 13, 12, 0xfd987193,(sU32 *)data );
f( c, d, a, b, 14, 17, 0xa679438e,(sU32 *)data );
f( b, c, d, a, 15, 22, 0x49b40821,(sU32 *)data );
g( a, b, c, d, 1, 5, 0xf61e2562,(sU32 *)data );
g( d, a, b, c, 6, 9, 0xc040b340,(sU32 *)data );
g( c, d, a, b, 11, 14, 0x265e5a51,(sU32 *)data );
g( b, c, d, a, 0, 20, 0xe9b6c7aa,(sU32 *)data );
g( a, b, c, d, 5, 5, 0xd62f105d,(sU32 *)data );
g( d, a, b, c, 10, 9, 0x02441453,(sU32 *)data );
g( c, d, a, b, 15, 14, 0xd8a1e681,(sU32 *)data );
g( b, c, d, a, 4, 20, 0xe7d3fbc8,(sU32 *)data );
g( a, b, c, d, 9, 5, 0x21e1cde6,(sU32 *)data );
g( d, a, b, c, 14, 9, 0xc33707d6,(sU32 *)data );
g( c, d, a, b, 3, 14, 0xf4d50d87,(sU32 *)data );
g( b, c, d, a, 8, 20, 0x455a14ed,(sU32 *)data );
g( a, b, c, d, 13, 5, 0xa9e3e905,(sU32 *)data );
g( d, a, b, c, 2, 9, 0xfcefa3f8,(sU32 *)data );
g( c, d, a, b, 7, 14, 0x676f02d9,(sU32 *)data );
g( b, c, d, a, 12, 20, 0x8d2a4c8a,(sU32 *)data );
h( a, b, c, d, 5, 4, 0xfffa3942,(sU32 *)data );
h( d, a, b, c, 8, 11, 0x8771f681,(sU32 *)data );
h( c, d, a, b, 11, 16, 0x6d9d6122,(sU32 *)data );
h( b, c, d, a, 14, 23, 0xfde5380c,(sU32 *)data );
h( a, b, c, d, 1, 4, 0xa4beea44,(sU32 *)data );
h( d, a, b, c, 4, 11, 0x4bdecfa9,(sU32 *)data );
h( c, d, a, b, 7, 16, 0xf6bb4b60,(sU32 *)data );
h( b, c, d, a, 10, 23, 0xbebfbc70,(sU32 *)data );
h( a, b, c, d, 13, 4, 0x289b7ec6,(sU32 *)data );
h( d, a, b, c, 0, 11, 0xeaa127fa,(sU32 *)data );
h( c, d, a, b, 3, 16, 0xd4ef3085,(sU32 *)data );
h( b, c, d, a, 6, 23, 0x04881d05,(sU32 *)data );
h( a, b, c, d, 9, 4, 0xd9d4d039,(sU32 *)data );
h( d, a, b, c, 12, 11, 0xe6db99e5,(sU32 *)data );
h( c, d, a, b, 15, 16, 0x1fa27cf8,(sU32 *)data );
h( b, c, d, a, 2, 23, 0xc4ac5665,(sU32 *)data );
i( a, b, c, d, 0, 6, 0xf4292244,(sU32 *)data );
i( d, a, b, c, 7, 10, 0x432aff97,(sU32 *)data );
i( c, d, a, b, 14, 15, 0xab9423a7,(sU32 *)data );
i( b, c, d, a, 5, 21, 0xfc93a039,(sU32 *)data );
i( a, b, c, d, 12, 6, 0x655b59c3,(sU32 *)data );
i( d, a, b, c, 3, 10, 0x8f0ccc92,(sU32 *)data );
i( c, d, a, b, 10, 15, 0xffeff47d,(sU32 *)data );
i( b, c, d, a, 1, 21, 0x85845dd1,(sU32 *)data );
i( a, b, c, d, 8, 6, 0x6fa87e4f,(sU32 *)data );
i( d, a, b, c, 15, 10, 0xfe2ce6e0,(sU32 *)data );
i( c, d, a, b, 6, 15, 0xa3014314,(sU32 *)data );
i( b, c, d, a, 13, 21, 0x4e0811a1,(sU32 *)data );
i( a, b, c, d, 4, 6, 0xf7537e82,(sU32 *)data );
i( d, a, b, c, 11, 10, 0xbd3af235,(sU32 *)data );
i( c, d, a, b, 2, 15, 0x2ad7d2bb,(sU32 *)data );
i( b, c, d, a, 9, 21, 0xeb86d391,(sU32 *)data );
Hash[0] += a;
Hash[1] += b;
Hash[2] += c;
Hash[3] += d;
}
void sChecksumMD5::CalcBegin()
{
Hash[0] = 0x67452301;
Hash[1] = 0xEFCDAB89;
Hash[2] = 0x98BADCFE;
Hash[3] = 0x10325476;
}
sInt sChecksumMD5::CalcAdd(const sU8 *data,sInt size)
{
const sU8* ptr = data;
sInt left = size;
while(left>=64)
{
Block(ptr);
ptr+=64;
left-=64;
}
return ptr-data;
}
void sChecksumMD5::CalcEnd(const sU8 *data, sInt size, sInt sizeall)
{
sInt done = CalcAdd(data,size);
data += done;
size -= done;
sU8 buffer[64];
sClear(buffer);
sCopyMem(buffer,data,size);
buffer[size++] = 0x80;
if(size>56)
{
Block(buffer);
sClear(buffer);
}
*((sU64 *)(buffer+56)) = sizeall*8;
Block(buffer);
// correct md5 swapping
sSwapEndianI(Hash[0]);
sSwapEndianI(Hash[1]);
sSwapEndianI(Hash[2]);
sSwapEndianI(Hash[3]);
}
void sChecksumMD5::Calc(const sU8 *data,sInt size)
{
Hash[0] = 0x67452301;
Hash[1] = 0xEFCDAB89;
Hash[2] = 0x98BADCFE;
Hash[3] = 0x10325476;
sU8 buffer[64];
sInt left = size;
while(left>=64)
{
Block(data);
data+=64;
left-=64;
}
sClear(buffer);
sCopyMem(buffer,data,left);
buffer[left++] = 0x80;
if(left>56)
{
Block(buffer);
left = 0;
sClear(buffer);
}
*((sU64 *)(buffer+56)) = size*8;
Block(buffer);
// correct md5 swapping
sSwapEndianI(Hash[0]);
sSwapEndianI(Hash[1]);
sSwapEndianI(Hash[2]);
sSwapEndianI(Hash[3]);
}
sBool sChecksumMD5::Check(const sU8 *data,sInt size)
{
sChecksumMD5 c;
c.Calc(data,size);
return c==*this;
}
sBool sChecksumMD5::operator== (const sChecksumMD5 &o)const
{
if(o.Hash[0]!=Hash[0]) return 0;
if(o.Hash[1]!=Hash[1]) return 0;
if(o.Hash[2]!=Hash[2]) return 0;
if(o.Hash[3]!=Hash[3]) return 0;
return 1;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f, const sChecksumMD5 &md5)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
info.Null = 1;
if(info.Format != 'x' && info.Format != 'X')
info.Format = 'x';
f.PrintInt<sU32>(info,md5.Hash[0],0);
f.PrintInt<sU32>(info,md5.Hash[1],0);
f.PrintInt<sU32>(info,md5.Hash[2],0);
f.PrintInt<sU32>(info,md5.Hash[3],0);
f.Fill();
return f;
}
/****************************************************************************/
/*** ***/
/*** Assembler ***/
/*** ***/
/****************************************************************************/
#if sINTRO
#if !sCONFIG_ASM_MSC_IA32 || !sCONFIG_ASM_87
sCOMPILEERROR("need msc inline assembly");
#endif
sF64 sFMod(sF64 a,sF64 b)
{
__asm
{
fld qword ptr [b];
fld qword ptr [a];
fprem;
fstp st(1);
fstp qword ptr [a];
}
return a;
}
sF64 sFPow(sF64 a,sF64 b)
{
__asm
{
fld qword ptr [a];
fld qword ptr [b];
fxch st(1);
ftst;
fstsw ax;
sahf;
jz zero;
fyl2x;
fld1;
fld st(1);
fprem;
f2xm1;
faddp st(1), st;
fscale;
zero:
fstp st(1);
fstp qword ptr [a];
}
return a;
}
sF64 sFExp(sF64 f)
{
__asm
{
fld qword ptr [f];
fldl2e;
fmulp st(1), st;
fld1;
fld st(1);
fprem;
f2xm1;
faddp st(1), st;
fscale;
fstp st(1);
fstp qword ptr [f];
}
return f;
}
#endif //sPC_64
/****************************************************************************/
/*** ***/
/*** Float / Ascii Conversion ***/
/*** ***/
/****************************************************************************/
void sFloatInfo::FloatToAscii(sU32 fu,sInt digits)
{
sInt e2 = ((fu&0x7f800000)>>23);
sU32 man = fu & 0x007fffff;
sInt e10 = 0;
const sInt shift=28;
Digits[0] = 0;
Negative = (fu&0x80000000) ? 1 : 0;
NaN = 0;
Exponent = 0;
Infinite = 0;
Denormal = 0;
if(e2==0)
{
if(man==0)
{
for(sInt i=0;i<digits;i++)
Digits[i]='0';
Digits[digits] = 0;
return;
}
Denormal = 1;
}
else if(e2==255)
{
if(man==0)
Infinite = 1;
else
NaN = man;
return;
}
if(!Denormal) man |= 0x00800000;
man = man<<(shift-23);
man += 1UL<<(shift-24);
e2 = e2 - 127;
// keep mantissa from 0x10000000 .. 0x9fffffff
// <1
while(e2<=-10)
{
man = sMulShiftU(man,1000*(1<<6)); // x*1000/1024 = x*1000*(65536/1024)/65536
e10 -= 3;
e2 += 10;
if(man<(1ULL<<(shift)))
{
man = man*10;
e10--;
}
}
while(e2<=-1)
{
if(man<(1ULL<<(shift+1)))
{
man *= 5;
e10--;
}
else
{
man = man>>1;
}
e2++;
}
// >1
while(e2>=10)
{
man = sMulDivU(man,1024,1000);
e10+=3;
e2-=10;
if(man>=(10UL<<shift))
{
man = man/10;
e10++;
}
}
while(e2>=1)
{
if(man>=(5UL<<shift))
{
man = man/5;
e10++;
}
else
{
man = man<<1;
}
e2--;
}
// output digits
//man += 1UL<<(shift-25);
for(sInt i=0;i<digits;i++)
{
Digits[i]= '0'+(man>>shift);
man = (man&((1UL<<shift)-1));
man = (man<<3) + (man<<1);
}
// done
Digits[digits] = 0;
Exponent = e10;
}
void sFloatInfo::DoubleToAscii(sU64 fu,sInt digits)
{
sInt e2 = ((fu&0x7ff0000000000000ULL)>>52);
sU64 man = fu &0x000fffffffffffffULL;
sInt e10 = 0;
const sInt shift=60;
Digits[0] = 0;
Negative = (fu&0x8000000000000000ULL) ? 1 : 0;
NaN = 0;
Exponent = 0;
Infinite = 0;
Denormal = 0;
if(e2==0)
{
if(man==0)
{
for(sInt i=0;i<digits;i++)
Digits[i]='0';
Digits[digits] = 0;
return;
}
Denormal = 1;
}
else if(e2==2047)
{
if(man==0)
Infinite = 1;
else
NaN = man;
return;
}
if(!Denormal) man |= 0x0010000000000000ULL;
man = man<<(shift-52);
man += 1ULL<<(shift-53);
e2 = e2 - 1023;
// keep mantissa from 0x10000000 .. 0x9fffffff
// <1
while(e2<=-1)
{
if(man<(1ULL<<(shift+1)))
{
man *= 5;
e10--;
}
else
{
man = man>>1;
}
e2++;
}
// >1
while(e2>=1)
{
if(man>=(5ULL<<shift))
{
man = man/5;
e10++;
}
else
{
man = man<<1;
}
e2--;
}
// output digits
//man += 1UL<<(shift-54);
for(sInt i=0;i<digits;i++)
{
Digits[i]= '0'+(man>>shift);
man = (man&((1ULL<<shift)-1));
man = man*10;
}
// done
Digits[digits] = 0;
Exponent = e10;
}
sU32 sFloatInfo::AsciiToFloat()
{
sU64 man;
sInt e10;
sInt e2;
const sInt shift=60;
const sU64 highmask = ~((1ULL<<shift)-1);
const sU64 highmask1 = ~((1ULL<<(shift-1))-1);
const sU64 highmask0 = ~((1ULL<<(shift+1))-1);
sU32 fu;
// simple cases
fu = Negative ? 0x80000000 : 0x00000000;
if(NaN)
{
fu |= 0x7f800000;
fu |= 0x007fffff & NaN;
goto ende;
}
if(Infinite)
{
fu |= 0x7f800000;
goto ende;
}
// scan digits
man = 0;
e10 = Exponent+1;
e2 = shift;
for(sInt i=0;Digits[i];i++)
{
if(man>0x1000000000000000ULL) // we don't really need more digits
break;
man = man*10;
man += Digits[i]&15;
e10--;
}
if(man==0)
{
e2 = 0;
goto ende;
}
// normalize for max precision during e10 passes
while((man & highmask1)==0)
{
man = man<<1;
e2--;
}
// get rid of e10
while(e10<-6)
{
man = (man+500000)/1000000;
while((man & highmask)==0)
{
man = man<<1;
e2--;
}
e10+=6;
}
while(e10<0)
{
man = (man+5)/10;
while((man & highmask)==0)
{
man = man<<1;
e2--;
}
e10++;
}
while(e10>0)
{
man = man*10;
while((man & highmask)!=0)
{
man = man>>1;
e2++;
}
e10--;
}
// normalize for 23 bits
if(man!=0)
{
while(highmask0 & man)
{
man = man>>1;
e2++;
}
while(!(highmask & man))
{
man = man<<1;
e2--;
}
}
// denormals
e2 += 127;
if(e2<-23)
{
man = 0;
e2 = 0;
}
while(e2<0)
{
e2++;
man = man>>1;
}
// infinity
if(e2>255)
{
e2 = 255;
man = 0;
}
// puzzle
fu |= 0x7f800000 & (e2<<23);
fu |= 0x007fffff & ((man+((1ULL<<(shift-24))-1))>>(shift-23));
ende:
return fu;
}
sU64 sFloatInfo::AsciiToDouble()
{
sU64 man;
sInt e10;
sInt e2;
const sInt shift=60;
const sU64 highmask = ~((1ULL<<shift)-1);
const sU64 highmask1 = ~((1ULL<<(shift-1))-1);
const sU64 highmask0 = ~((1ULL<<(shift+1))-1);
sU64 fu;
// simple cases
fu = Negative ? 0x8000000000000000ULL : 0;
if(NaN)
{
fu |= 0x7ff0000000000000ULL;
fu |= 0x000fffffffffffffULL & NaN;
goto ende;
}
if(Infinite)
{
fu |= 0x7ff0000000000000ULL;
goto ende;
}
// scan digits
man = 0;
e10 = Exponent+1;
e2 = shift;
for(sInt i=0;Digits[i];i++)
{
if(man>(0x1000000000000000ULL/10)) // we don't really need more digits
break;
man = man*10;
man += Digits[i]&15;
e10--;
}
if(man==0)
{
e2 = 0;
goto ende;
}
// normalize for max precision during e10 passes
while((man & highmask1)==0)
{
man = man<<1;
e2--;
}
// get rid of e10
while(e10<0)
{
man = (man+5)/10;
while((man & highmask)==0)
{
man = man<<1;
e2--;
}
e10++;
}
while(e10>0)
{
man = man*10;
while((man & highmask)!=0)
{
man = man>>1;
e2++;
}
e10--;
}
// normalize for 23 bits
if(man!=0)
{
while(highmask0 & man)
{
man = man>>1;
e2++;
}
while(!(highmask & man))
{
man = man<<1;
e2--;
}
}
// denormals
e2 += 1023;
if(e2<-52)
{
man = 0;
e2 = 0;
}
while(e2<0)
{
e2++;
man = man>>1;
}
// infinity
if(e2>2047)
{
e2 = 2047;
man = 0;
}
// puzzle
fu |= 0x7ff0000000000000ULL & (sU64(e2)<<52);
fu |= 0x000fffffffffffffULL & ((man+((1ULL<<(shift-53))-1))>>(shift-52));
ende:
return fu;
}
/****************************************************************************/
void sFloatInfo::PrintE(const sStringDesc &desc)
{
if(Infinite)
sSPrintF(desc,L"%c#inf",Negative?'-':'+');
else if(NaN)
sSPrintF(desc,L"%c#nan%d",Negative?'-':'+',NaN);
else
sSPrintF(desc,L"%c%c.%se%d",Negative?'-':'+',Digits[0],Digits+1,Exponent);
}
void sFloatInfo::PrintF(const sStringDesc &desc,sInt fractions)
{
if(Infinite || NaN || Exponent>50 || Exponent<-50)
{
PrintE(desc);
return;
}
sChar *s = desc.Buffer;
sChar *e = desc.Buffer+desc.Size-1;
sInt exp = Exponent+1;
sInt dig = 0;
if(Negative)
*s++ = '-';
sChar *sx = s;
while(exp>0 && Digits[dig] && s<e)
{
*s++ = Digits[dig++];
exp--;
}
while(exp>0 && s<e)
{
*s++ = '0';
exp--;
}
if(fractions>0 && s<e)
{
if(s==sx)
*s++ = '0';
if(s<e)
{
*s++ = '.';
sInt frac = fractions;
while(exp<0 && s<e && frac>0)
{
*s++ = '0';
exp++;
frac--;
}
while(frac>0 && Digits[dig] && s<e)
{
*s++ = Digits[dig++];
frac--;
}
if(frac==0 && Digits[dig]>='5') //aufrunden. gefährlich...
{
for(sChar *d = s-1;d>=sx;d--)
{
if(*d!='.')
{
*d = *d + 1;
if(*d == '0'+10)
*d = '0';
else
goto round_done;
}
}
for(sChar *d = s-1;d>=sx;d--)
d[1] = d[0];
*sx = '1';
s++;
round_done:;
}
while(frac>0 && s<e)
{
*s++ = '0';
frac--;
}
}
}
if(s<e)
*s++ = 0;
else
sCopyString(desc,L"###");
}
/****************************************************************************/
/*** ***/
/*** Strings and Memory ***/
/*** ***/
/****************************************************************************/
void sCopyString(sChar *d,const sChar *s,sInt size)
{
size--;
while(size>0 && *s)
{
size--;
*d++ = *s++;
}
*d = 0;
}
void sCopyString(sChar8* d,const sChar *s,sInt c)
{
while(c>0 && ((*d++=(sChar8)*s++))!=0) c--;
if(c==0)
d[-1]=0;
}
void sCopyString(sChar *d, const sChar8* s,sInt c)
{
while(c>0 && ((*d++=(*s++)&0xff))!=0) c--;
if(c==0)
d[-1]=0;
}
void sCopyString(sChar8* d, const sChar8* s,sInt c)
{
while(c>0 && ((*d++=(*s++)&0xff))!=0) c--;
if(c==0)
d[-1]=0;
}
//Encodes the 2byte unicode string into UTF-8 byte array
void sCopyStringToUTF8(sChar8 *d, const sChar *s, sInt size)
{
const sChar MASKBITS = 0x3F;
const sChar MASKBYTE = 0x80;
const sChar MASK2BYTES = 0xC0;
const sChar MASK3BYTES = 0xE0;
size--;
while(size > 0 && *s)
{
if(*s < 0x80)
{
// 0xxxxxx -> 1byte
size--;
*d++ = (sChar8)*s++;
}
else if(*s < 0x800)
{
size -= 2;
if (size < 0)
break;
// 110xxxxx 10xxxxxx -> 2byte
*d++ = (sChar8)(MASK2BYTES | (*s >> 6));
*d++ = (sChar8)(MASKBYTE | (*s & MASKBITS));
s++;
}
else //(*s < 0x10000)
{
size -= 3;
if (size < 0)
break;
// 1110xxxx 10xxxxxx 10xxxxxx -> 3byte
*d++ = (sChar8)(MASK3BYTES | (*s >> 12));
*d++ = (sChar8)(MASKBYTE | ((*s >> 6) & MASKBITS));
*d++ = (sChar8)(MASKBYTE | (*s & MASKBITS));
s++;
}
}
*d = 0;
}
//Decodes the 0 terminated UTF-8 byte array into an 2byte unicode string.
//Supports up to 3 byte UTF8 sequences -> longer ones wouldn't fit into 2byte sChar.
//'size' is the size of 'd'
void sCopyStringFromUTF8(sChar *d, const sChar8 *s, sInt size)
{
const sChar MASK4BITS = 0x0F;
const sChar MASK5BITS = 0x1F;
const sChar MASK6BITS = 0x3F;
const sChar MASKBYTE = 0x80;
const sChar MASK2BYTES = 0xC0;
const sChar MASK3BYTES = 0xE0;
sInt shiftBits = 0;
size--;
while(size > 0 && *s)
{
if (!(*s & MASKBYTE)) // ASCII character -> direct copy
{
size--;
*d++ = *s++;
}
else if ((*s & MASK3BYTES) == MASK3BYTES) // Start of a 3byte character sequence
{
shiftBits = 6;
*d = ( ((*s & MASK4BITS) << 12) );
s++;
}
else if ((*s & MASK2BYTES) == MASK2BYTES) // Start of a 2byte character sequence
{
shiftBits = 0;
*d = ( ((*s & MASK5BITS) << 6) );
s++;
}
else if ((*s & MASKBYTE) == MASKBYTE) // In between byte
{
// add the six bits to the destination character
*d |= ( (*s & MASK6BITS) << shiftBits );
s++;
shiftBits -= 6;
if (shiftBits < 0) // reached the end of the sequence (either 2 or 3 bytes), start new character
{
shiftBits = 0;
size--;
d++;
}
}
else
{
// unsupported sequence / character. this may produce aukward results...
shiftBits = 0;
size--;
*d++ = '?';
s++;
}
}
*d = 0;
}
/****************************************************************************/
sChar sUpperChar(sChar c)
{
if(c>=0xe0 && c<=0xfd && c!=247) return c-0x20;
if(c>='a' && c<='z' ) return c-0x20;
return c;
}
sChar sLowerChar(sChar c)
{
if(c>=0xc0 && c<=0xdd && c!=215) return c+0x20;
if(c>='A' && c<='Z' ) return c+0x20;
return c;
}
sChar *sMakeUpper(sChar * s)
{
sChar * c = s;
while (*c > 0)
{
*c = sUpperChar(*c);
c++;
}
return s;
}
sChar *sMakeLower(sChar * s)
{
sChar * c = s;
while (*c > 0)
{
*c = sLowerChar(*c);
c++;
}
return s;
}
/****************************************************************************/
void sAppendString(sChar *d,const sChar *s,sInt size)
{
size--;
while(size>0 && *d)
{
size--;
d++;
}
while(size>0 && *s)
{
size--;
*d++ = *s++;
}
*d = 0;
}
void sAppendString2(sChar *d,const sChar *s,sInt size,sInt len)
{
size--;
while(size>0 && *d)
{
size--;
d++;
}
while(size>0 && len>0 && *s)
{
size--;
len--;
*d++ = *s++;
}
*d = 0;
}
/****************************************************************************/
void sAppendPath(sChar *d,const sChar *s,sInt size)
{
// absolute path?
if(sIsAbsolutePath(s))
{
sCopyString(d,s,size);
return;
}
// delete trailing "/" from destination path
sInt p0 = sMax(sFindLastChar(d,'/'),sFindLastChar(d,'\\'));
if (p0>=0 && p0==sGetStringLen(d)-1)
d[p0]=0;
// eat up leading "../"
while(sCmpMem(s,L"../",sizeof(sChar)*3)==0 ||sCmpMem(s,L"..\\",sizeof(sChar)*3)==0)
{
p0 = sMax(sFindLastChar(d,'/'),sFindLastChar(d,'\\'));
if(p0>0)
d[p0]=0;
else
*d=0;
s+=3;
}
// relative path, append, and insert '/' if required
size--;
if(*d!=0)
{
while(size>0 && *d)
{
size--;
d++;
}
if(d[-1]!='\\' && d[-1]!='/' && size>0)
{
*d++ = '/';
size--;
}
}
while(size>0 && *s)
{
size--;
*d++ = *s++;
}
*d = 0;
}
/****************************************************************************/
sInt sCmpString(const sChar *a,const sChar *b)
{
sInt aa,bb;
do
{
aa = *a++;
bb = *b++;
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sInt sCmpStringI(const sChar *a,const sChar *b)
{
sInt aa,bb;
do
{
aa = *a++; if(aa>='a' && aa<='z') aa=aa-'a'+'A';
bb = *b++; if(bb>='a' && bb<='z') bb=bb-'z'+'Z';
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sInt sCmpStringP(const sChar *a,const sChar *b)
{
sInt aa,bb;
do
{
aa = *a++; if(aa>='A' && aa<='Z') aa=aa-'A'+'a'; if(aa=='\\') aa='/';
bb = *b++; if(bb>='A' && bb<='Z') bb=bb-'A'+'a'; if(bb=='\\') bb='/';
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sInt sCmpStringLen(const sChar *a,const sChar *b,sInt len)
{
sInt aa,bb;
do
{
if(len--==0) return 0;
aa = *a++;
bb = *b++;
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sInt sCmpStringILen(const sChar *a,const sChar *b,sInt len)
{
sInt aa,bb;
do
{
if(len--==0) return 0;
aa = *a++; if(aa>='a' && aa<='z') aa=aa-'a'+'A';
bb = *b++; if(bb>='a' && bb<='z') bb=bb-'z'+'Z';
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sInt sCmpStringPLen(const sChar *a,const sChar *b,sInt len)
{
sInt aa,bb;
do
{
if(len--==0) return 0;
aa = *a++; if(aa>='A' && aa<='Z') aa=aa-'A'+'a'; if(aa=='\\') aa='/';
bb = *b++; if(bb>='A' && bb<='Z') bb=bb-'A'+'a'; if(bb=='\\') bb='/';
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
/****************************************************************************/
sInt sFindString(const sChar *f,const sChar *s)
{
sInt testlen = sGetStringLen(f);
sInt findlen = sGetStringLen(s);
for (sInt i = 0; i <= testlen-findlen; i++)
{
if (sCmpMem(f+i,s,findlen*sizeof(sChar)) == 0)
return i;
}
return -1;
}
/****************************************************************************/
sInt sFindStringI(const sChar *f,const sChar *s)
{
sInt testlen = sGetStringLen(f);
sInt findlen = sGetStringLen(s);
for (sInt i = 0; i <= testlen-findlen; i++)
{
if (sCmpStringILen(f+i,s,findlen) == 0)
return i;
}
return -1;
}
/****************************************************************************/
sInt sFindFirstChar(const sChar *f,sInt c)
{
for(sInt i=0;f[i];i++)
if(f[i]==sChar(c))
return i;
return -1;
}
/****************************************************************************/
sInt sFindLastChar(const sChar *f,sInt c)
{
sInt best = -1;
for(sInt i=0;f[i];i++)
if(f[i]==sChar(c))
best = i;
return best;
}
/****************************************************************************/
sInt sFindNthChar(const sChar *f, sInt c, sInt n)
{
for(sInt i=0;f[i];i++)
{
if(f[i]==sChar(c))
{
if (n==0)
return i;
else
n--;
}
}
return -1;
}
/****************************************************************************/
sInt sFindChar(const sChar * str, sChar character, sInt startPos)
{
const sChar * startStr = &str[startPos];
sInt charPos = sFindFirstChar(startStr, character);
if (charPos >= 0) return startPos + charPos;
return -1;
}
/****************************************************************************/
const sChar *sFindFileExtension(const sChar *a)
{
const sChar *result=0;
while(*a)
{
if(*a=='.')
result = a+1;
a++;
}
if(result)
return result;
else
return L"";
}
sBool sExtractFileExtension(const sStringDesc &d, const sChar *a, sInt nr/*=0*/)
{
sInt count = 0;
const sChar *ptr = a;
while(*ptr)
{
if(*ptr++ == '.')
count++;
}
if(nr>=count)
return sFALSE;
ptr = a;
while(*ptr && count>nr)
{
if(*ptr++=='.')
count--;
}
sChar *dest = d.Buffer;
count = d.Size;
while(*ptr && *ptr != '.' && count>1)
*dest++ = *ptr++;
*dest = 0;
return !*ptr || *ptr == '.';
}
sBool sCheckFileExtension(const sChar *name,const sChar *ext)
{
if(!name)
return sFALSE;
sInt len1 = sGetStringLen(name);
sInt len2 = sGetStringLen(ext);
if(len1<len2) return 0;
return name[len1-len2-1]=='.' && sCmpStringI(name+len1-len2,ext)==0;
}
sChar *sFindFileExtension(sChar *a)
{
sChar *result=0;
while(*a)
{
if(*a=='.')
result = a+1;
a++;
}
if(result)
return result;
else
return emptyString;
}
const sChar *sFindFileWithoutPath(const sChar *a)
{
const sChar *result=a;
while(*a)
{
if(*a=='/' || *a=='\\' || *a==':')
result = a+1;
a++;
}
return result;
}
sBool sIsAbsolutePath(const sChar *a)
{
if(a[0]!=0 && a[1]==':') return 1; // does not handle "cdrom:"
if(a[0]=='\\' /*&& a[1]=='\\'*/) return 1;
if(a[0]=='/' /*&& a[1]=='/'*/) return 1;
return 0;
}
sBool sExtractPathDrive(const sChar *path, const sStringDesc &drive)
{
sVERIFY(drive.Size);
const sChar *ptr = path;
sInt count = 0;
while(*ptr && *ptr!=':' && count<drive.Size-1)
drive.Buffer[count++]=*ptr++;
if(*ptr && *ptr==':')
{
drive.Buffer[count] = 0;
return sTRUE;
}
drive.Buffer[0] = 0;
return sFALSE;
}
void sExtractPath(const sChar *a, const sStringDesc &path)
{
const sChar *b=sFindFileWithoutPath(a);
sCopyString(path.Buffer,a,sMin<sDInt>(path.Size,b-a+1));
}
void sExtractPath(const sChar *a, const sStringDesc &path, const sStringDesc &filename)
{
const sChar *b=sFindFileWithoutPath(a);
sCopyString(path.Buffer,a,sMin<sDInt>(path.Size,b-a+1));
sCopyString(filename,b);
}
void sMakeRelativePath(const sStringDesc &path, const sChar *relTo)
{
// check drive/protocol letters
sChar *p=path.Buffer;
sInt pcp=sFindFirstChar(path.Buffer,':');
sInt rcp=sFindFirstChar(relTo,':');
if (rcp<0)
{
if (pcp>=0) return; // we're lost. we don't know on what drive the rel path is.
}
else
{
if (pcp>=0)
{
if (rcp!=pcp || sCmpStringILen(p,relTo,pcp-1)) return; // different drive or protocol
sCopyString(path,p+pcp+1); // cut off everything
}
relTo+=rcp+1;
}
sInt plen=sGetStringLen(p);
sInt rlen=sGetStringLen(relTo);
// check for double slashes at beginning (URLs or network shares)
if (plen>=2 && ((p[0]=='/' && p[1]=='/') || (p[0]=='\\' || p[1]=='\\')))
{
if (rlen>=2 && ((relTo[0]=='/' && relTo[1]=='/') || (relTo[0]=='\\' || relTo[1]=='\\')))
{
relTo+=2;
rlen-=2;
sCopyString(path,p+2);
plen-=2;
}
else return; // we'll have to give up here.
}
// now simply cut off the common parts
while (*p && *relTo)
{
sInt pc=sLowerChar(*p); if (pc=='\\') pc='/';
sInt rc=sLowerChar(*relTo); if (pc=='\\') pc='/';
if (pc!=rc) break;
p++;
relTo++;
}
sCopyString(path,p);
}
sInt sCountChar(const sChar *str,sChar c)
{
sInt count = 0;
while(*str)
if(*str++ == c)
count++;
return count;
}
// find in a string like L"bli|bla|blub"
sInt sFindChoice(const sChar *str,const sChar *choices)
{
sInt i = 0;
sInt len;
sInt len2 = sGetStringLen(str);
while(*choices)
{
while(*choices=='|')
{
choices++;
i++;
}
len = 0;
while(choices[len]!=0 && choices[len]!='|')
len++;
if(len==len2 && sCmpMem(str,choices,len*sizeof(sChar))==0)
return i;
choices+=len;
}
return -1;
}
sBool sFindFlag(const sChar *str,const sChar *choices,sInt &mask_,sInt &value_)
{
const sChar *s = choices;
sInt len2 = sGetStringLen(str);
while(*s)
{
sInt max = 0;
sInt shift = 0;
sInt found = 0;
sInt value = 0;
if(*s=='*')
{
s++;
sScanInt(s,shift);
}
for(;;)
{
while(*s=='|')
{
s++;
value++;
}
if(*s==':' || *s==0)
break;
if(sIsDigit(*s))
sScanInt(s,value);
if(*s==' ')
s++;
sInt len = 0;
while(s[len]!='|' && s[len]!=':' && s[len]!=0) len++;
if(len2==len && sCmpMem(str,s,len*sizeof(sChar))==0)
{
value_ = value<<shift;
found = 1;
}
s += len;
max = sMax(max,value);
}
if(found)
{
mask_ = 1;
while(mask_ < max)
mask_ = mask_*2+1;
mask_ <<= shift;
return 1;
}
if(*s==':')
s++;
}
return 0;
}
/*
sChar * sMakeChoice(sChar * choice, const sChar * choices, sInt index)
{
choice[0] = 0;
sInt charPos;
if (index > 0)
{
charPos = sFindNthChar(choices, '|', index-1);
if (charPos < 0) return sNULL;
choices += charPos+1;
}
else
{
charPos = sFindFirstChar(choices, ':');
if (charPos >= 0)
choices += charPos+1;
}
charPos = sFindFirstChar(choices, '|');
if (charPos < 0) charPos = sGetStringLen(choices);
sCopyString(choice, choices, charPos+1);
return choice;
}
*/
sInt sCountChoice(const sChar *choices)
{
if(*choices==0)
{
return 0;
}
else
{
sInt i = 1;
while(*choices)
if(*choices++ == '|')
i ++;
return i;
}
}
void sMakeChoice(const sStringDesc &desc,const sChar *choices,sInt index)
{
while(index>0)
{
while(*choices!='|' && *choices!=0) choices++;
if(*choices=='|') choices++;
index--;
}
sInt n=0;
while(choices[n]!='|' && choices[n]!=0) n++;
sChar *d = desc.Buffer;
for(sInt i=0;i<desc.Size-1 && i<n;i++)
*d++ = choices[i];
*d++ = 0;
}
sStringDesc sGetAppendDesc(const sStringDesc &sd)
{
sStringDesc result = sd;
sInt length = sGetStringLen(sd.Buffer);
result.Buffer += length;
result.Size -= length;
return result;
}
sBool sCheckPrefix(const sChar *string,const sChar *prefix)
{
for(sInt i=0;prefix[i];i++)
if(string[i]!=prefix[i])
return 0;
return 1;
}
sBool sCheckSuffix(const sChar *string,const sChar *suffix)
{
sInt len1 = sGetStringLen(string);
sInt len2 = sGetStringLen(suffix);
if(len2>len1)
return 0;
for(sInt i=0;i<len2;i++)
if(string[len1-len2+i]!=suffix[i])
return 0;
return 1;
}
sInt sReplaceChar(sChar *string, sChar from, sChar to)
{
if (!string) return 0;
sInt count=0;
for (;*string;string++)
{
if (*string==from)
{
*string=to;
count++;
}
}
return count;
}
/****************************************************************************/
// ?=any one character, *=zero or more characters
sBool sMatchWildcard(const sChar *wild,const sChar *text,sBool casesensitive,sBool pathsensitive)
{
for(;;)
{
if(*wild=='*')
{
wild++;
while(*text)
{
if(sMatchWildcard(wild,text,casesensitive,pathsensitive)) return 1;
text++;
}
}
else if(*wild=='?')
{
if(*text==0) return 0;
wild++;
text++;
}
else if(*wild==0)
{
return (*text==0);
}
else
{
sInt a = *text;
sInt b = *wild;
if(!casesensitive)
{
a = sUpperChar(a);
b = sUpperChar(b);
}
if(!pathsensitive)
{
if(a=='\\') a = '/';
if(b=='\\') b = '/';
}
if(a!=b) return 0;
wild++;
text++;
}
}
}
/****************************************************************************/
void sReadString(sU32 *&data,sChar *buffer,sInt size)
{
sInt len = *data++;
sInt chars = sMin(len,size-1);
sCopyMem(buffer,data,chars*2);
buffer[chars] = 0;
data += (len+1)/2;
}
void sWriteString(sU32 *&data,const sChar *buffer)
{
sInt len = sGetStringLen(buffer);
sVERIFY(len<0x7ffe);
*data++ = len;
len = sAlign(len,2);
sCopyMem(data,buffer,len*2);
data += len/2;
}
/****************************************************************************/
sBool sIsName(const sChar *s)
{
if(!sIsLetter(*s++)) return 0;
sInt c;
while((c = (*s++))!=0)
if(!sIsLetter(c) && !sIsDigit(c))
return 0;
return 1;
}
sU32 sHashString(const sChar *string)
{
sU32 crc = 0xffffffff;
while(*string)
{
crc = sCRCTable[(crc ^ (*string++)) & 0xff] ^ (crc>>8);
}
return crc ^ 0xfffffffe;
}
sU32 sHashString(const sChar *string,sInt len)
{
sU32 crc = 0xffffffff;
while(len>0)
{
crc = sCRCTable[(crc ^ (*string++)) & 0xff] ^ (crc>>8);
len--;
}
return crc ^ 0xfffffffe;
}
/****************************************************************************/
sBool sScanInt(const sChar *&s,sInt &result)
{
sU32 val = 0;
sBool sign = 0;
sInt overflow = 0;
sU32 digit;
// scan sign
if(*s=='-')
{
s++;
sign = 1;
}
else if(*s=='+')
{
s++;
}
// scan value
if(!sIsDigit(*s))
return 0;
while(sIsDigit(*s))
{
digit = ((*s++)&15);
if(val>(0x80000000-digit)/10)
overflow = 1;
val = val * 10 + digit;
}
if(overflow)
return 0;
// apply sign and write out
if(sign)
{
result = -(sInt)val;
}
else
{
if(val>0x7fffffff)
return 0;
result = val;
}
// done
return 1;
}
#if sCONFIG_64BIT
sBool sScanInt(const sChar *&s,sDInt &result)
{
sU64 val = 0;
sBool sign = 0;
sInt overflow = 0;
sU64 digit;
// scan sign
if(*s=='-')
{
s++;
sign = 1;
}
else if(*s=='+')
{
s++;
}
// scan value
if(!sIsDigit(*s))
return 0;
while(sIsDigit(*s))
{
digit = ((*s++)&15);
if(val>(0x80000000-digit)/10)
overflow = 1;
val = val * 10 + digit;
}
if(overflow)
return 0;
// apply sign and write out
if(sign)
{
result = -(sInt)val;
}
else
{
if(val>0x7fffffff)
return 0;
result = val;
}
// done
return 1;
}
#endif
sBool sScanFloat(const sChar *&s,sF32 &result)
{
sF64 val = 0;
sF64 dec = 1;
sInt sign = 1;
// scan sign
if(*s=='-')
{
s++;
sign = -1;
}
else if(*s=='+')
{
s++;
}
// here we must have either '.' or digit.
if(!sIsDigit(*s) && *s!='.')
return 0;
// scan integer part
while(sIsDigit(*s))
{
val = val * 10 + ((*s++)&15);
}
// optional fractional part
if(*s=='.')
{
s++;
while(sIsDigit(*s))
{
dec = dec * 10;
val += ((*s++)&15) / dec;
}
}
// optional exponent
if(*s=='e' || *s=='E')
{
s++;
// optional exponent sign
sInt eSign = 1;
if(*s == '-')
s++, eSign = -1;
else if(*s == '+')
s++;
// exponent itself
if(!sIsDigit(*s))
return 0;
sInt eVal = 0;
while(sIsDigit(*s))
eVal = eVal * 10 + (*s++ - '0');
val *= sFPow(10.0f,eSign * eVal);
}
// apply sign and write out
result = sF32(val * sign);
// done
return 1;
}
sBool sScanHex(const sChar *&s,sInt &result, sInt maxlen)
{
sU32 val;
sInt c;
if(!sIsHex(*s)) return 0;
if(s[0]=='0' && (s[1]=='x' || s[1]=='X')) // skip leading 0x
s+=2;
val = 0;
while(sIsHex(*s))
{
c = *s++;
val = val * 16;
if(c>='0' && c<='9') val += c-'0';
if(c>='a' && c<='f') val += c-'a'+10;
if(c>='A' && c<='F') val += c-'A'+10;
if (!--maxlen) break;
}
result = val;
return 1;
}
sBool sScanMatch(const sChar *&scan, const sChar *match)
{
const sChar *sp=scan;
while (*match)
if (*sp++!=*match++) return sFALSE;
scan=sp;
return sTRUE;
}
sBool sScanGUID(const sChar *&str, sGUID &guid)
{
const sChar *p=str;
sInt temp;
if (!sScanHex(p,temp,8)) return sFALSE;
guid.Data32=temp;
if (*p++!='-') return sFALSE;
for (sInt i=0; i<3; i++)
{
if (!sScanHex(p,temp,4)) return sFALSE;
guid.Data16[i]=temp;
if (*p++!='-') return sFALSE;
}
for (sInt i=0; i<6; i++)
{
if (!sScanHex(p,temp,2)) return sFALSE;
guid.Data8[i]=temp;
}
str=p;
return sTRUE;
}
/****************************************************************************/
/*** ***/
/*** String Formatting, varargs ***/
/*** ***/
/****************************************************************************/
/*
extern "C" char * __cdecl _fcvt( double value, int count, int *dec, int *sign );
extern "C" char * __cdecl _ecvt( double value, int count, int *dec, int *sign );
sBool sFormatString(const sStringDesc &desc,const sChar *s,const sChar **fp)
{
sInt c;
sInt field0;
sInt field1;
sInt minus;
sInt null;
sInt len;
sChar buffer[64];
char *convert;
sChar *string;
sInt val;
sInt arg;
sInt sign;
sInt i;
sF64 fval;
sChar *d = desc.Buffer;
sInt left = desc.Size;
static sChar hex[17] = L"0123456789abcdef";
static sChar HEX[17] = L"0123456789ABCDEF";
arg = 0;
left--;
c = *s++;
while(c)
{
if(c=='%')
{
c = *s++;
minus = 0;
null = 0;
field0 = 0;
field1 = 4;
if(c=='-')
{
minus = 1;
c = *s++;
}
if(c=='0')
{
null = 1;
}
while(c>='0' && c<='9')
{
field0 = field0*10 + c - '0';
c = *s++;
}
if(c=='.')
{
field1=0;
c = *s++;
while(c>='0' && c<='9')
{
field1 = field1*10 + c - '0';
c = *s++;
}
}
if(c=='%')
{
c = *s++;
if(left>0)
{
*d++ = '%';
left--;
}
}
else if(c=='d' || c=='x' || c=='X' || c=='i' || c=='f' || c=='e')
{
len = 0;
sign = 0;
if(c=='f' || c=='e')
{
fval = sVARARGF(fp,arg);arg+=2;
}
else
{
val = sVARARG(fp,arg);arg++;
}
if(c=='f') // this is preliminary!!!!!!!
{
#if sINTRO
convert = "???";
#else
if(fval<0)
field1++;
convert = _fcvt(fval,field1,&i,&sign);
#endif
if(i<0)
{
buffer[len++]='.';
while(i<=-1)
{
i++;
buffer[len++]='0';
}
i=-1;
}
while(*convert)
{
if(i==0)
buffer[len++]='.';
i--;
buffer[len++]=*convert++;
}
}
else if(c=='e')
{
#if sINTRO
convert = "???";
#else
if(fval<0)
field1++;
convert = _ecvt(fval,field1,&i,&sign);
#endif
if(*convert)
{
buffer[len++] = *convert++;
buffer[len++] = '.';
while(*string)
buffer[len++] = *convert++;
buffer[len++] = 'e';
if(i<0)
{
buffer[len++] = '-';
i = -i;
}
else
buffer[len++] = '+';
if(i>=100)
{
buffer[len++] = 'X';
buffer[len++] = 'X';
}
else
{
buffer[len++] = (i/10)%10 + '0';
buffer[len++] = i%10 + '0';
}
}
}
else if(c=='d' || c=='i')
{
if(sU32(val)==0x80000000)
{
val = val/10;
buffer[len++] = '8';
}
if(val<0)
{
sign = 1;
val = -val;
}
do
{
buffer[len++] = val%10+'0';
val = val/10;
}
while(val!=0);
if(sign)
len++;
}
else if(c=='x' || c=='X')
{
do
{
if(c=='x')
buffer[len] = hex[val&15];
else
buffer[len] = HEX[val&15];
val = (val>>4)&0x0fffffff;
len++;
}
while(val!=0);
}
if(!minus && !null)
{
while(field0>len && left>0)
{
*d++ = ' ';
left--;
field0--;
}
}
if(sign && left>0)
{
*d++ = '-';
left--;
field0--;
len--;
}
if(!minus && null)
{
while(field0>len && left>0)
{
*d++ = '0';
left--;
field0--;
}
}
i = 0;
while(len>0 && left>0)
{
len--;
if(c=='f' || c=='e')
*d++ = buffer[i++];
else
*d++ = buffer[len];
left--;
field0--;
}
if(!minus)
{
while(field0>len && left>0)
{
*d++ = ' ';
left--;
field0--;
}
}
c = *s++;
}
else if(c=='c')
{
val = (sInt)sVARARG(fp,arg);arg++;
if(left>0)
{
*d++ = val;
left--;
}
c = *s++;
}
else if(c=='s')
{
string = (sChar * )(sDInt)sVARARG(fp,arg);arg++;
len = sGetStringLen(string);
if(field0<=len)
field0=len;
if(!minus)
{
while(field0>len && left>0)
{
*d++ = ' ';
left--;
field0--;
}
}
while(*string && left>0)
{
*d++=*string++;
left--;
}
if(minus)
{
while(field0>len && left>0)
{
*d++ = ' ';
left--;
field0--;
}
}
c = *s++;
}
else if(c=='h' || c=='H')
{
val = sVARARG(fp,arg);arg++;
if(c=='H')
{
if(sAbs(val)>=0x00010000)
{
if(val>0x80000000)
val |= 0x000000ff;
else
val &= 0xffffff00;
}
}
*d++ = '0';
*d++ = 'x';
*d++ = hex[(val>>28)&15];
*d++ = hex[(val>>24)&15];
*d++ = hex[(val>>20)&15];
*d++ = hex[(val>>16)&15];
*d++ = '.';
*d++ = hex[(val>>12)&15];
*d++ = hex[(val>> 8)&15];
*d++ = hex[(val>> 4)&15];
*d++ = hex[(val )&15];
c = *s++;
}
else if(c!=0)
{
c = *s++;
}
}
else
{
if(left>0)
{
*d++ = c;
left--;
}
c = *s++;
}
}
*d=0;
return left>0; // actually, if text fit's exactly, a false truncation error is reported!
}
*/
/****************************************************************************/
/*
void __cdecl sSPrintF(const sStringDesc &desc,const sChar *format,...)
{
sFormatString(desc,format,&format);
}
*/
/****************************************************************************/
#if sENABLE_DPRINT
void (*sPrintScreenCB)(const sChar *text) = 0;
void sPrintScreen(const sChar *text)
{
if(text)
sDPrint(text);
if(sPrintScreenCB)
(*sPrintScreenCB)(text);
}
#endif
/****************************************************************************/
/*** ***/
/*** String Formatting, type-safe ***/
/*** ***/
/****************************************************************************/
sBool sFormatStringBuffer::Fill()
{
loop:
while(*Format!='%' && *Format!=0 && Dest<End-1)
*Dest++ = *Format++;
if(Format[0]=='%' && Format[1]=='%' && Dest<End-1)
{
*Dest++ = '%';
Format+=2;
goto loop;
}
sVERIFY(Dest<End);
if(*Format==0 || *Format=='%')
{
Dest[0] = 0;
return 1;
}
else
{
if(Dest==End-1)
Dest--;
*Dest++ = '?'; // the format string has not yet been consumed!
*Dest = 0;
Format = L"";
return 0;
}
}
void sFormatStringBuffer::GetInfo(sFormatStringInfo &info)
{
sInt c;
info.Format = 0;
info.Field = 0;
info.Fraction = -1;
info.Minus = 0;
info.Null = 0;
info.Truncate = 0;
if(!Fill())
return;
sVERIFY(*Format=='%');
Format++;
c = *Format++;
if (c=='!')
{
info.Truncate = 1;
c = *Format++;
}
if(c=='-')
{
info.Minus = 1;
c = *Format++;
}
if(c=='0')
{
info.Null = 1;
}
while(c>='0' && c<='9')
{
info.Field = info.Field*10 + c - '0';
c = *Format++;
}
if(c=='.')
{
info.Fraction=0;
c = *Format++;
while(c>='0' && c<='9')
{
info.Fraction = info.Fraction*10 + c - '0';
c = *Format++;
}
}
if(c!=0)
info.Format = c;
sVERIFY(info.Format);
}
void sFormatStringBuffer::Add(const sFormatStringInfo &info,const sChar *buffer,sBool sign)
{
sInt len = sGetStringLen(buffer);
sInt field = info.Field;
if(!info.Minus && !info.Null)
{
while(field>(sign?len+1:len) && Dest<End-1)
{
*Dest++ = ' ';
field--;
}
}
if(sign && Dest<End-1)
{
*Dest++ = '-';
field--;
}
if(info.Null)
{
while(field>len && Dest<End-1)
{
*Dest++ = '0';
field--;
}
}
if (info.Truncate)
{
while(len>0 && field>0 && Dest<End-1)
{
*Dest++ = *buffer++;
len--;
field--;
}
}
else
{
while(len>0 && Dest<End-1)
{
*Dest++ = *buffer++;
len--;
field--;
}
}
if(info.Minus)
{
while(field>len && Dest<End-1)
{
*Dest++ = ' ';
field--;
}
}
}
void sFormatStringBuffer::Print(const sChar *str)
{
while(Dest<End-1 && *str)
*Dest++ = *str++;
}
template<typename Type>
void sFormatStringBuffer::PrintInt(const sFormatStringInfo &info,Type val,sBool sign)
{
sChar buf[32];
static sChar hex[17] = L"0123456789abcdef";
static sChar HEX[17] = L"0123456789ABCDEF";
static sChar units[] = L" kmgtpe";
static sChar UNITS[] = L" KMGTPE";
sInt len=0,komma=0,unit=0;
switch(info.Format)
{
case '_':
if(!sign)
{
while(val>0 && Dest<End-1)
{
*Dest++ = ' ';
val--;
}
}
break;
case 't':
if(!sign)
{
while(val>0 && Dest<End-1)
{
*Dest++ = '\t';
val--;
}
}
break;
case 'c':
*Dest++ = sign ? -sInt(val) : val;
break;
case 'k': // kilo mega tera
case 'K':
len = sCOUNTOF(buf);
buf[--len] = 0;
if (info.Format=='k')
{
// GCC warning: comparison is always false due to limited range of data type
while (val>=1000*10 && unit<(sCOUNTOF(units)-1))
{
unit++;
val=(val)/1000;
}
if (units[unit]!=' ') buf[--len] = units[unit];
}
else
{
// GCC warning: comparison is always false due to limited range of data type
while (val>=1024*10 && unit<(sCOUNTOF(units)-1))
{
unit++;
val=(val)/1024;
}
if (UNITS[unit]!=' ') buf[--len] = UNITS[unit];
}
do
{
buf[--len] = val%10+'0';
val = val/10;
}
while(val!=0);
Add(info,buf+len,sign);
break;
case 'x':
case 'X':
len = sCOUNTOF(buf);
buf[--len] = 0;
do
{
if(info.Format=='x')
buf[--len] = hex[val&15];
else
buf[--len] = HEX[val&15];
val = (val>>4);
}
while(val!=0);
Add(info,buf+len,sign);
break;
case 'r': // radis "12.34"
len = sCOUNTOF(buf);
buf[--len] = 0;
komma = 0;
do
{
buf[--len] = val%10+'0';
val = val/10;
komma++;
if(komma == info.Fraction)
buf[--len] = '.';
}
while(val!=0 || komma<info.Fraction+1);
Add(info,buf+len,sign);
break;
case 'f':
case 'e':
case 'F':
case 'E':
case 'i':
case 'd':
default:
len = sCOUNTOF(buf);
buf[--len] = 0;
do
{
buf[--len] = val%10+'0';
val = val/10;
}
while(val!=0);
Add(info,buf+len,sign);
break;
case 'h': // 4h3d1w , a weak is 5 days, a day is 8 hours
len = 0;
if(val==0)
{
buf[len++] = '0';
}
else
{
if(val>39)
{
sInt weeks = val / 40;
val -= weeks*40;
if(weeks>=10)
{
buf[len++] = weeks/10+'0';
weeks = weeks % 10;
}
buf[len++] = weeks+'0';
buf[len++] = 'w';
}
if(val>8)
{
sInt days = val/8;
val -= days*8;
buf[len++] = days+'0';
buf[len++] = 'd';
}
if(val>0)
{
buf[len++] = val+'0';
buf[len++] = 'h';
}
}
buf[len++] = 0;
Add(info,buf,sign);
break;
}
}
void sFormatStringBuffer::PrintFloat(const sFormatStringInfo &info,sF32 v)
{
sFloatInfo fi;
sString<256> buf;
switch(info.Format)
{
case 'f':
case 'e':
case 'F':
case 'E':
{
fi.FloatToAscii(sRawCast<sU32,sF32>(v));
sInt sign = fi.Negative;
fi.Negative = 0;
sInt frac = info.Fraction;
sInt field = info.Field;
if(field==0)
field = 7;
if(frac==-1)
frac = sClamp(field-2-fi.Exponent-sign,0,field-2);
if(info.Format=='F' && frac>0)
frac--;
fi.PrintF(buf,frac);
if(info.Format=='F') // special format: add trailing 'f'
{
sBool addf = 0;
for(sInt n=0;buf[n];n++)
if(buf[n]=='.')
addf = 1;
if(addf==1)
buf.Add(L"f");
}
Add(info,buf,sign);
}
break;
default:
if(v>=0)
PrintInt(info,sU64(v),0);
else
PrintInt(info,sU64(-v),1);
break;
}
}
void sFormatStringBuffer::PrintFloat(const sFormatStringInfo &info,sF64 v)
{
sFloatInfo fi;
sString<256> buf;
fi.DoubleToAscii(sRawCast<sU64,sF64>(v));
sInt sign = fi.Negative;
fi.Negative = 0;
sInt frac = info.Fraction;
sInt field = info.Field;
if(field==0)
{
field = 7;
frac = 3;
}
else
{
if(frac==-1) frac = sMax(0,field-2-fi.Exponent-sign);
}
fi.PrintF(buf,frac);
Add(info,buf,sign);
}
// sNOINLINE for size and a VS2005 compiler error
sNOINLINE sFormatStringBuffer sFormatStringBase(const sStringDesc &buffer,const sChar *format)
{
sFormatStringBuffer buf;
buf.Start = buffer.Buffer;
buf.Dest = buffer.Buffer;
buf.End = buffer.Buffer + buffer.Size;
buf.Format = format;
buf.Fill();
return buf;
}
sNOINLINE void sFormatStringBaseCtx(sFormatStringBuffer &buf,const sChar *format)
{
sThreadContext *tx = sGetThreadContext();
buf.Start = tx->PrintBuffer;
buf.Dest = tx->PrintBuffer;
buf.End = tx->PrintBuffer + sCOUNTOF(tx->PrintBuffer);
buf.Format = format;
buf.Fill();
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sInt val)
{
if (!*f.Format)
return f;
sBool sign=(val<0);
if(sign) val = -val;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintInt<sU32>(info,sU32(val),sign);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sU32 val)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintInt<sU32>(info,val,0);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sU64 val)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintInt<sU64>(info,val,0);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sS64 val)
{
if (!*f.Format)
return f;
sBool sign=(val<0);
if(sign) val = -val;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintInt<sU64>(info,sU64(val),sign);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,void *ptr)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
info.Null = 1;
info.Field = sCONFIG_64BIT ? 16 : 8;
f.PrintInt<sU64>(info,sU64(ptr),0);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sF32 v)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintFloat(info,v);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,sF64 v)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintFloat(info,v);
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f,const sChar *str)
{
sString<sMAXPATH> path;
sInt i;
sBool q=1;
const sChar *s;
if (!*f.Format)
return f;
if (!str) str=L"<NULL>";
sFormatStringInfo info;
f.GetInfo(info);
if (str)
{
switch(info.Format)
{
case 'c': // lower case
i=0;
while(i<sMAXPATH-2 && *str)
{
sChar c = *str++;
if(c>='A' && c<='Z') c=c+'a'-'A';
path[i++] = c;
}
path[i++] = 0;
f.Add(info,path,0);
break;
case 'C': // upper case
i=0;
while(i<sMAXPATH-2 && *str)
{
sChar c = *str++;
if(c>='a' && c<='z') c=c+'A'-'a';
path[i++] = c;
}
path[i++] = 0;
f.Add(info,path,0);
break;
case 'p':
i=0;
while(i<sMAXPATH-2 && *str)
{
sChar c = *str++;
if(c=='\\') c='/';
// if(c=='"') path[i++] = '\\';
path[i++] = c;
}
path[i++] = 0;
f.Add(info,path,0);
break;
case 'P':
i=0;
while(i<sMAXPATH-2 && *str)
{
sChar c = *str++;
if(c=='/') c='\\';
// if(c=='"') path[i++] = '\\';
path[i++] = c;
}
path[i++] = 0;
f.Add(info,path,0);
break;
case 'Q': // quote if required
q = 1;
s = str;
if(sIsLetter(*s))
{
q = 0;
while(*s && !q)
{
if(!sIsLetter(*s) && !sIsDigit(*s))
q = 1;
s++;
}
}
// nobreak
case 'q': // quote always
if(q)
{
sChar *d = path;
*d++ = '\"';
while(d < path+sMAXPATH-3 && *str)
{
switch(*str)
{
case '"':
*d++ = '\\';
*d++ = '"';
break;
case '\\':
*d++ = '\\';
*d++ = '\\';
break;
case '\n':
*d++ = '\\';
*d++ = 'n';
break;
case '\t':
*d++ = '\\';
*d++ = 't';
break;
case '\r':
*d++ = '\\';
*d++ = 'r';
break;
case '\f':
*d++ = '\\';
*d++ = 'f';
break;
default:
*d++ = *str;
break;
}
str++;
}
*d++ = '\"';
*d++ = 0;
f.Add(info,path,0);
}
else
{
f.Add(info,str,0);
}
break;
default: // 's'
f.Add(info,str,0);
break;
}
}
f.Fill();
return f;
}
//sString<1024> sXDPrintFBuffer;
/****************************************************************************/
/*** ***/
/*** Small Structures ***/
/*** ***/
/****************************************************************************/
sFormatStringBuffer& operator% (sFormatStringBuffer &f, const sRect &r)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
sBool sign;
sign = (r.x0<0);
f.PrintInt<sU32>(info,sign?-r.x0:r.x0,sign);
f.Print(L" ");
sign = (r.y0<0);
f.PrintInt<sU32>(info,sign?-r.y0:r.y0,sign);
f.Print(L"-");
sign = (r.x1<0);
f.PrintInt<sU32>(info,sign?-r.x1:r.x1,sign);
f.Print(L" ");
sign = (r.y1<0);
f.PrintInt<sU32>(info,sign?-r.y1:r.y1,sign);
f.Print(L" ");
f.Fill();
return f;
}
sFormatStringBuffer& operator% (sFormatStringBuffer &f, const sFRect &r)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
f.PrintFloat(info,r.x0);
f.Print(L" ");
f.PrintFloat(info,r.y0);
f.Print(L"-");
f.PrintFloat(info,r.x1);
f.Print(L" ");
f.PrintFloat(info,r.y1);
f.Print(L" ");
f.Fill();
return f;
}
/****************************************************************************/
sFRect sRectToFRect(const sRect &src)
{
return sFRect(src.x0, src.y0, src.x1, src.y1);
}
/****************************************************************************/
sRect sFRectToRect(const sFRect &src)
{
return sRect(sInt(src.x0), sInt(src.y0), sInt(src.x1), sInt(src.y1));
}
/****************************************************************************/
void sRandom::Seed(sU32 seed)
{
kern = seed+seed*17+seed*121+(seed*121/17);
Int32();
kern ^= seed+seed*17+seed*121+(seed*121/17);
Int32();
kern ^= seed+seed*17+seed*121+(seed*121/17);
Int32();
kern ^= seed+seed*17+seed*121+(seed*121/17);
Int32();
}
sU32 sRandom::Step()
{
const sU32 a = 1103515245;
const sU32 c = 12345;
kern = a*kern+c;
return kern & 0x7fffffff;
}
sU32 sRandom::Int32()
{
sU32 r0,r1;
r0 = Step();
r1 = Step();
return r0 ^ ((r1<<16) | (r1>>16));
}
sInt sRandom::Int(sInt max_)
{
sVERIFY(max_>=1);
sU32 max = sU32(max_-1);
sU32 mask = sMakeMask(max);
sU32 v;
do
{
v = Int32()&mask;
}
while(v>max);
return v;
}
sF32 sRandom::Float(sF32 max)
{
return (0x7fffffff & Int32())*max/(0x8000*65536.0f);
}
/****************************************************************************/
sU32 sRandomMT::Step()
{
if(Index==Count)
{
Reload();
Index = 0;
}
sU32 v = State[Index++];
v ^= (v>>11);
v ^= (v<< 7)&0x9d2c5680UL;
v ^= (v<<15)&0xefc60000UL;
v ^= (v>>18);
return v;
}
static sU32 mtwist(sU32 m,sU32 s0,sU32 s1)
{
return m ^ ((s0&0x80000000)|(s1&0x7fffffff)) ^ (sU32(-sInt(s1&1))&0x9908b0dfUL);
}
void sRandomMT::Reload()
{
sU32 *p = State;
for(sInt i=0;i<Count-Period;i++)
p[i] = mtwist(p[i+Period],p[i+0],p[i+1]);
for(sInt i=Count-Period;i<Count-1;i++)
p[i] = mtwist(p[i+Period-Count],p[i+0],p[i+1]);
p[Count-1] = mtwist(p[Period-1],p[Count-1],State[0]);
}
void sRandomMT::Seed(sU32 seed)
{
for(sInt i=0;i<Count;i++)
{
State[i] = seed;
seed = 1812433253UL*(seed^(seed>>30)+i);
}
Index = Count;
}
sInt sRandomMT::Int(sInt max_)
{
sVERIFY(max_>=1);
sU32 max = sU32(max_-1);
sU32 mask = sMakeMask(max);
sU32 v;
do
{
v = Step()&mask;
}
while(v>max);
return v;
}
sF32 sRandomMT::Float(sF32 max)
{
return Step()*max/(65536.0f*65536.0f);
}
/****************************************************************************/
sU32 sRandomKISS::Step()
{
sU64 t;
sU64 a = 698769069ull;
x = 69069*x+12345;
y ^= y<<13;
y ^= y>>17;
y ^= y<<5;
t = a*z+c;
c = t>>32;
z = (sU32) t;
return x+y+z;
}
void sRandomKISS::Seed(sU32 seed)
{
x = seed + 123456789;
y = seed + 362436000;
if(y==0) y = 362436000;
z = seed + 521288629;
c = 7654321;
}
sF32 sRandomKISS::Float(sF32 max)
{
return Step()*max/(65536.0f*65536.0f);
}
sInt sRandomKISS::Int(sInt max_)
{
sVERIFY(max_>=1);
sU32 max = sU32(max_-1);
sU32 mask = sMakeMask(max);
sU32 v;
do
{
v = Step()&mask;
}
while(v>max);
return v;
}
/****************************************************************************/
sTiming::sTiming()
{
TotalTime = 0;
LastTime = 0;
FirstFrame = 1;
Slices = 0;
TimeSlice = 10;
Delta = 0;
Jitter = 0;
Timestamp = 0;
sClear(History);
HistoryIndex = 0;
HistoryCount = 0;
HistorySkip = 4;
HistoryMax = sCOUNTOF(History);
}
void sTiming::SetTimeSlice(sInt ts)
{
TimeSlice = ts;
}
void sTiming::OnFrame(sInt time)
{
StopMeasure(time);
StartMeasure(time);
}
void sTiming::StartMeasure(sInt time)
{
LastTime = time;
}
void sTiming::StopMeasure(sInt time)
{
if(FirstFrame)
{
LastTime = time;
Timestamp = time;
FirstFrame = 0;
}
Slices = time/TimeSlice - LastTime/TimeSlice;
Jitter = time%TimeSlice;
Delta = time - LastTime;
TotalTime += Delta;
// sDPrintF(L"time %d - last %d delta %d total %d\n",time,LastTime,Delta,TotalTime);
if(HistorySkip==0)
{
History[HistoryIndex] = Delta;
HistoryIndex = (HistoryIndex+1)%HistoryMax;
HistoryCount = sMin((sInt)HistoryCount+1,HistoryMax);
}
else
{
HistorySkip--;
}
}
sF32 sTiming::GetAverageDelta() const
{
sF32 avg = 0;
if(HistoryCount>0)
{
for(sInt i=0;i<HistoryCount;i++)
avg += sF32(History[i]);
avg = avg / HistoryCount;
}
if(avg<0.001f) avg = 0.001f; // at least 1 microsec, to avoid division by zero!
return avg;
}
/****************************************************************************/
/*** ***/
/*** Colors ***/
/*** ***/
/****************************************************************************/
sU32 sMulColor(sU32 a,sU32 b)
{
return ((((a>> 0)&255)*((b>> 0)&255)>>8)<< 0)
+ ((((a>> 8)&255)*((b>> 8)&255)>>8)<< 8)
+ ((((a>>16)&255)*((b>>16)&255)>>8)<<16)
+ ((((a>>24)&255)*((b>>24)&255)>>8)<<24);
}
sU32 sScaleColor(sU32 a,sInt scale)
{
return (((((a>> 0)&255)*scale)>>16)<< 0)
+ (((((a>> 8)&255)*scale)>>16)<< 8)
+ (((((a>>16)&255)*scale)>>16)<<16)
+ (((((a>>24)&255)*scale)>>16)<<24);
}
sU32 sScaleColorFast(sU32 a,sInt scale)
{
sU32 ag = (a&0xff00ff00)>>8;
sU32 rb = a&0x00ff00ff;
sU32 col = ((ag*scale)&0xff00ff00) | (((rb*scale)>>8)&0x00ff00ff);
return col;
}
sU32 sAddColor(sU32 a,sU32 b)
{
return (sClamp<sInt>(((a>> 0)&255)+((b>> 0)&255),0,255)<< 0)
+ (sClamp<sInt>(((a>> 8)&255)+((b>> 8)&255),0,255)<< 8)
+ (sClamp<sInt>(((a>>16)&255)+((b>>16)&255),0,255)<<16)
+ (sClamp<sInt>(((a>>24)&255)+((b>>24)&255),0,255)<<24);
}
sU32 sSubColor(sU32 a,sU32 b)
{
return (sClamp<sInt>(((a>> 0)&255)-((b>> 0)&255),0,255)<< 0)
+ (sClamp<sInt>(((a>> 8)&255)-((b>> 8)&255),0,255)<< 8)
+ (sClamp<sInt>(((a>>16)&255)-((b>>16)&255),0,255)<<16)
+ (sClamp<sInt>(((a>>24)&255)-((b>>24)&255),0,255)<<24);
}
sU32 sMadColor(sU32 mul1,sU32 mul2,sU32 add)
{
return (sClamp<sInt>((((mul1>> 0)&255)*((mul2>> 0)&255)>>8)+((add>> 0)&255),0,255)<< 0)
+ (sClamp<sInt>((((mul1>> 8)&255)*((mul2>> 8)&255)>>8)+((add>> 8)&255),0,255)<< 8)
+ (sClamp<sInt>((((mul1>>16)&255)*((mul2>>16)&255)>>8)+((add>>16)&255),0,255)<<16)
+ (sClamp<sInt>((((mul1>>24)&255)*((mul2>>24)&255)>>8)+((add>>24)&255),0,255)<<24);
}
sU32 sFadeColor(sInt fade,sU32 a,sU32 b)
{
sInt f1 = fade;
sInt f0 = 0x10000-fade;
return (( ((((a>> 0)&255)*f0)>>16) + ((((b>> 0)&255)*f1)>>16) )<< 0)
+ (( ((((a>> 8)&255)*f0)>>16) + ((((b>> 8)&255)*f1)>>16) )<< 8)
+ (( ((((a>>16)&255)*f0)>>16) + ((((b>>16)&255)*f1)>>16) )<<16)
+ (( ((((a>>24)&255)*f0)>>16) + ((((b>>24)&255)*f1)>>16) )<<24);
}
/****************************************************************************/
sU32 sColorFade (sU32 a, sU32 b, sF32 f)
{
sU32 f1 = sU32(f*256);
sU32 f0 = 256-sU32(f*256);
return (sClamp((((a>>24)&0xff)*f0+((b>>24)&0xff)*f1)/256,sU32(0),sU32(255)) << 24) |
(sClamp((((a>>16)&0xff)*f0+((b>>16)&0xff)*f1)/256,sU32(0),sU32(255)) << 16) |
(sClamp((((a>> 8)&0xff)*f0+((b>> 8)&0xff)*f1)/256,sU32(0),sU32(255)) << 8) |
(sClamp((((a>> 0)&0xff)*f0+((b>> 0)&0xff)*f1)/256,sU32(0),sU32(255)) << 0);
}
sU32 sPMAlphaFade (sU32 c, sF32 f)
{
sU32 f0 = sU32(f*255);
return (((((c>> 0)&255)*f0)>>8)<< 0)
+ (((((c>> 8)&255)*f0)>>8)<< 8)
+ (((((c>>16)&255)*f0)>>8)<<16)
+ (((((c>>24)&255)*f0)>>8)<<24);
}
sU32 sAlphaFade (sU32 c, sF32 f)
{
sU32 f0 = sU32(f*256);
return (sClamp((((c >> 24) & 0xff)*f0)/256,sU32(0),sU32(255)) << 24)|(c&0xffffff);
}
sF32 sScaleFade(sF32 a, sF32 b, sF32 f)
{
if (f <= 0.0f)
return a;
if (f >= 1.0f)
return b;
sF32 la = sLog(a);
return sExp(la+(sLog(b) - la)*f);
}
sF32 sDegreeDiff(sF32 alpha, sF32 beta)
{
return sAngleDiff(alpha/360.0f,beta/360.0f) * 360.0f;
}
sF32 sRadianDiff(sF32 alpha, sF32 beta)
{
return sAngleDiff(alpha/sPI2F, beta/sPI2F) * sPI2F;
}
sF32 sAngleDiff(sF32 alpha, sF32 beta)
{
sF32 a=sFrac(alpha);
sF32 b=sFrac(beta);
sF32 r=a-b;
if (r<-0.5f) r+=1;
if (r>0.5f) r-=1;
return r;
}
// a-b, angles=0..c, -count/2 > result > count/2
sInt sAngleDiff(sInt alpha, sInt beta, sInt count)
{
sInt r = alpha - beta;
if (r < -(count>>1)) r += count;
if (r > (count>>1)) r -= count;
return r;
}
/****************************************************************************/
/****************************************************************************/
/*** ***/
/*** Basic Stuff ***/
/*** ***/
/****************************************************************************/
/****************************************************************************/
static sChar ShellParaBuffer[4096];
static sChar *ShellParaBufferEnd;
static sChar *ShellParaVal[64];
static sChar *ShellParaOpt[64];
static sInt ShellParaCount;
/****************************************************************************/
/*** ***/
/*** Commandline Parsing ***/
/*** ***/
/****************************************************************************/
void sParseCmdLine(const sChar *cmd,sBool skipcmd)
{
static sChar strOne[] = L"1";
sChar *d;
const sChar *s;
sChar c;
sInt mode;
sInt i;
// skip first (programm name)
if(skipcmd)
{
while(sIsSpace(*cmd)) cmd++;
if(*cmd=='"')
{
cmd++;
while(*cmd!=0 && *cmd!='"') cmd++;
if(*cmd=='"') cmd++;
}
else
{
while(*cmd && !sIsSpace(*cmd)) cmd++;
}
}
// skip initial whitespace
while(*cmd && sIsSpace(*cmd)) cmd++;
// initialize
ShellParaCount = 0;
s = cmd;
d = ShellParaBuffer;
mode = 0;
for(i=0;i<64;i++)
{
ShellParaVal[i]=0;
ShellParaOpt[i]=0;
}
// scan
while(*s)
{
c=*s++;
switch(mode)
{
case 0: // what's next?
if(!(c==' ' || c=='\t' || c=='\r' || c=='\n'))
{
if(c=='-') // read option word
{
if(ShellParaOpt[ShellParaCount])
{
ShellParaVal[ShellParaCount]=strOne;
ShellParaCount++;
}
ShellParaOpt[ShellParaCount] = d;
mode = 1;
}
else if(c=='"') // read string (for unnamed options)
{
if(ShellParaCount>0 && ShellParaVal[ShellParaCount-1]==0)
ShellParaCount--;
ShellParaVal[ShellParaCount] = d;
mode = 2;
}
#if sPLATFORM != sPLAT_WINDOWS && sPLATFORM != sPLAT_LINUX
// this breaks network path parameters
else if(c=='/' && s[0]=='/') // skip end-of-line comment (for command line files)
{
s++;
while(*s && *s!='\n' && *s!='\r')
s++;
}
#endif
else // read word (for unnamed options)
{
if(ShellParaCount>0 && ShellParaVal[ShellParaCount-1]==0)
ShellParaCount--;
ShellParaVal[ShellParaCount] = d;
*d++ = c;
mode = 1;
}
ShellParaCount++;
}
break;
case 1: // read in word till next whitespace
if(c==' ' || c=='\t' || c=='\r' || c=='\n')
{
*d++ = 0;
mode = 0;
}
else
{
*d++ = c;
}
break;
case 2: // read in string
if(c=='"')
{
*d++ = 0;
mode = 0;
}
else
{
*d++ = c;
}
break;
}
}
*d++ = 0;
if(ShellParaOpt[ShellParaCount])
{
ShellParaVal[ShellParaCount]=strOne;
ShellParaCount++;
}
/*
#if sCOMMANDLINE
if(ShellParaCount>0)
{
for(i=1;i<ShellParaCount;i++)
{
ShellParaVal[i-1] = ShellParaVal[i];
ShellParaOpt[i-1] = ShellParaOpt[i];
}
ShellParaCount--;
}
#endif
*/
if(ShellParaCount>0)
{
sLogPrefixLength = sGetShellParameterInt(L"logprefix", 0, 8);
sLogF(L"sys",L"Shell Parameters\n");
for(i=0;i<ShellParaCount;i++)
{
if(ShellParaOpt[i])
sLogF(L"sys",L" -%s %s\n",ShellParaOpt[i],ShellParaVal[i]);
else
sLogF(L"sys",L" %s\n",ShellParaVal[i]);
}
}
// save the end of the used buffer
ShellParaBufferEnd = d;
sEnableLogFilter();
}
/****************************************************************************/
sBool sAddShellParameter(const sChar *opt, const sChar *val)
{
sBool result = sTRUE;
// get end of ShellParaBuffer
sChar *d = ShellParaBufferEnd;
sDInt spaceLeft = sCOUNTOF(ShellParaBuffer) - (d-ShellParaBuffer);
sDInt neededSpace = sGetStringLen(opt) + sGetStringLen(val) + 2; // 2 trailing zeroes
// is there enough space and entries left
result = (spaceLeft>=neededSpace) && (ShellParaCount < sCOUNTOF(ShellParaVal));
if (result)
{
sInt length;
// copy opt
sCopyString(d, opt, spaceLeft );
ShellParaOpt[ShellParaCount] = d;
length = sGetStringLen(opt) + 1;
d += length;
spaceLeft -= length;
// copy val
sCopyString(d, val, spaceLeft );
ShellParaVal[ShellParaCount] = d;
length = sGetStringLen(val) + 1;
d += length;
spaceLeft -= length;
sLogF(L"sys",L"Additional Shell Parameter: ");
if(ShellParaOpt[ShellParaCount])
sLogF(L"sys",L"-%s %s\n",ShellParaOpt[ShellParaCount],ShellParaVal[ShellParaCount]);
else
sLogF(L"sys",L"%s\n",ShellParaVal[ShellParaCount]);
ShellParaBufferEnd = d;
ShellParaCount++;
}
return result;
}
/****************************************************************************/
const sChar *sGetShellParameter(const sChar *opt,sInt n)
{
sInt i;
for(i=0;i<ShellParaCount;i++)
{
if((opt==0 && ShellParaOpt[i]==0) || (opt && ShellParaOpt[i] && sCmpStringI(opt,ShellParaOpt[i])==0))
{
if(n==0)
return ShellParaVal[i];
n--;
}
}
return 0;
}
/****************************************************************************/
sInt sGetShellParameterInt(const sChar *opt,sInt n,sInt def)
{
const sChar *s = sGetShellParameter(opt,n);
sInt val;
if (s && sScanInt(s, val))
return val;
return def;
}
/****************************************************************************/
sF32 sGetShellParameterFloat(const sChar *opt,sInt n,sF32 def)
{
const sChar *s = sGetShellParameter(opt,n);
sF32 val;
if (s && sScanFloat(s, val))
return val;
return def;
}
/****************************************************************************/
sU32 sGetShellParameterHex(const sChar *opt, sInt n, sU32 def)
{
const sChar *s = sGetShellParameter(opt,n);
sInt val;
if (s && sScanHex(s, val))
return (sU32)val;
return def;
}
/****************************************************************************/
sBool sGetShellSwitch(const sChar *opt)
{
sInt i;
sBool result;
result = sFALSE;
for(i=0;i<ShellParaCount;i++)
{
if(ShellParaOpt[i] && sCmpStringI(opt,ShellParaOpt[i])==0)
{
result = sTRUE;
// ShellParaOpt[i] = 0;
}
}
return result;
}
/****************************************************************************/
const sChar *sGetShellString(const sChar *opt0,const sChar *opt1,const sChar *def)
{
const sChar *str;
if(opt0)
{
str = sGetShellParameter(opt0,0);
if(str) return str;
}
if(opt1)
{
str = sGetShellParameter(opt1,0);
if(str) return str;
}
return def;
}
/****************************************************************************/
sInt sGetShellInt(const sChar *opt0,const sChar *opt1,sInt def)
{
const sChar *str;
if(opt0)
{
str = sGetShellParameter(opt0,0);
if(str)
{
sScanInt(str,def);
return def;
}
}
if(opt1)
{
str = sGetShellParameter(opt1,0);
if(str)
{
sScanInt(str,def);
return def;
}
}
return def;
}
/****************************************************************************/
sFormatStringBuffer& operator% (sFormatStringBuffer &f, const sGUID &guid)
{
if (!*f.Format)
return f;
sFormatStringInfo info;
f.GetInfo(info);
info.Null = 1;
if(info.Format != 'x' && info.Format != 'X')
info.Format = 'x';
f.PrintInt<sU32>(info,guid.Data32,0);
f.Print(L"-");
for (sInt i=0; i<3; i++)
{
f.PrintInt<sU16>(info,guid.Data16[i],0);
f.Print(L"-");
}
for (sInt i=0; i<6; i++)
f.PrintInt<sU8>(info,guid.Data8[i],0);
f.Fill();
return f;
}
sGUID sGetGUIDFromString(const sChar *str)
{
sChecksumMD5 md5;
md5.Calc((const sU8 *)str,2*sGetStringLen(str));
sGUID guid;
guid.Data32=md5.Hash[0];
guid.Data16[0]=md5.Hash[1]>>16;
guid.Data16[1]=md5.Hash[1]&0xffff;
guid.Data16[2]=md5.Hash[2]>>16;
guid.Data8[0]=(md5.Hash[2]>>8)&0xff;
guid.Data8[1]=md5.Hash[2]&0xff;
guid.Data8[2]=md5.Hash[3]>>24;
guid.Data8[3]=(md5.Hash[3]>>16)&0xff;
guid.Data8[4]=(md5.Hash[3]>>8)&0xff;
guid.Data8[5]=md5.Hash[3]&0xff;
return guid;
}
/****************************************************************************/
void sHexDump(const void *data,sInt bytes)
{
const sU32 *ptr = (sU32 *)(sDInt(data)&~3);
for(sInt i=0;i<bytes;i+=32)
{
sDPrintF(L"%08x %08x %08x %08x %08x %08x %08x %08x %08x\n",
sPtr(ptr),ptr[0],ptr[1],ptr[2],ptr[3],ptr[4],ptr[5],ptr[6],ptr[7]);
ptr += 8;
}
}
void sHexByteDump(const void *data,sInt bytes)
{
const sU8 *ptr = (sU8*)data;
for(sInt i=0;i<bytes;i+=8)
{
sDPrintF(L"%08x %02x %02x %02x %02x %02x %02x %02x %02x\n",
sPtr(ptr),ptr[0],ptr[1],ptr[2],ptr[3],ptr[4],ptr[5],ptr[6],ptr[7]);
ptr += 8;
}
}
/****************************************************************************/
sChar sLogFilterPosList[1024];
sChar sLogFilterNegList[1024];
const sChar *sLastLogModule=L"";
void sEnableLogFilter()
{
sInt n = 0;
sChar *ps,*pe;
sChar *ns,*ne;
ps = sLogFilterPosList;
pe = ps+sCOUNTOF(sLogFilterPosList)-2;
ns = sLogFilterNegList;
ne = ns+sCOUNTOF(sLogFilterNegList)-2;
n = 0;
for(;;)
{
const sChar *s = sGetShellParameter(L"log",n);
if(!s) break;
while(sIsSpace(*s)) s++;
while(*s)
{
while(*s && !sIsSpace(*s) && ps<pe)
*ps++ = *s++;
*ps++ = 0;
while(sIsSpace(*s)) s++;
}
n++;
}
*ps++ = 0;
n = 0;
for(;;)
{
const sChar *s = sGetShellParameter(L"dontlog",n);
if(!s) break;
while(sIsSpace(*s)) s++;
while(*s)
{
while(*s && !sIsSpace(*s) && ns<ne)
*ns++ = *s++;
*ns++ = 0;
while(sIsSpace(*s)) s++;
}
n++;
}
*ns++ = 0;
}
sBool sCheckLogFilter(const sChar *module)
{
const sChar *ps = sLogFilterPosList;
const sChar *ns = sLogFilterNegList;
if(*ps)
{
while(*ps)
{
if(sCmpStringI(module,ps)==0) return 1;
while(*ps) ps++;
ps++;
}
return 0;
}
if(*ns)
{
while(*ns)
{
if(sCmpStringI(module,ns)==0)
return 0;
while(*ns) ns++;
ns++;
}
}
return 1;
}
#if sPLATFORM!=sPLAT_WINDOWS
void sPrintError(const sChar *text) { sPrint(text); }
void sPrintWarning(const sChar *text) { sPrint(text); }
#endif
#if sENABLE_DPRINT
static sBool enableLogConsole = sFALSE;
void sEnableLogConsole(sBool enable)
{
enableLogConsole = enable;
}
void sLog(const sChar *module,const sChar *text)
{
const sInt max = 1024;
sString<max> buf;
if(module==0)
module = sLastLogModule;
else
sLastLogModule = module;
if(sCheckLogFilter(module))
{
while(*text)
{
sInt i=0;
if (sLogPrefixLength > 0)
{
buf[i++] = '[';
const sChar *m = module;
while(*m && i<max-4 && i<sLogPrefixLength+1)
buf[i++] = *m++;
buf[i++] = ']';
buf[i++] = ' ';
while (i < sLogPrefixLength + 3) buf[i++] = ' '; // this line can't overflow!
}
while(*text!=0 && *text!='\n' && i<max-2)
buf[i++] = *text++;
if(*text=='\n')
buf[i++]= *text++;
buf[i++] = 0;
sDPrint(buf);
if (enableLogConsole)
sPrint(buf);
}
}
}
#else
void sEnableLogConsole(sBool enable)
{
}
#if !sSTRIPPED
void sLog(const sChar *module,const sChar *text)
{
}
#endif
#endif
/****************************************************************************/
/****************************************************************************/
/*** ***/
/*** Memory Management ***/
/*** ***/
/****************************************************************************/
/****************************************************************************/
sPtr sMemoryUsed;
sPtr sMemoryUsedByMemSystem;
sInt sMemoryInitFlags;
sPtr sMemoryInitSize;
sPtr sMemoryInitDebugSize=16*1024*1024;
struct sMemoryMarkStruct
{
sMemoryMarkStruct()
{
sClear(*this);
}
sU32 Hash; // hash of the memorysituation
sInt Taken; // shows if a hash was taken
sInt Reset; // flags that a reset should be performed
sInt AllocId; // allocId when the first memmark was taken
sInt Id; // this int can be used to determine if this memmark is a special one
};
static sStackArray<sMemoryMarkStruct,3> sMemoryMarks;
static sMemoryMarkStruct sMemoryMark; // the structure to work on
static sBool sMemoryMarkFlushVertexFormat = sFALSE; // flag to flush vertexformats during memmark
static sInt sMemoryAllocId;
static sInt sMemoryBreakId;
static sHooks1<sBool> *sMemFlushHook;
static sMemoryLeakTracker *sMemoryLeaks;
static sBool sMemoryLeakCheck;
static sMemoryHandler *sMemoryHandlers[sAMF_MASK+1];
static sInt sMemoryHandlerMax;
sInt sOutOfMemory=0; // set to heap id upon out of memory condition
#if !sSTRIPPED
static sF32 sMemFailProbability = 0;
static sRandom sMemFailRandom;
void sSetAllocFailTest(sF32 prob) { sMemFailProbability=prob; }
sF32 sGetAllocFailTest() { return sMemFailProbability; }
#else
void sSetAllocFailTest(sF32) {}
sF32 sGetAllocFailTest() { return 0.0f; }
#endif
/****************************************************************************/
sMemoryHandler::sMemoryHandler()
{
Start = 0;
End = 0;
Owner = 0;
ThreadSafe = 0;
IncludeInSnapshot = 0;
MayFail = 0;
NoLeaks = 0;
Lock_ = 0;
MinTotalFree = 0;
}
sMemoryHandler::~sMemoryHandler()
{
sMemoryHandler::MakeThreadUnsafe();
}
void sMemoryHandler::MakeThreadSafe()
{
if(!Lock_)
{
Lock_ = new sThreadLock;
ThreadSafe = 1;
Owner = 0;
}
}
void sMemoryHandler::MakeThreadUnsafe()
{
if(Lock_)
{
sThreadLock *l= Lock_;
l->Lock();
Owner = sGetThreadContext();
Lock_ = 0;
ThreadSafe = 0;
l->Unlock();
delete l;
}
}
void sMemoryHandler::Lock()
{
if(ThreadSafe)
Lock_->Lock();
}
sBool sMemoryHandler::TryLock()
{
if(ThreadSafe)
return Lock_->TryLock();
return sTRUE;
}
void sMemoryHandler::Unlock()
{
if(ThreadSafe)
Lock_->Unlock();
}
/****************************************************************************/
struct sDbgMemRange
{
sDbgMemRange() { Start=0; End=0; }
sDbgMemRange(sPtr start, sPtr end) { Start=start; End=end; }
sPtr Start;
sPtr End;
};
static sThreadLock *sDbgMemRangesLock=0;
static sStaticArray<sDbgMemRange> *sDbgMemRanges=0;
void sMemDbgLock(sPtr start, sPtr size)
{
#if sCFG_MEMDBG_LOCKING
if(sDbgMemRangesLock)
{
sScopeLock sl(sDbgMemRangesLock);
sDbgMemRanges->AddTail(sDbgMemRange(start,start+size));
}
#endif
}
void sMemDbgUnlock(sPtr start, sPtr size)
{
#if sCFG_MEMDBG_LOCKING
if(sDbgMemRangesLock)
{
sScopeLock sl(sDbgMemRangesLock);
sDbgMemRange *range;
sFORALL(*sDbgMemRanges,range)
{
if(range->Start==start&&range->End==start+size)
{
sDbgMemRanges->RemAt(_i);
return;
}
}
}
#endif
}
sBool sMemDbgCheckLock(void *ptr_)
{
#if sCFG_MEMDBG_LOCKING
if(sDbgMemRangesLock)
{
sScopeLock sl(sDbgMemRangesLock);
sDbgMemRange *range;
sPtr ptr = (sPtr)ptr_;
sFORALL(*sDbgMemRanges,range)
{
if(ptr>=range->Start&&ptr<range->End)
return sTRUE;
}
}
#endif
return sFALSE;
}
/****************************************************************************/
void sInitMem0()
{
sMemoryLeakCheck = 0;
sMemoryAllocId = 1;
sMemoryBreakId = 0;
sMemoryHandlerMax = 0;
sInitMem1();
sMemoryUsedByMemSystem = sMemoryUsed;
if(sMemoryHandlers[sAMF_DEBUG])
sMemoryHandlers[sAMF_DEBUG]->MakeThreadSafe();
if(sCONFIG_DEBUGMEM && (sIsMemTypeAvailable(sAMF_DEBUG) || (sPLATFORM==sPLAT_WINDOWS || sPLATFORM==sPLAT_LINUX)))
{
if(!(sMemoryInitFlags & sIMF_NOLEAKTRACK))
{
sLogF(L"mem",L"initialize memory leak tracker\n");
sInt memtype=sIsMemTypeAvailable(sAMF_DEBUG) ? sAMF_DEBUG : sAMF_HEAP;
sPushMemType(memtype);
sMemoryLeaks = new sMemoryLeakTracker;
sMemoryLeaks->Init2(memtype);
sPopMemType(memtype);
sMemoryLeakCheck = 1;
}
}
sMemFlushHook = new sHooks1<sBool>;
sDumpMemoryMap();
#if sCFG_MEMDBG_LOCKING
sDbgMemRangesLock = new sThreadLock;
sDbgMemRanges = new sStaticArray<sDbgMemRange>;
sDbgMemRanges->HintSize(1024);
#endif
}
void sDumpMemoryMap()
{
sLogF(L"mem",L"MEMORY MAP:\n");
for(sInt i=0;i<sAMF_MASK+1;i++)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h)
sLogF(L"mem",L"%02d: %08x..%08x (%Kb)\n",i,h->Start,h->End,(sU64)h->GetSize());
}
}
void sExitMem0()
{
#if sCFG_MEMDBG_LOCKING
sDelete(sDbgMemRangesLock);
sDelete(sDbgMemRanges);
#endif
sPartitionMemory(0,0,0);
for(sInt i=1;i<=sMemoryHandlerMax;i++)
if(sMemoryHandlers[i])
sMemoryHandlers[i]->sMemoryHandler::MakeThreadUnsafe();
delete sMemFlushHook;
if(sMemoryLeakCheck && sMemoryLeaks->HasLeak())
{
sDPrintF(L"/****************************************************************************/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/*** Memory Leak Detected. ***/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/****************************************************************************/\n");
sMemoryLeaks->DumpLeaks(L"memory leaks",0);
sMemoryLeakCheck = 0;
sMemoryLeaks->Exit();
sDelete(sMemoryLeaks);
sLogF(L"mem",L"%K bytes leaked\n",sMemoryUsed-sMemoryUsedByMemSystem);
}
else
{
if(sMemoryLeakCheck)
{
sMemoryLeakCheck = 0;
sMemoryLeaks->Exit();
sDelete(sMemoryLeaks);
}
if(sMemoryUsed-sMemoryUsedByMemSystem!=0)
{
sDPrintF(L"/****************************************************************************/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/*** Memory Leak Detected. ***/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/****************************************************************************/\n");
sLogF(L"mem",L"%K bytes leaked\n",sMemoryUsed-sMemoryUsedByMemSystem);
}
}
sExitMem1();
}
void sRegisterMemHandler(sInt slot,sMemoryHandler *h)
{
sVERIFYRELEASE(sMemoryHandlers[slot]==0);
if (h&&!h->IsThreadSafe())
h->Owner = sGetThreadContext(); // threadsafe memory handlers don't have an owner
sMemoryHandlers[slot] = h;
sMemoryHandlerMax = sMax(sMemoryHandlerMax,slot);
}
void sUnregisterMemHandler(sInt slot)
{
if (sMemoryHandlers[slot])
sMemoryHandlers[slot]->Owner = 0;
sMemoryHandlers[slot] = 0;
for (sInt i=slot-1; i>=0; i--)
if (sMemoryHandlers[i])
{
sMemoryHandlerMax=i;
break;
}
}
sMemoryHandler *sGetMemHandler(sInt slot)
{
if(slot>=0&&slot<sCOUNTOF(sMemoryHandlers))
return sMemoryHandlers[slot];
return 0;
}
sBool sIsMemTypeAvailable(sInt t)
{
return sMemoryHandlers[t&sAMF_MASK]!=0;
}
sCONFIG_SIZET sGetFreeMem(sInt type)
{
type&=sAMF_MASK;
if (type==sAMF_DEFAULT)
{
sThreadContext *tx = sGetThreadContext();
type=tx->MemTypeStack[tx->MemTypeStackIndex];
}
sMemoryHandler *h=sMemoryHandlers[type&sAMF_MASK];
if (!h) return 0;
h->Lock();
sCONFIG_SIZET free=h->GetFree();
h->Unlock();
return free;
}
void sPushMemType(sInt t)
{
sThreadContext *tx = sGetThreadContext();
tx->MemTypeStackIndex++;
sVERIFY(tx->MemTypeStackIndex<sCOUNTOF(tx->MemTypeStack));
tx->MemTypeStack[tx->MemTypeStackIndex] = t;
}
void sPopMemType(sInt t)
{
sThreadContext *tx = sGetThreadContext();
sVERIFY(tx->MemTypeStack[tx->MemTypeStackIndex] == t);
tx->MemTypeStackIndex--;
sVERIFY(tx->MemTypeStackIndex>=0);
}
void *sAllocMem_(sPtr size,sInt align,sInt flags)
{
if(sMemoryBreakId == sMemoryAllocId)
sDEBUGBREAK;
sThreadContext *tx = sGetThreadContext();
#if sCONFIG_DEBUGMEM
tx->TagLastFile = tx->TagMemFile;
#endif
void *p = 0;
if((flags & sAMF_MASK)==0)
{
flags |= tx->MemTypeStack[tx->MemTypeStackIndex];
}
sMemoryHandler *h = sMemoryHandlers[flags & sAMF_MASK];
sVERIFY(h);
sVERIFY(h->Owner==0 || h->Owner==tx);
#if !sSTRIPPED
// random fail test
if (h->MayFail && sMemFailRandom.Float(1.0f)<sMemFailProbability)
{
#if sCONFIG_DEBUGMEM
tx->TagMemFile = 0;
#endif
return 0;
}
#endif
h->Lock();
p = h->Alloc(size,align,flags);
h->Unlock();
if(!p)
{
if(h->MayFail || (flags & sAMF_MAYFAIL))
{
#if sCONFIG_DEBUGMEM
tx->TagMemFile = 0;
#endif
return 0;
}
else
{
sOutOfMemory=flags&sAMF_MASK;
#if sCONFIG_DEBUGMEM
const char *tagfile=tx->TagMemFile;
const sInt tagline=tx->TagMemLine;
if (sMemoryLeakCheck && sOutOfMemory!=sAMF_DEBUG) sMemoryLeaks->DumpLeaks(L"",0,sOutOfMemory);
#endif
h->Lock();
sCONFIG_SIZET hsize=h->GetSize();
sCONFIG_SIZET free=h->GetFree();
sCONFIG_SIZET largest=h->GetLargestFree();
h->Unlock();
#if sCONFIG_DEBUGMEM
if (tagfile)
{
static sChar oomfile[1024];
sCopyString(oomfile,tagfile,1023);
sFatal(L"%s(%d): out of mem: tried %K, align %d, flags %08x\n(free: %K of %K, largest: %K)",oomfile,tagline,size,align,flags,(sU64)free,(sU64)hsize,(sU64)largest);
}
else
#endif
sFatal(L"out of mem - tried %K, align %d, flags %08x\n(free: %K of %K, largest: %K)",size,align,flags,(sU64)free,(sU64)hsize,(sU64)largest);
}
}
#if sCONFIG_DEBUGMEM
if(sMemoryLeakCheck && !(flags & sAMF_NOLEAK) && !h->NoLeaks)
sMemoryLeaks->AddLeak(p,size,tx->TagMemFile,tx->TagMemLine,sMemoryAllocId,flags&(sAMF_MASK|sAMF_ALT),tx->MemLeakDescBuffer2,tx->MLDBCRC);
tx->TagMemFile = 0;
#endif
sMemoryAllocId++;
return p;
}
void sFreeMem(void *ptr)
{
if(ptr)
{
sThreadContext *tx = sGetThreadContext();
sMemoryHandler *h=0;
sPtr p = sPtr(ptr);
for(sInt i=sMemoryHandlerMax;i>=1;i--)
{
h = sMemoryHandlers[i];
if(h)
{
if(p>=h->Start && p<h->End)
{
sVERIFY(h->Owner==0 || h->Owner==tx);
h->Lock();
sBool r= h->Free(ptr);
h->Unlock();
if (r) break;
}
}
h=0;
}
if (!h) for(sInt i=sMemoryHandlerMax;i>=1;i--)
{
h = sMemoryHandlers[i];
if(h && h->End==0)
{
sVERIFY(h->Owner==0 || h->Owner==tx);
h->Lock();
sBool r= h->Free(ptr);
h->Unlock();
if(r) break;
}
h=0;
}
if (!h)
sFatal(L"pointer %08x seems not to belong to any sMemoryHandler",p);
#if sCONFIG_DEBUGMEM
if(sMemoryLeakCheck && !h->NoLeaks)
sMemoryLeaks->RemLeak(ptr);
#endif
}
}
sPtr sMemSize(void *ptr)
{
if(ptr)
{
sThreadContext *tx = sGetThreadContext();
sPtr p = sPtr(ptr);
for(sInt i=sMemoryHandlerMax;i>=1;i--)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h)
{
if(p>=h->Start && p<h->End)
{
sVERIFY(h->Owner==0 || h->Owner==tx);
h->Lock();
sPtr r= h->MemSize(ptr);
h->Unlock();
return r;
}
}
}
for(sInt i=sMemoryHandlerMax;i>=1;i--)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h && h->End==0)
{
sVERIFY(h->Owner==0 || h->Owner==tx);
h->Lock();
sPtr r= h->MemSize(ptr);
h->Unlock();
return r;
}
}
sFatal(L"pointer %08x seems not to belong to any sMemoryHandler",p);
}
return 0;
}
void sCheckMem_()
{
sThreadContext *tx = sGetThreadContext();
for(sInt i=1;i<=sMemoryHandlerMax;i++)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h && (h->Owner==0 || h->Owner==tx))
{
h->Lock();
h->Validate();
h->Unlock();
}
}
}
void sResetMemChecksum()
{
sLog(L"mem",L"WARNING! resetting memory checksum!\n");
sMemoryMark.Reset = 1;
}
void sMemMark(sBool fatal/*=sTRUE*/)
{
sU32 hash;
sMemFlushHook->Call(sMemoryMarkFlushVertexFormat);
sMemoryMarkFlushVertexFormat = sTRUE;
// calculate hash over all handlers, excluding debug
sThreadContext *tx = sGetThreadContext();
hash = 0;
for(sInt i=1;i<=sMemoryHandlerMax;i++)
{
if(i!=sAMF_DEBUG)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h&&h->IncludeInSnapshot)
{
sVERIFY(h->Owner==0 || h->Owner==tx);
h->Lock();
hash ^= h->MakeSnapshot();
h->Unlock();
}
}
}
sLogF(L"mem",L"Memory Hash is %08x\n",hash);
// check
if(sMemoryMark.Reset)
{
sMemoryMark.Taken = 0;
sMemoryMark.Reset = 0;
}
if(sMemoryMark.Taken)
{
if(sMemoryMark.Hash!=hash)
{
sLogF(L"mem",L"Memory Hash has Changed %08x -> %08x\n",sMemoryMark.Hash,hash);
sDPrintF(L"/****************************************************************************/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/*** Memory Leak Detected. ***/\n");
sDPrintF(L"/*** ***/\n");
sDPrintF(L"/****************************************************************************/\n");
#if sCONFIG_DEBUGMEM
if (sMemoryLeakCheck)
{
for(sInt i=1;i<=sMemoryHandlerMax;i++)
{
sMemoryHandler *h = sMemoryHandlers[i];
if(h && h->IncludeInSnapshot && i!=sAMF_DEBUG)
{
sString<64> str;
sSPrintF(str,L"heap 0x%2x, leaks since first sMemMark()",i);
sMemoryLeaks->DumpLeaks(str,sMemoryMark.AllocId,i);
}
}
}
#endif
if(fatal) sFatal(L"memory leaks are fatal.\n");
}
}
else // or save state
{
sMemoryMark.Hash = hash; // this may be called more than once
if(sMemoryMark.AllocId==0) // but we set the markallocid only once, otherwise we mess up the MemMarkCallback
sMemoryMark.AllocId = sMemoryAllocId;
}
sMemoryMark.Taken = 1;
}
sBool sIsMemMarkSet() // this will return true even when we are in reset memmark mode!
{
return sMemoryMark.AllocId>0;
}
void sAddMemMarkCallback(void (*cb)(sBool flush,void *user),void *user)
{
//if (!sMemStackActive) return;
sMemFlushHook->Add(cb,user);
}
void sBreakOnAllocation(sInt n)
{
sMemoryBreakId = n;
}
sInt sGetMemoryAllocId()
{
return sMemoryAllocId;
}
void sMemMarkPush(sInt id)
{
sMemoryMarks.AddTail(sMemoryMark);
if (id!=-1)
sMemoryMark.Id = id;
}
void sMemMarkPop()
{
if (sMemoryMarks.GetCount())
{
sMemoryMarkStruct stored = sMemoryMark;
sMemoryMark = sMemoryMarks.RemTail();
sMemoryMark.AllocId = stored.AllocId;
sMemoryMark.Reset = stored.Reset;
}
}
sInt sMemMarkGetId()
{
return sMemoryMark.Id;
}
#if sCONFIG_DEBUGMEM
void sTagMem(const char *file,sInt line)
{
sThreadContext *tx=sGetThreadContext();
if(!tx->TagMemFile) { tx->TagMemFile=file; tx->TagMemLine=line; }
}
void sTagMemLast()
{
sThreadContext *tx=sGetThreadContext();
tx->TagMemFile = tx->TagLastFile;
}
#endif
sMemoryLeakTracker *sGetMemoryLeakTracker()
{
if (!sSTRIPPED && sMemoryLeakCheck)
return sMemoryLeaks;
else
return 0;
}
/****************************************************************************/
/*** ***/
/*** Memory Partitioning ***/
/*** ***/
/****************************************************************************/
static sBool sFrameMemDoubleBuffered = sFALSE;
sPtr sMemFrameSize;
sPtr sMemFrameLastUsed;
static sPtr sMemFramePtr[2]={0,0};
static sPtr sMemFrameUsed;
static sInt sMemFrameToggle;
static sPtr sMemFrameWasted;
static sPtr sMemDmaSize;
static sPtr sMemDmaPtr[2]={0,0};
static sPtr sMemDmaUsed;
static sInt sMemDmaToggle;
static sPtr sMemDmaWasted;
static sU64 sMemFlipFrame;
sDInt sMemStatMaxFrameUsed=0;
sDInt sMemStatMaxDMAUsed=0;
void sRender3DFlush();
void sPartitionMemory(sPtr frame,sPtr dma,sPtr gfx)
{
// delete old
if(sMemFrameSize!=frame && frame!=1)
{
sFreeMem((void*)sMemFramePtr[0]);
sMemFramePtr[0] = 0;
sMemFrameSize = 0;
if(sFrameMemDoubleBuffered)
{
sFreeMem((void*)sMemFramePtr[1]);
sMemFramePtr[1] = 0;
}
}
if(sMemDmaSize!=dma && dma!=1)
{
sRender3DFlush();
sMemDmaSize = 0;
sFreeMem((void*)sMemDmaPtr[0]); sMemDmaPtr[0] = 0;
sFreeMem((void*)sMemDmaPtr[1]); sMemDmaPtr[1] = 0;
}
// alloc new
sInitMem2(gfx);
if(!sMemFramePtr[0] && frame>=2)
{
sPushMemLeakDesc(L"FrameMemory");
sMemFrameSize = frame;
sMemFramePtr[0] = (sPtr)sAllocMem(sMemFrameSize,16,sAMF_HEAP);
if(sFrameMemDoubleBuffered)
sMemFramePtr[1] = (sPtr)sAllocMem(sMemFrameSize,16,sAMF_HEAP);
sMemFrameToggle = 0;
sPopMemLeakDesc();
}
if(!sMemDmaPtr[0] && dma>=2)
{
sPushMemLeakDesc(L"DMAMemory");
sMemDmaSize = dma;
sMemDmaPtr[0] = (sPtr)sAllocMem(sMemDmaSize,4096,sAMF_GFX);
sMemDmaPtr[1] = (sPtr)sAllocMem(sMemDmaSize,4096,sAMF_GFX);
sMemDmaToggle = 0;
sPopMemLeakDesc();
}
// set Used ptrs.
sFlipMem();
sMemFrameLastUsed=0;
sMemStatMaxFrameUsed=0;
sMemStatMaxDMAUsed=0;
}
void sFrameMemDoubleBuffer(sBool enable)
{
if(sFrameMemDoubleBuffered!=enable)
{
if(sFrameMemDoubleBuffered)
{
sFreeMem((void*)sMemFramePtr[1]);
sMemFramePtr[1] = 0;
}
else
sMemFramePtr[1] = (sPtr)sAllocMem(sMemFrameSize,16,sAMF_HEAP);
sMemFrameToggle = 0;
sFrameMemDoubleBuffered=enable;
sFlipMem();
}
}
/****************************************************************************/
#if !sCONFIG_FRAMEMEM_MT
// single-threaded implementations
sBool sRender3DLockOwner();
void *sAllocFrame(sPtr size,sInt align)
{
sVERIFY(sRender3DLockOwner());
if(sMemFrameUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate frame memory");
size = sAlign(size,align);
sMemFrameUsed = sAlign(sMemFrameUsed,align);
sMemFrameUsed += size;
if(sMemFrameUsed > sMemFramePtr[sMemFrameToggle]+sMemFrameSize)
sFatal(L"out of frame mem");
return (void*)(sMemFrameUsed-size);
}
void *sAllocFrameBegin(sInt size,sInt align)
{
sVERIFY(sRender3DLockOwner());
if(sMemFrameUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate frame memory");
size = sAlign(size,align);
sMemFrameUsed = sAlign(sMemFrameUsed,align);
if((sMemFrameUsed+size) > sMemFramePtr[sMemFrameToggle]+sMemFrameSize)
sFatal(L"out of frame mem");
return (void*)(sMemFrameUsed);
}
void sAllocFrameEnd(void *ptr)
{
sVERIFY(sRender3DLockOwner());
sPtr end = (sPtr) ptr;
if (end<sMemFrameUsed || end>sMemFramePtr[sMemFrameToggle]+sMemFrameSize)
sFatal(L"sAllocFrameEnd: illegal pointer");
sMemFrameUsed = end;
}
#else
/****************************************************************************/
#define sCFG_ALLOCFRAME_FAST 0 // multithread safe but may waste more memory then the slow version
// generally shouldn't be an issue for allocframe (most of the time small allocs with 16 byte alignment)
static void *sAllocFrameBeginImpl(sThreadContext *ctx,sInt size,sInt align)
{
retry:
// we remember up to two borrowed memory segments, enough free memory in one of them?
sBool fit = ctx->FrameCurrent ? sAlign(ctx->FrameCurrent,align)+size<=ctx->FrameEnd : 0;
sBool fitalt = ctx->FrameAltCurrent ? sAlign(ctx->FrameAltCurrent,align)+size<=ctx->FrameAltEnd : 0;
if(fit)
{
ctx->FrameCurrent = sAlign(ctx->FrameCurrent,align);
}
else if(fitalt)
{
sSwap(ctx->FrameCurrent,ctx->FrameAltCurrent);
sSwap(ctx->FrameEnd,ctx->FrameAltEnd);
ctx->FrameCurrent = sAlign(ctx->FrameCurrent,align);
}
else // none fits
{
sPtr free = ctx->FrameCurrent ? ctx->FrameEnd-ctx->FrameCurrent : 0;
sPtr freealt = ctx->FrameAltCurrent ? ctx->FrameAltEnd-ctx->FrameAltCurrent : 0;
if(freealt<free) // move larger chunk to alt memory
{
sSwap(ctx->FrameCurrent,ctx->FrameAltCurrent);
sSwap(ctx->FrameEnd,ctx->FrameAltEnd);
}
// allocate new segment
sPtr old_fc = 0;
sPtr old_fe = 0;
sSwap(old_fc,ctx->FrameCurrent);
sSwap(old_fe,ctx->FrameEnd);
sInt size2 = sAlign(sMax(size+align,sMin(256*1024,size*2+align)),16); // try to borrow up to size*2+align memory but at least requested size+align
ctx->FrameCurrent = (sPtr) sAllocFrame(size2,16);
ctx->FrameEnd = ctx->FrameCurrent + size2;
// if possible merged new segment with existing ones
if(old_fe==ctx->FrameCurrent)
{
ctx->FrameCurrent = old_fc;
goto retry;
}
else if(ctx->FrameAltEnd==ctx->FrameCurrent)
{
ctx->FrameAltEnd = ctx->FrameEnd;
ctx->FrameCurrent = old_fc;
ctx->FrameEnd = old_fe;
goto retry;
}
// can not be merged, we have wasted some memory
sAtomicAdd(&sMemFrameWasted,old_fe-old_fc);
ctx->FrameCurrent = sAlign(ctx->FrameCurrent,align);
}
ctx->FrameBorrow = ctx->FrameCurrent + size;
sVERIFY(ctx->FrameBorrow <= ctx->FrameEnd);
return (void *) ctx->FrameCurrent;
}
static sINLINE void sAllocFrameEndImpl(sThreadContext *ctx,void *ptr)
{
if(ctx->FrameBorrow==0) sFatal(L"sAllocFrameEnd without sAllocFrameBegin");
sPtr end = (sPtr) ptr;
sVERIFY(end <= ctx->FrameBorrow);
ctx->FrameCurrent = end;
ctx->FrameBorrow = 0;
if(ctx->FrameCurrent==ctx->FrameEnd)
{
// borrowed memory completely consumed, reset segment and swap with alt
ctx->FrameCurrent = 0;
ctx->FrameEnd = 0;
sSwap(ctx->FrameCurrent,ctx->FrameAltCurrent);
sSwap(ctx->FrameEnd,ctx->FrameAltEnd);
}
}
#if sCFG_ALLOCFRAME_FAST
sINLINE void *sAllocFrame(sPtr size,sInt align/*=16*/)
{
// multithread safe but may waste more memory then the slow version
// generally shouldn't be an issue for allocframe (most of the time small allocs with 16 byte alignment)
if(sMemFrameUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate frame memory");
size = sAlign(size,16);
if(align>16)
size += sAlign(align,16);
sPtr end = sAtomicAdd(&sMemFrameUsed,size);
sWriteBarrier();
if(end > sMemFramePtr[sMemFrameToggle]+sMemFrameSize)
sFatal(L"out of frame mem");
sPtr result = sAlign(end-size,align);
return (void *) result;
}
#else
void *sAllocFrame(sPtr size,sInt align)
{
if(sMemFrameUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate frame memory");
sPtr result = 0;
sThreadContext *ctx = sGetThreadContext();
if(ctx->FrameFrame!=sMemFlipFrame)
{
// first alloc frame this frame, reset state
ctx->FrameFrame = sMemFlipFrame;
ctx->FrameBorrow = 0;
ctx->FrameCurrent = ctx->FrameEnd = 0;
ctx->FrameAltCurrent = ctx->FrameAltEnd =0;
}
if(ctx->FrameBorrow) sFatal(L"can't call sAllocFrame() between sAllocFrameBegin() and sAllocFrameEnd()");
if(ctx->FrameCurrent || align!=16)
{
if(align==16)
{
sBool fit = ctx->FrameCurrent ? sAlign(ctx->FrameCurrent,align)+size<=ctx->FrameEnd : 0;
sBool fitalt = ctx->FrameAltCurrent ? sAlign(ctx->FrameAltCurrent,align)+size<=ctx->FrameAltEnd : 0;
if(!fit&&!fitalt)
goto nonborrowed; // don't discard borrowed segments for non-fitting allocation with native alignment
}
// we have or want borrowed memory, use it
result = (sPtr) sAllocFrameBeginImpl(ctx,size,align);
sAllocFrameEndImpl(ctx,(void *)(result+size));
}
else
{
nonborrowed:
// no borrowed memory and native alignment
size = sAlign(size,16);
sPtr end = sAtomicAdd(&sMemFrameUsed,size);
sWriteBarrier();
if(end > sMemFramePtr[sMemFrameToggle]+sMemFrameSize)
sFatal(L"out of frame mem");
result = end-size;
}
return (void *) result;
}
#endif // sCFG_ALLOCFRAME_FAST
void *sAllocFrameBegin(sInt size,sInt align)
{
sThreadContext *ctx = sGetThreadContext();
if(ctx->FrameFrame!=sMemFlipFrame)
{
// first alloc frame this frame, reset state
ctx->FrameFrame = sMemFlipFrame;
ctx->FrameBorrow = 0;
ctx->FrameCurrent = ctx->FrameEnd = 0;
ctx->FrameAltCurrent = ctx->FrameAltEnd =0;
}
if(ctx->FrameBorrow) sFatal(L"can't call sAllocFrameBegin() between sAllocFrameBegin() and sAllocFrameEnd()");
return sAllocFrameBeginImpl(ctx,size,align);
}
void sAllocFrameEnd(void *ptr)
{
return sAllocFrameEndImpl(sGetThreadContext(),ptr);
}
#endif
/****************************************************************************/
sBool sIsFrameMem(void *ptr)
{
sPtr p=(sPtr)ptr;
return p>=sMemFramePtr[sMemFrameToggle] && p<(sMemFramePtr[sMemFrameToggle]+sMemFrameSize);
}
sBool sIsDmaMem(void *ptr)
{
sPtr p=(sPtr)ptr;
return p>=sMemDmaPtr[sMemDmaToggle] && p<(sMemDmaPtr[sMemFrameToggle]+sMemDmaSize);
}
/****************************************************************************/
void *sAllocDma(sPtr size,sInt align)
{
if(sMemDmaUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate dma memory");
sPtr result = 0;
sThreadContext *ctx = sGetThreadContext();
if(ctx->DmaFrame!=sMemFlipFrame)
{
// first alloc dma this frame, reset state
ctx->DmaFrame = sMemFlipFrame;
ctx->DmaBorrow = 0;
ctx->DmaCurrent = ctx->DmaEnd = 0;
ctx->DmaAltCurrent = ctx->DmaAltEnd =0;
}
if(ctx->DmaBorrow) sFatal(L"can't call sAllocDma() between sAllocDmaBegin() and sAllocDmaEnd()");
if(ctx->DmaCurrent || align!=16)
{
if(align==16)
{
sBool fit = ctx->DmaCurrent ? sAlign(ctx->DmaCurrent,align)+size<=ctx->DmaEnd : 0;
sBool fitalt = ctx->DmaAltCurrent ? sAlign(ctx->DmaAltCurrent,align)+size<=ctx->DmaAltEnd : 0;
if(!fit&&!fitalt)
goto nonborrowed; // don't discard borrowed segments for non-fitting allocation with native alignment
}
// we have or want borrowed memory, use it
result = (sPtr) sAllocDmaBegin(size,align);
sAllocDmaEnd((void *)(result+size));
}
else
{
nonborrowed:
// no borrowed memory and native alignment
size = sAlign(size,16);
sPtr end = sAtomicAdd(&sMemDmaUsed,size);
sWriteBarrier();
if(end > sMemDmaPtr[sMemDmaToggle]+sMemDmaSize)
{
sFatal(L"out of dma mem");
}
result = end-size;
}
return (void *) result;
}
sU32 sAllocDmaEstimateAvailable (void)
//////////////////////////////////////
// Returns an estimate of the available DMA memory.
// This function underestimates in case some of the memory has been borrowed but will
// never overestimate. At least that's what Dierk said.
{
if(sMemDmaUsed==0)
sFatal(L"please use sPartitionMemory() in sMain() to allocate dma memory");
return (sMemDmaPtr[sMemDmaToggle]+sMemDmaSize) - sMemDmaUsed;
}
sU32 sDMAMemSize(void)
{ return sMemDmaSize;
}
void *sAllocDmaBegin(sInt size, sInt align/*=16*/)
{
sThreadContext *ctx = sGetThreadContext();
if(ctx->DmaFrame!=sMemFlipFrame)
{
// first alloc frame this frame, reset state
ctx->DmaFrame = sMemFlipFrame;
ctx->DmaBorrow = 0;
ctx->DmaCurrent = ctx->DmaEnd = 0;
ctx->DmaAltCurrent = ctx->DmaAltEnd =0;
}
if(ctx->DmaBorrow) sFatal(L"can't call sAllocDmaBegin() between sAllocDmaBegin() and sAllocDmaEnd()");
retry:
// we remember up to two borrowed memory segments, enough free memory in one of them?
sBool fit = ctx->DmaCurrent ? sAlign(ctx->DmaCurrent,align)+size<=ctx->DmaEnd : 0;
sBool fitalt = ctx->DmaAltCurrent ? sAlign(ctx->DmaAltCurrent,align)+size<=ctx->DmaAltEnd : 0;
if(fit)
{
ctx->DmaCurrent = sAlign(ctx->DmaCurrent,align);
}
else if(fitalt)
{
sSwap(ctx->DmaCurrent,ctx->DmaAltCurrent);
sSwap(ctx->DmaEnd,ctx->DmaAltEnd);
ctx->DmaCurrent = sAlign(ctx->DmaCurrent,align);
}
else // none fits
{
sPtr free = ctx->DmaCurrent ? ctx->DmaEnd-ctx->DmaCurrent : 0;
sPtr freealt = ctx->DmaAltCurrent ? ctx->DmaAltEnd-ctx->DmaAltCurrent : 0;
if(freealt<free) // move larger chunk to alt memory
{
sSwap(ctx->DmaCurrent,ctx->DmaAltCurrent);
sSwap(ctx->DmaEnd,ctx->DmaAltEnd);
}
// allocate new segment
sPtr old_dc = 0;
sPtr old_de = 0;
sSwap(old_dc,ctx->DmaCurrent);
sSwap(old_de,ctx->DmaEnd);
sInt size2 = sAlign(sMax(size+align,sMin(256*1024,size*2+align)),16); // try to borrow up to size*2+align memory but at least requested size+align
ctx->DmaCurrent = (sPtr) sAllocDma(size2,16);
ctx->DmaEnd = ctx->DmaCurrent + size2;
// if possible merged new segment with existing ones
if(old_de==ctx->DmaCurrent)
{
ctx->DmaCurrent = old_dc;
goto retry;
}
else if(ctx->DmaAltEnd==ctx->DmaCurrent)
{
ctx->DmaAltEnd = ctx->DmaEnd;
ctx->DmaCurrent = old_dc;
ctx->DmaEnd = old_de;
goto retry;
}
// can not be merged, we have wasted some memory
sAtomicAdd(&sMemDmaWasted,old_de-old_dc);
ctx->DmaCurrent = sAlign(ctx->DmaCurrent,align);
}
ctx->DmaBorrow = ctx->DmaCurrent + size;
sVERIFY(ctx->DmaBorrow <= ctx->DmaEnd);
sVERIFY((ctx->DmaCurrent & (align-1))==0);
return (void *) ctx->DmaCurrent;
}
void sAllocDmaEnd(void *ptr)
{
sThreadContext *ctx = sGetThreadContext();
if(ctx->DmaBorrow==0) sFatal(L"sAllocDmaEnd without sAllocDmaBegin");
sPtr end = (sPtr) ptr;
sVERIFY(end <= ctx->DmaBorrow);
ctx->DmaCurrent = end;
ctx->DmaBorrow = 0;
if(ctx->DmaCurrent==ctx->DmaEnd)
{
// borrowed memory completely consumed, reset segment and swap with alt
ctx->DmaCurrent = 0;
ctx->DmaEnd = 0;
sSwap(ctx->DmaCurrent,ctx->DmaAltCurrent);
sSwap(ctx->DmaEnd,ctx->DmaAltEnd);
}
}
/****************************************************************************/
void sFlipMem()
{
if (sMemFrameUsed)
{
sDInt used = sMemFrameUsed-sMemFramePtr[sMemFrameToggle];
if(used>sMemStatMaxFrameUsed)
{
sMemStatMaxFrameUsed = used;
sLogF(L"sys",L"max frame used %Kb\n",sMemStatMaxFrameUsed);
}
}
if (sMemDmaUsed)
{
sDInt used = sMemDmaUsed-sMemDmaPtr[sMemDmaToggle];
if(used>sMemStatMaxDMAUsed)
{
sMemStatMaxDMAUsed = used;
sLogF(L"sys",L"max DMA Used %Kb\n",sMemStatMaxDMAUsed);
}
}
sMemFrameLastUsed=sMemFrameUsed-sMemFramePtr[sMemFrameToggle];
//sDPrintF(L"frame %K of %K\n",sMemFrameUsed-sMemFramePtr[sMemFrameToggle],sMemFrameSize);
sMemDmaToggle = !sMemDmaToggle;
sMemDmaUsed = sMemDmaPtr[sMemDmaToggle];
if(sFrameMemDoubleBuffered) sMemFrameToggle = !sMemFrameToggle;
sMemFrameUsed = sMemFramePtr[sMemFrameToggle];
//if(sMemFrameWasted||sMemDmaWasted) sDPrintF(L"wasted %Kb frame mem, %Kb dma mem\n",sMemFrameWasted,sMemDmaWasted);
sMemFrameWasted = 0;
sMemDmaWasted = 0;
sMemFlipFrame++;
}
/****************************************************************************/
/*** ***/
/*** MemoryPool ***/
/*** ***/
/****************************************************************************/
sMemoryPool::sMemoryPoolBuffer::sMemoryPoolBuffer(sPtr size,sInt flags)
{
Start = (sU8 *)sAllocMem(size,16,flags);
Current = Start;
End = Start + size;
Next = 0;
}
sMemoryPool::sMemoryPoolBuffer::~sMemoryPoolBuffer()
{
sFreeMem(Start);
}
/****************************************************************************/
sMemoryPool::sMemoryPool(sPtr defaultsize,sInt allocflags, sInt maxstacksize)
{
DefaultSize = defaultsize;
AllocFlags = allocflags;
sPushMemType(AllocFlags);
First = Current = Last = new sMemoryPoolBuffer(DefaultSize,AllocFlags);
Stack.HintSize(maxstacksize);
sPopMemType(AllocFlags);
}
sMemoryPool::~sMemoryPool()
{
sMemoryPoolBuffer *i,*n;
i = First;
while(i)
{
n = i->Next;
delete i;
i = n;
}
}
void sMemoryPool::Reset()
{
sMemoryPoolBuffer *i,*n;
i = First;
while(i)
{
n = i->Next;
delete i;
i = n;
}
Stack.Clear();
sPushMemType(AllocFlags);
First = Current = Last = new sMemoryPoolBuffer(DefaultSize,AllocFlags);
sPopMemType(AllocFlags);
}
sU8 *sMemoryPool::Alloc(sPtr size,sInt align)
{
sU8 *result;
if(size>DefaultSize/2) // special case: very large object.
{
sPushMemType(AllocFlags);
sMemoryPoolBuffer *b = new sMemoryPoolBuffer(size+align,AllocFlags);
sPopMemType(AllocFlags);
b->Next = First;
First = b;
b->Current = sAlign(b->Current,align);
result = b->Current;
b->Current += size;
}
else // normal case: stuff it in!
{
Current->Current = sAlign(Current->Current,align);
if(Current->Current+size>Current->End)
{
if(Current!=Last)
{
Current = Current->Next;
Current->Current = sAlign(Current->Current,align);
}
else
{
sPushMemType(AllocFlags);
Last->Next = new sMemoryPoolBuffer(DefaultSize,AllocFlags);
sPopMemType(AllocFlags);
Last = Last->Next;
Last->Current = sAlign(Last->Current,align); // if align>DefaultSize/2 we have a problem...
Current = Last;
}
}
result = Current->Current;
Current->Current += size;
}
return result;
}
sChar *sMemoryPool::AllocString(const sChar *str)
{
return AllocString(str,sGetStringLen(str));
}
sChar *sMemoryPool::AllocString(const sChar *str,sInt len)
{
sVERIFY(len>=0);
sChar *r = Alloc<sChar>(len+1);
sCopyMem(r,str,len*sizeof(sChar));
r[len] = 0;
return r;
}
void sMemoryPool::Push()
{
sStackItem *item = Stack.AddMany(1);
item->CBuffer = Current;
item->CPtr = Current->Current;
}
void sMemoryPool::Pop()
{
sInt count = Stack.GetCount();
sVERIFY(count);
sStackItem *item = &Stack[count-1];
Current = item->CBuffer;
sMemoryPoolBuffer *buf = item->CBuffer;
buf->Current = item->CPtr;
buf = buf->Next;
while(buf)
{
buf->Current = buf->Start;
buf = buf->Next;
}
Stack.RemTail();
}
sPtr sMemoryPool::BytesAllocated() const
{
const sMemoryPoolBuffer *buf = First;
sPtr allocsize = buf->Current-buf->Start;
while(buf!=Last)
{
buf = buf->Next;
allocsize += buf->Current-buf->Start;
}
return allocsize;
}
sPtr sMemoryPool::BytesReserved() const
{
const sMemoryPoolBuffer *buf = First;
sPtr memsize = buf->End-buf->Start;
while(buf!=Last)
{
buf = buf->Next;
memsize += buf->End-buf->Start;
}
return memsize;
}
/****************************************************************************/
/*** ***/
/*** Memory Leak Counter ***/
/*** ***/
/****************************************************************************/
/*
sMemoryLeakTracker1::sMemoryLeakTracker1()
{
LeakAlloc = 0;
LeakUsed = 0;
LeakData = 0;
Lock = 0;
}
void sMemoryLeakTracker1::Init(sInt listsize,sInt allocflags)
{
Lock = new sThreadLock();
AllocFlags = allocflags;
LeakAlloc = listsize;
LeakUsed = 0;
if (sIsMemTypeAvailable(AllocFlags))
LeakData = (LeakCheck *) sAllocMem(sizeof(LeakCheck)*LeakAlloc,4,AllocFlags);
else
LeakData=0;
if(LeakData==0)
{
sLogF(L"mem",L"leak debugging disabled (no workmem)\n");
LeakAlloc = 0;
}
else
sLogF(L"mem",L"initializing leak debugging %08x..%08x\n",sPtr(LeakData),sPtr(LeakData)+(sPtr)sizeof(LeakCheck)*LeakAlloc);
}
void sMemoryLeakTracker1::Exit()
{
LeakCheck *d = LeakData;
LeakData = 0;
LeakAlloc = 0;
LeakUsed = 0;
sFreeMem(d);
delete Lock;
}
void sMemoryLeakTracker1::AddLeak(void *ptr,sPtr size,const char *file,sInt line,sInt allocid,sInt heapid)
{
if (LeakAlloc)
{
sScopeLock l(Lock);
if(LeakUsed<LeakAlloc)
{
sInt i = LeakUsed++;
LeakData[i].Ptr = ptr;
LeakData[i].Size = size;
LeakData[i].File = file;
LeakData[i].Line = line;
LeakData[i].AllocId = allocid;
LeakData[i].HeapId = heapid;
LeakData[i].pad = 0;
LeakData[i].Duplicates = 0;
LeakData[i].TotalBytes = 0;
}
else
{
sFatal(L"sMemoryLeakTracker1 overflow");
}
#if sCONFIG_DEBUGMEM
sGetThreadContext()->TagMemFile = 0;
#endif
}
}
void sMemoryLeakTracker1::RemLeak(void *ptr)
{
if (LeakAlloc)
{
sScopeLock l(Lock);
for(sInt i=LeakUsed-1;i>=0;i--)
{
if(LeakData[i].Ptr==ptr)
{
LeakData[i] = LeakData[--LeakUsed];
return;
}
}
sFatal(L"delete without new");
}
}
void sMemoryLeakTracker1::DumpLeaks(const sChar *heapname,sInt minallocid)
{
sScopeLock l(Lock);
sInt count = 0;
sPtr bytes = 0;
sInt maxallocid = sMemoryAllocId;
for(sInt i=0;i<LeakUsed;i++)
{
if(LeakData[i].AllocId>=minallocid)
{
count++;
bytes += LeakData[i].Size;
}
}
if(count>0)
{
sLogF(L"mem",L"%q: (%d leaks in %K Bytes total)\n",heapname,count,bytes);
sPushMemType(AllocFlags);
// merge leaks
sHashTable<LeakCheck,LeakCheck> hash;
LeakCheck *n;
for(sInt i=0;i<LeakUsed;i++)
{
if(LeakData[i].AllocId>=minallocid && LeakData[i].AllocId<maxallocid)
{
n = hash.Find(&LeakData[i]);
if(n)
{
n->TotalBytes += LeakData[i].Size;
n->Duplicates ++;
}
else
{
n = &LeakData[i];
n->TotalBytes = LeakData[i].Size;
n->Duplicates = 1;
hash.Add(&LeakData[i],&LeakData[i]);
}
}
}
// sort leaks
sArray<LeakCheck *> Leaks;
hash.GetAll(&Leaks);
sSortDown(Leaks,&LeakCheck::Size);
sFORALL(Leaks,n)
{
sString<128> s,b;
if(n->File)
sCopyString(s,n->File,128);
else
s = L"unknown";
b.PrintF(L"%p(%d):",s,n->Line);
sDPrintF(L"%s%_ %5k bytes, %5k*%-5d, group %-2d, id:%d\n",b,60-sGetStringLen(b),n->TotalBytes,n->Size,n->Duplicates,n->HeapId,n->AllocId);
}
sPopMemType(AllocFlags);
}
}
sInt sMemoryLeakTracker1::BeginGetLeaks(const sMemoryLeakTracker1::LeakCheck *&leaks)
{
Lock->Lock();
leaks=LeakData;
return LeakUsed;
}
void sMemoryLeakTracker1::EndGetLeaks()
{
Lock->Unlock();
}
*/
/****************************************************************************/
/*** ***/
/*** Track Memory Leaks, second try ***/
/*** ***/
/****************************************************************************/
#if sCONFIG_DEBUGMEM
void sPushMemLeakDesc(const sChar *desc)
{
sThreadContext *tc=sGetThreadContext();
sInt dl=sMin(sGetStringLen(desc),sMemoryLeakDesc::STRINGLEN-tc->MLDBPos-1);
if (dl<sGetStringLen(desc))
{
sLogF(L"mem",L"Warning: sPushMemLeakDesc overflow!\n");
return;
}
if (tc->MLDBPos>0) tc->MemLeakDescBuffer2[tc->MLDBPos-1]=' ';
for (sInt i=0; i<dl; i++, tc->MLDBPos++)
tc->MemLeakDescBuffer[tc->MLDBPos]=tc->MemLeakDescBuffer2[tc->MLDBPos]=desc[i];
tc->MemLeakDescBuffer[tc->MLDBPos]=0;
tc->MemLeakDescBuffer2[tc->MLDBPos]=0;
tc->MLDBPos++;
tc->MLDBCRC=sChecksumRandomByte((const sU8*)tc->MemLeakDescBuffer2,2*tc->MLDBPos);
}
void sPopMemLeakDesc()
{
sThreadContext *tc=sGetThreadContext();
if (!tc->MLDBPos)
{
sLogF(L"mem",L"Warning: sPopMemLeakDesc underflow!\n");
return;
}
tc->MLDBPos-=2;
if (tc->MLDBPos<=0)
{
tc->MLDBPos=0;
tc->MemLeakDescBuffer[tc->MLDBPos]=0;
tc->MemLeakDescBuffer2[tc->MLDBPos]=0;
tc->MLDBCRC=0;
}
for (;tc->MLDBPos>0 && tc->MemLeakDescBuffer[tc->MLDBPos]!=0;--tc->MLDBPos) {}
tc->MemLeakDescBuffer[tc->MLDBPos]=0;
tc->MemLeakDescBuffer2[tc->MLDBPos]=0;
if (tc->MLDBPos) tc->MLDBPos++;
tc->MLDBCRC=sChecksumRandomByte((const sU8*)tc->MemLeakDescBuffer2,2*tc->MLDBPos);
}
#endif
sMemoryLeakTracker2::sMemoryLeakTracker2()
{
Pool = 0;
sClear(LocHash);
sClear(LeakHash);
sClear(DescHash);
LeakCount = 0;
AllocFlags = 0;
Lock = 0;
InformationStrings = sNULL;
}
sMemoryLeakTracker2::~sMemoryLeakTracker2()
{
Exit();
}
void sMemoryLeakTracker2::Init2(sInt allocflags)
{
Lock = new sThreadLock();
AllocFlags = allocflags|sAMF_NOLEAK;
Pool = new sMemoryPool(256*1024,AllocFlags);
}
void sMemoryLeakTracker2::Exit()
{
sClear(LocHash);
sClear(LeakHash);
FreeLeaks.Clear();
Descs.Clear();
FreeDescs.Clear();
sDelete(Pool);
sDelete(Lock);
}
void sMemoryLeakTracker2::DumpLeaks(const sChar *message,sInt minallocid,sInt heapid)
{
if (!Pool) return;
sPushMemType(AllocFlags);
sMemoryLeakInfo *info;
sStaticArray<sMemoryLeakInfo> locs;
sStaticArray<sMemoryLeakDesc> descs;
GetLeaks(locs,minallocid);
if(locs.GetCount()>0)
{
sInt totalleak = 0;
sInt totalmem = 0;
sFORALL(locs,info) if (!heapid || (info->HeapId&sAMF_MASK)==(heapid&sAMF_MASK))
{
totalleak += info->Count;
totalmem += info->TotalBytes;
}
sLogF(L"mem",L"%q: (%d leaks in %K Bytes total)\n",message,totalleak,totalmem);
sSortDown(locs,&sMemoryLeakInfo::TotalBytes);
sFORALL(locs,info) if (!heapid || (info->HeapId&sAMF_MASK)==(heapid&sAMF_MASK))
{
sString<128> s,b;
if(info->File)
sCopyString(s,info->File,128);
else
s = L"unknown";
b.PrintF(L"%p(%d):",s,info->Line);
sDPrintF(L"%s%_ %5K bytes in %5K chunks %5K avg group %-2d id:%d first bytes: %d (%08x)\n",b,
60-sGetStringLen(b),info->TotalBytes,info->Count,info->TotalBytes/info->Count,info->HeapId,info->AllocId,info->FirstSize,info->FirstSize);
}
GetLeakDescriptions(descs, minallocid);
if (descs.GetCount()>1 || minallocid>0)
{
sMemoryLeakDesc *d;
sHeapSortUp(descs,&sMemoryLeakDesc::String);
sDPrintF(L"\n");
sLogF(L"mem",L"Memory usage by description:\n");
if (minallocid>0)
sLogF(L"mem",L"(warning: this list is incomplete)\n");
sFORALL(descs,d)
{
sInt size=0;
sInt count=0;
if (heapid)
{
size=d->Size[heapid&sAMF_MASK];
count=d->Count[heapid&sAMF_MASK];
}
else for (sInt i=1; i<=sAMF_MASK; i++)
{
size+=d->Size[i];
count+=d->Count[i];
}
if (count)
sDPrintF(L"%s%_: %5K bytes in %5K chunks %5K avg\n",d->String,60-sGetStringLen(d->String),size,count,size/count);
}
}
}
sPopMemType(AllocFlags);
}
static sBool cmp(sMemoryLeakTracker2::LeakLoc *loc,sInt line,sInt heapid,const char *file)
{
if(loc->Line!=line) return 0;
if(loc->HeapId!=heapid) return 0;
if(loc->File==file) return 1;
if(loc->File==0 || file==0) return 0;
for(sInt i=0;loc->File[i] || file[i];i++)
if(loc->File[i]!=file[i]) return 0;
return 1;
}
static sInt sMemoryLeakTracker2Hash(void *ptr,sInt hashSize)
{
sDInt ptrInt = (sDInt) ptr;
sU64 nHashVal = 0x84222325cbf29ce4ULL,
nMagicPrime = 0x00000100000001b3ULL;
nHashVal ^= ((ptrInt >> 0) & 0xffff);
nHashVal *= nMagicPrime;
nHashVal ^= ((ptrInt >> 16) & 0xffff);
nHashVal *= nMagicPrime;
#if sCONFIG_64BIT
nHashVal ^= ((ptrInt >> 32) & 0xffff);
nHashVal *= nMagicPrime;
nHashVal ^= ((ptrInt >> 48) & 0xffff);
nHashVal *= nMagicPrime;
#endif
return nHashVal & (hashSize-1);
}
void sMemoryLeakTracker2::AddLeak( void *ptr,sPtr size,const char *file,sInt line,sInt allocid,sInt heapid, const sChar *descstr, sU32 descCRC )
{
if (!Pool) return;
LeakLoc *loc;
Leak *leak;
sMemoryLeakDesc *desc;
Lock->Lock();
// find leak loc
sInt hash = line & (LocHashSize-1);
loc = LocHash[hash];
while(loc)
{
if(cmp(loc,line,heapid,file))
break;
loc = loc->NextHash;
}
if(loc==0)
{
loc = Pool->Alloc<LeakLoc>();
loc->File = file;
loc->Line = line;
loc->HeapId = heapid;
loc->NextHash = LocHash[hash];
loc->Leaks.Clear();
LocHash[hash] = loc;
}
// find leak description
if (!descstr || !*descstr)
{
descstr=L"(unknown)";
descCRC=0;
}
hash = descCRC & (DescHashSize-1);
desc = DescHash[hash];
while (desc)
{
if (!sCmpString(descstr,desc->String))
break;
desc=desc->NextHash;
}
if (desc==0)
{
if (FreeDescs.IsEmpty())
desc = Pool->Alloc<sMemoryLeakDesc>();
else
desc = FreeDescs.RemTail();
desc->String=descstr;
desc->RefCount=0;
sClear(desc->Size);
sClear(desc->Count);
desc->Hash=hash;
desc->NextHash = DescHash[hash];
desc->AllocId = allocid;
DescHash[hash]=desc;
Descs.AddTail(desc);
}
desc->RefCount++;
desc->Count[heapid&sAMF_MASK]++;
desc->Size[heapid&sAMF_MASK]+=size;
if(FreeLeaks.IsEmpty())
leak = Pool->Alloc<Leak>();
else
leak = FreeLeaks.RemTail();
leak->Ptr = ptr;
leak->Size = size;
leak->AllocId = allocid;
leak->HeapId = heapid;
leak->Desc=desc;
leak->NextHash = 0;
loc->Leaks.AddTail(leak);
hash = sMemoryLeakTracker2Hash(ptr,LeakHashSize);
//hash = ((sDInt(ptr)>>4)|(sDInt(ptr)>>16)) & (LeakHashSize-1);
leak->NextHash = LeakHash[hash];
LeakHash[hash] = leak;
LeakCount++;
Lock->Unlock();
}
void sMemoryLeakTracker2::RemLeak(void *ptr)
{
if (!Pool) return;
Lock->Lock();
sInt hash = sMemoryLeakTracker2Hash(ptr,LeakHashSize);
//sInt hash = ((sDInt(ptr)>>4)|(sDInt(ptr)>>16)) & (LeakHashSize-1);
Leak **leakp = &LeakHash[hash];
Leak *leak = *leakp;
while(leak)
{
if(leak->Ptr==ptr)
{
*leakp = leak->NextHash;
goto found;
}
leakp = &leak->NextHash;
leak = *leakp;
}
Lock->Unlock();
sDPrintF(L"warning: delete without new: 0x%08x. may happen inside leaktracker\n",sPtr(ptr));
return;
found:
sMemoryLeakDesc *desc=leak->Desc;
desc->Size[leak->HeapId&sAMF_MASK]-=leak->Size;
desc->Count[leak->HeapId&sAMF_MASK]--;
desc->RefCount--;
if (!desc->RefCount)
{
desc->Node.Rem();
FreeDescs.AddTail(leak->Desc);
sMemoryLeakDesc **descp=&DescHash[desc->Hash];
while (*descp)
{
if (*descp==desc)
{
*descp=desc->NextHash;
break;
}
descp=&((*descp)->NextHash);
}
}
leak->Desc=0; // for safety
leak->Node.Rem();
FreeLeaks.AddTail(leak);
LeakCount--;
Lock->Unlock();
}
void sMemoryLeakTracker2::GetLeaks(sStaticArray<sMemoryLeakInfo> &locs,sInt minallocid)
{
if (!Pool) return;
LeakLoc *loc;
Leak *leak;
sMemoryLeakInfo *info;
Lock->Lock();
// count locations
sInt count=0;
for(sInt i=0;i<LocHashSize;i++)
{
loc = LocHash[i];
while(loc)
{
if(!loc->Leaks.IsEmpty())
count++;
loc = loc->NextHash;
}
}
// do we leak?
if(count>0)
{
locs.HintSize(count+1); // +1 because this very call might open a new location entry
sInt maxallocid = sMemoryAllocId;
for(sInt i=0;i<LocHashSize;i++)
{
loc = LocHash[i];
while(loc)
{
if(!loc->Leaks.IsEmpty())
{
sInt totalleak=0;
sInt totalmem=0;
sInt allocid=maxallocid+1;
sInt firstsize = 0;
sFORALL_LIST(loc->Leaks,leak)
{
if(leak->AllocId>=minallocid && leak->AllocId<maxallocid)
{
if(leak->AllocId<allocid)
{
allocid = leak->AllocId;
firstsize = leak->Size;
}
totalleak++;
totalmem += leak->Size;
}
}
if(totalleak>0)
{
info = locs.AddMany(1);
info->File = loc->File;
info->Line = loc->Line;
info->HeapId = loc->HeapId;
info->AllocId = allocid;
info->Count = totalleak;
info->TotalBytes = totalmem;
info->FirstSize = firstsize;
}
}
loc = loc->NextHash;
}
}
}
Lock->Unlock();
}
void sMemoryLeakTracker2::GetLeakDescriptions(sStaticArray<sMemoryLeakDesc> &descs, sInt minid)
{
if (!Pool) return;
Lock->Lock();
// count descriptions
sInt count=Descs.GetCount();
// do we leak?
if(count>0)
{
descs.HintSize(count);
// sInt maxallocid = sMemoryAllocId;
sMemoryLeakDesc *dp=Descs.GetHead();
for(sInt i=0;i<count;i++, dp=Descs.GetNext(dp))
{
if (dp->AllocId>=minid)
descs.AddTail(*dp);
}
}
Lock->Unlock();
}
sInt sMemoryLeakTracker2::CmpString8(const sChar *a, const sChar8 *b)
{
sInt aa,bb;
do
{
aa = *a++;
bb = *b++;
}
while(aa!=0 && aa==bb);
return sSign(aa-bb);
}
sChar8 *sMemoryLeakTracker2::GetPersistentString8(const sChar *informationString)
{
sChar8 *result = sNULL;
if (Pool)
{
Lock->Lock();
// find string in list and sort LRU-Style
sMemoryLeakInformationStrings *infoStrings = InformationStrings;
sMemoryLeakInformationStrings *infoStrings_prev = sNULL;
sInt i=0;
while (!result && infoStrings)
{
result = infoStrings->InformationString;
if (CmpString8(informationString, result)==0)
{
// hit - result is valid
// move node to the start of the list as new head
if (infoStrings_prev)
{
infoStrings_prev->NextInfo = infoStrings->NextInfo;
infoStrings->NextInfo = InformationStrings;
InformationStrings = infoStrings;
}
}
else
{
// miss - try the next one
result = sNULL;
infoStrings_prev = infoStrings;
infoStrings = infoStrings->NextInfo;
}
i++;
}
// if no string is found make a copy and register it
if (!result)
{
// create node
infoStrings = Pool->Alloc<sMemoryLeakInformationStrings>();
// fill data
sInt len = sGetStringLen(informationString)+1;
result = (sChar8*)Pool->Alloc(len * sizeof(sChar8), 1);
sCopyString(result, informationString, len);
infoStrings->InformationString = result;
// add at head
infoStrings->NextInfo = InformationStrings;
InformationStrings = infoStrings;
}
Lock->Unlock();
}
return result;
}
/****************************************************************************/
/*** ***/
/*** Memory Heap (simple, slow, efficient) ***/
/*** ***/
/****************************************************************************/
#if sCONFIG_64BIT
#define HEADER 8
#define OFFSET 24
#define MASK 31
#define ALIGN 32
#else
#define HEADER 4 // HEADER == sizeof(void *)
#define OFFSET 12 // OFFSET == sizeof(sMemoryHeapFreeNode)
#define MASK 15 // MASK + 1 == ALIGN
#define ALIGN 16 // HEADER + OFFSET == ALIGN
#endif
#define MEMVERBOSE 0
sMemoryHeap::sMemoryHeap()
{
Start = 0;
End = 0;
TotalFree = 0;
LastFreeNode = 0;
Clear = MEMVERBOSE;
}
void sMemoryHeap::Init(sU8 *start,sPtr size)
{
sVERIFY(start);
Start = sPtr(start);
End = Start + size;
Start = ((Start+HEADER+MASK)&(~MASK))-HEADER;
End = ((End+HEADER)&(~MASK))-HEADER;
TotalFree = End-Start;
if(Clear) sSetMem((void *)Start,0xaa,End-Start);
sMemoryHeapFreeNode *node = (sMemoryHeapFreeNode *) Start;
node->Size = TotalFree;
FreeList.AddHead(node);
LastFreeNode = node;
MinTotalFree = GetFree();
}
void sMemoryHeap::SetDebug(sBool clear,sInt memwall)
{
Clear = clear;
}
/****************************************************************************/
sCONFIG_SIZET sMemoryHeap::GetFree()
{
return TotalFree;
}
sCONFIG_SIZET sMemoryHeap::GetLargestFree()
{
Lock();
sPtr max=0;
sMemoryHeapFreeNode *n;
sFORALL_LIST(FreeList,n)
max=sMax(max,n->Size);
Unlock();
return max;
}
sPtr sMemoryHeap::GetUsed()
{
return End-Start-TotalFree;
}
void sMemoryHeap::DumpStats(sInt verbose)
{
sInt count = 0;
sInt fcount = 0;
sPtr largest = 0;
sPtr free = 0;
sPtr smallest = 0x7fffffff;
sPtr fragged = 0;
sMemoryHeapFreeNode *node;
sFORALL_LIST(FreeList,node)
{
count++;
free += node->Size;
if(node->Size<64*1024)
{
fragged += node->Size;
fcount++;
}
if(node->Size>largest)
largest = node->Size;
if(node->Size<smallest)
smallest = node->Size;
}
if(count==0)
smallest = 0;
sU32 hash = MakeSnapshot();
sLogF(L"mem",L"HeapStats: %08x..%08x, HASH %08x\n",Start,End,hash);
sLogF(L"mem",L"%08x(%5K) Free in %d nodes\n",free,free,count);
sLogF(L"mem",L"%08x(%5K) Largest\n",largest,largest);
sLogF(L"mem",L"%08x(%5K) Smallest\n",smallest,smallest);
sLogF(L"mem",L"%08x(%5K) Fragged in %d nodes\n",fragged,fragged,fcount);
sLogF(L"mem",L"%08x(%5K) Unfragged in %d nodes\n",free-fragged,free-fragged,count-fcount);
if(free!=TotalFree)
sLogF(L"mem",L"Heap Corrupted! (%08x free, %08x should)\n",free,TotalFree);
}
sU32 sMemoryHeap::MakeSnapshot()
{
sMemoryHeapFreeNode *node;
sInt freeNodeCount = 0;
sChecksumAdler32Begin();
sLogF(L"mem", L"starting snapshot\n");
sFORALL_LIST(FreeList,node)
{
sChecksumAdler32Add((const sU8 *)node,sizeof(*node));
// use this for verbose debugging
//sLogF(L"mem", L"0x%08x : node = 0x%08x size = %d\n", (sInt)node, (sInt)&node->Node, node->Size);
freeNodeCount++;
}
sLogF(L"mem", L"Free mem nodes: %d\n", freeNodeCount);
// use this for verbose debugging
// sMemoryLeaks->DumpLeaks(L"Current leaks", 0,0);
return sChecksumAdler32End();
}
sBool sMemoryHeap::IsFree(const void *ptr) const
{
const sMemoryHeapFreeNode *node;
sFORALL_LIST(FreeList,node)
{
sU8 *ns = (sU8 *)node;
sU8 *ne = ns + node->Size;
if(ptr>=ns && ptr<ne)
return 1;
}
return 0;
}
void sMemoryHeap::Validate()
{
sPtr free = 0;
sPtr last = 0;
sPtr t = 0;
sInt errors = 0;
sMemoryHeapFreeNode *node,*prev,*next;
sFORALL_LIST(FreeList,node)
{
prev = FreeList.GetPrev(node);
next = FreeList.GetNext(node);
if(FreeList.GetNext(prev)!=node || FreeList.GetPrev(next)!=node)
{
sLogF(L"mem",L"linked list broken\n");
errors++;
break;
}
if(node->Size<ALIGN || node->Size>(End-Start))
{
sLogF(L"mem",L"invalid free node size! %08x for %08x\n",sPtr(node),node->Size);
errors++;
}
free += node->Size;
t = sPtr(node);
if(t==last)
{
sLogF(L"mem",L"nodes not merged! %08x..%08x\n",last,t);
errors++;
}
if(t<last)
{
sLogF(L"mem",L"nodes not sorted! %08x..%08x\n",last,t);
errors++;
}
last = t+node->Size;
if(t<Start || last>End)
{
sLogF(L"mem",L"nodes outside heap! %08x..%08x\n",t,last);
errors++;
}
}
if(free!=TotalFree)
{
sLogF(L"mem",L"%08x free, %08x expected\n",free,TotalFree);
errors++;
}
if(errors)
sFatal(L"heap corrupted");
}
void sMemoryHeap::ClearFree()
{
sMemoryHeapFreeNode *node;
Validate();
sU8 pattern=0x00;
sLogF(L"mem",L"clearing mem with 0x%02x\n",pattern);
sFORALL_LIST(FreeList,node)
sSetMem(((sU8 *)node)+sizeof(sMemoryHeapFreeNode),pattern,node->Size-sizeof(sMemoryHeapFreeNode));
}
/****************************************************************************/
void *sMemoryHeap::Alloc(sPtr bytes,sInt align,sInt flags)
{
sMemoryHeapFreeNode *n,*p;
sPtr size = ((bytes+HEADER+MASK)&(~MASK)); // align size to 16'er, including header
sVERIFY(bytes<size); // overflow check
if(align<ALIGN) align = ALIGN;
if(/*size<0 ||*/ size>TotalFree) return 0; // no way we can do it
if(MEMVERBOSE) Validate();
sBool found = 0;
sPtr fs,fe,as,ae;
if(!(flags & sAMF_ALT))
{
sFORALL_LIST(FreeList,n)
{
fs = sPtr(n); // free start
fe = fs+n->Size; // free end
as = sAlign(fs+HEADER,align)-HEADER; // allocated start, aligned and with header
ae = as+size; // allocated end
if(ae<=fe) // does it fit?
{
sVERIFY(as>=fs);
sVERIFY(ae<=fe);
found = 1;
break;
}
}
}
else
{
sFORALL_LIST_REVERSE(FreeList,n)
{
fs = sPtr(n); // free start
fe = fs+n->Size; // free end
as = ((fe-size+HEADER)&~(align-1))-HEADER; // allocated start, aligned and with header
ae = as+size; // allocated end
if(fs<=as) // does it fit?
{
sVERIFY(as>=fs);
sVERIFY(ae<=fe);
found = 1;
break;
}
}
}
if(found) // does it fit?
{
if(as!=fs) // free space at start to pad misalignment
{
sVERIFY(((as-fs)&MASK)==0); // free space should be multiple of 16
n->Size = (as-fs); // trim node to new size
p = n; // link rest after this node
}
else // no free space at start (small alignemnt)
{
p = FreeList.GetPrev(n);
if(!FreeList.IsLast(n))
LastFreeNode = FreeList.GetNext(n);
else if(!FreeList.IsFirst(n))
LastFreeNode = FreeList.GetPrev(n);
else
LastFreeNode = 0;
FreeList.Rem(n);
}
if(ae!=fe) // add node at end
{
sVERIFY(((fe-ae)&MASK)==0);
sVERIFY(((fe+HEADER)&MASK)==0);
n = (sMemoryHeapFreeNode *)(ae);
n->Size = fe-ae;
FreeList.AddAfter(n,p);
}
if(Clear) sSetMem((void *)as,0xcc,ae-as);
*((sPtr *)as) = bytes;
TotalFree -= size;
MinTotalFree = sMin(TotalFree,MinTotalFree);
sVERIFY(((as+HEADER)&MASK)==0);
if(MEMVERBOSE) Validate();
sAtomicAdd(&sMemoryUsed,size);
return (sU8 *) (as+HEADER);
}
return 0;
}
sBool sMemoryHeap::Free(void *ptr)
{
#if sCFG_MEMDBG_LOCKING
if(sMemDbgCheckLock(ptr))
sFatal(L"called free an locked memory\n");
#endif
sMemoryHeapFreeNode *n,*b;
sPtr bytes = ((sPtr *)ptr)[-1];
sPtr size = ((bytes+HEADER+MASK)&(~MASK)); // align size to 16'er, including header
sPtr bs = sPtr(ptr)-HEADER;
sPtr be = bs+size;
if(MEMVERBOSE) Validate();
if(MEMVERBOSE) sVERIFYRELEASE(!IsFree(ptr));
sAtomicAdd(&sMemoryUsed,-(sDInt)size);
if(Clear) sSetMem((void *)(((sU8 *)ptr)-HEADER),0xee,size);
sVERIFY(bs>=Start && be<=End);
TotalFree += size;
b = (sMemoryHeapFreeNode *) bs;
b->Size = size;
if(FreeList.IsEmpty())
{
FreeList.AddHead(b);
return 1;
}
#if 1
sMemoryHeapFreeNode *end = FreeList.GetTail();
if(be<=sPtr(end)) // freed block in free list memory range?
{
sMemoryHeapFreeNode *start = FreeList.GetHead();
// use last freed node to divide search list
if(LastFreeNode)
{
if(be<=sPtr(LastFreeNode))
end = LastFreeNode;
else
start = LastFreeNode;
}
if(be<=sPtr(start)) // new first element?
{
FreeList.AddBefore(b,start);
goto found;
}
else if(bs-sPtr(start)<sPtr(end)-be) // forward search ptr in free list
{
n = start;
while(1)
{
sPtr sn = sPtr(n);
if(be<=sn)
{
FreeList.AddBefore(b,n);
goto found;
}
if(n==end)
break;
n = FreeList.GetNext(n);
}
}
else // backward search ptr in free list
{
n = end;
while(1)
{
sPtr en = sPtr(n)+n->Size;
if(bs>=en)
{
FreeList.AddAfter(b,n);
goto found;
}
sVERIFY(n!=start);
if(n==start)
break;
n = FreeList.GetPrev(n);
}
}
}
#else
sFORALL_LIST(FreeList,n)
{
sPtr sn = sPtr(n);
if(be<=sn)
{
FreeList.AddBefore(b,n);
goto found;
}
}
#endif
FreeList.AddTail(b);
found:;
if(!FreeList.IsFirst(b))
{
sMemoryHeapFreeNode *a = FreeList.GetPrev(b);
sPtr ae = sPtr(a)+a->Size;
if(ae == bs)
{
FreeList.Rem(b);
a->Size += b->Size;
b = a;
}
}
if(!FreeList.IsLast(b))
{
sMemoryHeapFreeNode *c = FreeList.GetNext(b);
sPtr cs = sPtr(c);
if(be==cs)
{
FreeList.Rem(c);
b->Size += c->Size;
}
}
if(MEMVERBOSE) Validate();
LastFreeNode = b;
return 1;
}
sPtr sMemoryHeap::MemSize(void *ptr)
{
sPtr bytes = ((sPtr *)ptr)[-1];
return bytes;
}
/****************************************************************************/
#undef HEADER
#undef OFFSET
#undef MASK
/****************************************************************************/
/*** ***/
/*** memory heap, usefull for gpu-memory scenarious (slow read access) ***/
/*** ***/
/****************************************************************************/
sGpuHeap::sGpuHeap()
{
HashTable = 0;
HashSize = 0;
HashMask = 0;
HashShift = 0;
NodeMem = 0;
TotalFree = 0;
Clear = 0;
}
sGpuHeap::~sGpuHeap()
{
delete[] HashTable;
delete[] NodeMem;
}
void sGpuHeap::Init(sU8 *start,sPtr size,sInt maxallocations)
{
Start = sPtr(start);
End = Start+size;
TotalFree = End - Start;
// init hashtable
HashShift = sFindLowerPower(maxallocations/8);
HashSize = 1<<HashShift;
HashMask = HashSize-1;
HashTable = new sGpuHeapUsedNode *[HashSize];
for(sInt i=0;i<HashSize;i++)
HashTable[i] = 0;
// allocate unused nodes
NodeMem = new sGpuHeapFreeNode[maxallocations];
for(sInt i=0;i<maxallocations;i++)
UnusedNodes.AddTail(&NodeMem[i]);
// initialize free list
sGpuHeapFreeNode *n = UnusedNodes.RemTail();
n->Data = sPtr(start);
n->Size = size;
FreeList.AddTail(n);
MinTotalFree = GetFree();
}
void sGpuHeap::Exit()
{
FreeList.Clear();
UnusedNodes.Clear();
sDeleteArray(HashTable);
sDeleteArray(NodeMem);
HashSize = 0;
HashMask = 0;
HashShift = 0;
TotalFree = 0;
}
void sGpuHeap::AddNode(sGpuHeapUsedNode *l)
{
sU32 hash = ((l->Data>>4) ^ (l->Data>>(4+HashShift))) & HashMask;
l->Next = HashTable[hash];
HashTable[hash] = l;
}
sGpuHeapUsedNode *sGpuHeap::FindNode(sPtr data,sBool remove)
{
sGpuHeapUsedNode *l,**p;
sU32 hash = ((data>>4) ^ (data>>(4+HashShift))) & HashMask;
p = &HashTable[hash];
while(*p)
{
l = *p;
if(l->Data==data)
{
if(remove)
*p = l->Next;
return l;
}
p = &l->Next;
}
return 0;
}
/****************************************************************************/
sCONFIG_SIZET sGpuHeap::GetFree()
{
return TotalFree;
}
sCONFIG_SIZET sGpuHeap::GetLargestFree()
{
Lock();
sPtr max=0;
sGpuHeapFreeNode *n;
sFORALL_LIST(FreeList,n)
max=sMax(max,n->Size);
Unlock();
return max;
}
sPtr sGpuHeap::GetUsed()
{
return End-Start-TotalFree;
}
void sGpuHeap::SetDebug(sBool clear,sInt memwall)
{
Clear = clear;
}
sU32 sGpuHeap::MakeSnapshot()
{
sGpuHeapFreeNode *node;
sPtr data[2];
sChecksumAdler32Begin();
sFORALL_LIST(FreeList,node)
{
data[0] = node->Data;
data[1] = node->Size;
sChecksumAdler32Add((const sU8 *)data,sizeof(sPtr)*2);
}
return sChecksumAdler32End();
}
void sGpuHeap::Validate()
{
sPtr free = 0;
sPtr allocated = 0;
sPtr last = 0;
sInt errors = 0;
sGpuHeapFreeNode *node,*prev,*next;
// free list
sFORALL_LIST(FreeList,node)
{
prev = FreeList.GetPrev(node);
next = FreeList.GetNext(node);
if(FreeList.GetNext(prev)!=node || FreeList.GetPrev(next)!=node)
{
sLogF(L"mem",L"linked list broken\n");
errors++;
break;
}
if(node->Size<16 || node->Size>(End-Start))
{
sLogF(L"mem",L"invalid free node size! %08x for %08x\n",sPtr(node),node->Size);
errors++;
}
free += node->Size;
if(node->Data==last)
{
sLogF(L"mem",L"nodes not merged! %08x..%08x\n",last,node->Data);
errors++;
}
if(node->Data<last)
{
sLogF(L"mem",L"nodes not sorted! %08x..%08x\n",last,node->Data);
errors++;
}
last = node->Data+node->Size;
if(node->Data<Start || last>End)
{
sLogF(L"mem",L"nodes outside heap! %08x..%08x\n",node->Data,last);
errors++;
}
}
if(free!=TotalFree)
{
sLogF(L"mem",L"%08x free, %08x expected\n",free,TotalFree);
errors++;
}
// allocated
sGpuHeapUsedNode *h;
for(sInt i=0;i<HashSize;i++)
{
h = HashTable[i];
while(h)
{
allocated += sAlign(h->Size,16);
if(h->Data<Start || h->Data+h->Size>End)
{
sLogF(L"mem",L"allocated memory outside heap! %08x..%08x\n",h->Data,h->Data+h->Size);
errors++;
}
h = h->Next;
}
}
if(allocated+free!=End-Start)
{
sLogF(L"mem",L"some memory leaked from the heap: %08x free + %08x allocated != %08x total\n",free,allocated,End-Start);
errors++;
}
// final message
if(errors)
sFatal(L"heap corrupted");
}
void *sGpuHeap::Alloc(sPtr bytes,sInt align,sInt flags)
{
sGpuHeapFreeNode *n,*p;
sPtr size = sAlign(bytes,16); // align size to 16'er, including header
sVERIFY(bytes<=size); // overflow check
if(align<16) align = 16;
if(/*size<0 ||*/ size>TotalFree) return 0; // no way we can do it
if(MEMVERBOSE) Validate();
sBool found = 0;
sPtr fs,fe,as,ae;
if((flags & sAMF_ALT)) // default is reverse allocation
{
sFORALL_LIST(FreeList,n)
{
fs = n->Data; // free start
fe = fs+n->Size; // free end
as = sAlign(fs,align); // allocated start, aligned
ae = as+size; // allocated end
if(ae<=fe) // does it fit?
{
sVERIFY(as>=fs);
sVERIFY(ae<=fe);
found = 1;
break;
}
}
}
else
{
sFORALL_LIST_REVERSE(FreeList,n)
{
fs = n->Data; // free start
fe = fs+n->Size; // free end
as = (fe-size)&~(align-1); // allocated start, aligned
ae = as+size; // allocated end
if(fs<=as) // does it fit?
{
sVERIFY(as>=fs);
sVERIFY(ae<=fe);
found = 1;
break;
}
}
}
if(found) // does it fit?
{
if(as!=fs) // free space at start to pad misalignment
{
sVERIFY(((as-fs)&15)==0); // free space should be multiple of 16
n->Size = (as-fs); // trim node to new size
p = n; // link rest after this node
}
else // no free space at start (small alignemnt)
{
p = FreeList.GetPrev(n);
FreeList.Rem(n);
UnusedNodes.AddTail(n);
}
if(ae!=fe) // add node at end
{
sVERIFY(((fe-ae)&15)==0);
sVERIFY(((fe)&15)==0);
n = UnusedNodes.RemTail();
if(!n) sVERIFY(0);
n->Data = ae;
n->Size = fe-ae;
FreeList.AddAfter(n,p);
}
if(Clear) sSetMem((void *)as,0xcc,ae-as);
sGpuHeapUsedNode *alloc = (sGpuHeapUsedNode *) UnusedNodes.RemTail();
alloc->Data = as;
alloc->Size = bytes;
AddNode(alloc);
TotalFree -= size;
MinTotalFree = sMin(TotalFree,MinTotalFree);
sVERIFY((as&15)==0);
if(MEMVERBOSE) Validate();
sAtomicAdd(&sMemoryUsed,size);
return (sU8 *) (as);
}
return 0;
}
sBool sGpuHeap::Free(void *ptr)
{
#if sCFG_MEMDBG_LOCKING
if(sMemDbgCheckLock(ptr))
sFatal(L"called free an locked memory\n");
#endif
sGpuHeapFreeNode *n,*b;
sGpuHeapUsedNode *old;
if(MEMVERBOSE) Validate();
// find allocation node
old = FindNode(sPtr(ptr),1);
if(old==0)
return 0;
sVERIFY(old->Data==sPtr(ptr));
sPtr bytes = old->Size;
sPtr size = sAlign(bytes,16); // align size to 16'er, including header
sPtr bs = sPtr(ptr);
sPtr be = bs+size;
b = (sGpuHeapFreeNode *) old;
old->Size = size;
old->Data = sPtr(ptr);
sAtomicAdd(&sMemoryUsed,-(sDInt)size);
if(Clear) sSetMem((void *)ptr,0xee,size);
sVERIFY(bs>=Start && be<=End);
TotalFree += size;
b->Size = size;
if(FreeList.IsEmpty())
{
FreeList.AddHead(b);
return 1;
}
sFORALL_LIST(FreeList,n)
{
sPtr sn = n->Data;
if(be<=sn)
{
FreeList.AddBefore(b,n);
goto found;
}
}
FreeList.AddTail(b);
found:;
if(!FreeList.IsFirst(b))
{
sGpuHeapFreeNode *a = FreeList.GetPrev(b);
sPtr ae = a->Data+a->Size;
if(ae == bs)
{
FreeList.Rem(b);
UnusedNodes.AddTail(b);
a->Size += b->Size;
b = a;
}
}
if(!FreeList.IsLast(b))
{
sGpuHeapFreeNode *c = FreeList.GetNext(b);
sPtr cs = c->Data;
if(be==cs)
{
FreeList.Rem(c);
b->Size += c->Size;
UnusedNodes.AddTail(c);
}
}
if(MEMVERBOSE) Validate();
return 1;
}
sPtr sGpuHeap::MemSize(void *ptr)
{
sGpuHeapUsedNode *l = FindNode(sPtr(ptr),0);
if(l)
return l->Size;
else
return 0;
}
/****************************************************************************/
/*** ***/
/*** sSimpleMemPool ***/
/*** ***/
/****************************************************************************/
sSimpleMemPool::sSimpleMemPool(sInt size)
{
DeleteMe = 1;
Start = Current = sPtr(new sU8[size]);
End = Start+size;
}
sSimpleMemPool::sSimpleMemPool(sU8 *data,sInt size)
{
DeleteMe = 0;
Start = Current = sPtr(data);
End = Start+size;
}
sSimpleMemPool::~sSimpleMemPool()
{
if(DeleteMe)
delete[] (sU8 *)Start;
}
sU8 *sSimpleMemPool::Alloc(sInt size,sInt align)
{
Current = sAlign(Current,align);
sU8 *result = (sU8 *) Current;
Current+=size;
sVERIFY(Current<=End);
return result;
}
void sSimpleMemPool::Reset()
{
Current = Start;
}
/****************************************************************************/
/*** ***/
/*** Compression ***/
/*** ***/
/****************************************************************************/
//
// equal sequence compression format
//
// 00pppppp pppppppp ssssssss copy dest[-p-s-4],s+4
// 01ssssss pppppppp copy dest[-p-s-3],s+3
// 10sspppp copy dest[-p-s-2],s+2
// 1100ssss ... copy src[0],s+1
// 1101ssss dddddddd fill d,s+2
// 1110ssss fill 0,s+1
// 11110sss ssssssss ... copy src[0],s+1+15
// 11111sss ssssssss dddddddd fill d,s+2+15
//
/****************************************************************************/
sBool sCompES(sU8 *s,sU8 *d,sInt ssize,sInt &dsize,sInt scan)
{
sInt si; // source index
sInt di; // destination index
sInt ri; // bytes read from source without pattern
sInt i,j; // :-)
sInt imax; // temporary limit for scanning
sInt jmin; // temporary limit for scanning
sInt mode; // 3 = run found, 1 = sequence found
sInt data; // value for run
sInt size; // size for both modes
sInt pos; // offset for equal sequence (absolute)
sInt dpos; // offset for equal sequence (as written)
sInt write; // bytes needed to write pattern (approx.)
sU16 seq; // first two characters of sequence to look for
si=0; // initialize (important)
di=0;
ri=0;
data = 0; // initialize (unimportant, only to get rid of warnings)
size = 0;
pos = 0;
dpos = 0;
write = 0;
while(si<ssize && di<dsize)
{
mode = 0;
if(ri+si+1<ssize && s[ri+si]==s[ri+si+1]) // find run
{
size=2;
data = s[ri+si];
while(ri+si+size<ssize && data==s[ri+si+size])
size++;
mode = 3;
write = 2;
if(data==0)
write = 1;
}
if(ri+si+1<ssize && mode==0) // find equal seq
{
size = 0;
j = ri+si-2;
jmin = ri+si-scan;
if(jmin<0)
jmin = 0;
seq = ((sU16 *) (s+ri+si))[0];
while(j>=jmin)
{
if(((sU16 *)(s+j))[0]==seq)
{
i=0;
imax = 255+4;
imax = sMin(imax,ssize-ri-si);
imax = sMin(imax,ri+si-j);
// while(ri+si+i<ssize && s[j+i]==s[ri+si+i] && j+i<ri+si && i<255+4)
sVERIFY(ri+si+imax<=ssize);
while(i<imax && s[j+i]==s[ri+si+i])
i++;
if(i>size)
{
size = i;
pos = j;
mode = 1;
}
}
j--;
}
if(mode==1)
{
write = 2;
dpos = si+ri-pos-size;
if(dpos > 15)
write = 3;
if(dpos > 255)
write = 4;
if(dpos > 0x3fff || write>size)
mode = 0;
}
}
if((mode!=0 && (ri==0 || size>write)) || ri+si==ssize) // is it worth writing?
{
while(ri>0) // write uncompressed data
{
if(ri>15+1)
{
i = sMin(ri,0x7ff+1+15);
d[di++] = 0xf0 | ((i-1-15)>>8);
d[di++] = (i-1-15)&0xff;
}
else
{
i = ri;
d[di++] = 0xc0|(ri-1);
}
ri-=i;
while(i>0)
{
d[di++] = s[si++];
i--;
}
}
if(mode==1) // write sequence
{
if(size<=3+2 && dpos<=15)
{
d[di++] = 0x80 | ((size-2)<<4) | (dpos);
}
else if(size<=63+3 && dpos<=255)
{
d[di++] = 0x40 | (size-3);
d[di++] = dpos;
}
else if(size<=255+4 && dpos<=0x3fff)
{
d[di++] = 0x00 | (dpos>>8);
d[di++] = (dpos)&0xff;
d[di++] = size-4;
}
si+=size;
}
if(mode==3) // write fill
{
si+=size;
while(size>0)
{
if(data==0 && size<=15+1)
{
d[di++] = 0xe0 | (size-1);
size=0;
}
else if(size<=15+2)
{
d[di++] = 0xd0 | (size-2);
d[di++] = data;
size=0;
}
else
{
i = sMin(size,0x7ff+2+15);
d[di++] = 0xf8 | ((i-2-15)>>8);
d[di++] = (i-2-15)&0xff;
d[di++] = data;
size-=i;
}
}
}
}
else
ri++;
}
dsize = di;
return (si==ssize);
}
/****************************************************************************/
sBool sDeCompES(sU8 *s,sU8 *d,sInt ssize,sInt &dsize,sBool verify)
{
sInt val; // code byte
sInt si; // source stream index
sInt di; // destination stream index
sInt mode; // mode of operation; 1=dest copy, 2=source copy, 3=fill
sInt pos; // offset for dest copy mode
sInt size; // number of bytes (all modes)
sInt data; // data for fill mode
sInt i; // :-)
si=0; // initialize (important)
di=0;
data = 0; // initialize (unimportant, only to get rid of warnings)
pos = 0;
while(si<ssize)
{
val = s[si++];
if(val&0x80)
{
if(val&0x40)
{
if(val&0x20)
{
if(val&0x10)
{
if(val&0x08) // 11111xxx
{
mode = 3;
size = ((val&0x07)<<8)+s[si++]+2+15;
data = s[si++];
}
else // 11110xxx
{
mode = 2;
size = ((val&0x07)<<8)+s[si++]+1+15;
}
}
else // 1110xxxx
{
mode = 3;
size = (val&0x0f)+1;
data = 0;
}
}
else
{
if(val&0x10) // 1101xxxx
{
mode = 3;
size = (val&0x0f)+2;
data = s[si++];
}
else // 1100xxxx
{
mode = 2;
size = (val&0x0f)+1;
}
}
}
else // 10xxxxxx
{
mode = 1;
size = ((val&0x30)>>4)+2;
pos = ((val&0x0f)+size);
}
}
else
{
if(val&0x40) // 01xxxxxx
{
mode = 1;
size = (val&0x3f)+3;
pos = (s[si++]+size);
}
else // 00xxxxxx
{
mode = 1;
pos = ((val&0x3f)<<8)+s[si++];
size = s[si++]+4;
pos = (pos+size);
}
}
if(di+size>dsize)
return sFALSE;
switch(mode)
{
case 1: // copy dest
if(verify)
{
if(sCmpMem(d+di,d+di-pos,size)!=0)
return sFALSE;
}
else
{
sCopyMem(d+di,d+di-pos,size);
}
di+=size;
break;
case 2: // copy source
if(verify)
{
if(sCmpMem(d+di,s+si,size)!=0)
return sFALSE;
}
else
{
sCopyMem(d+di,s+si,size);
}
di+=size;
si+=size;
break;
case 3: // fill
if(verify)
{
for(i=0;i<size;i++)
if(d[di+i]!=data)
return sFALSE;
}
else
{
sSetMem(d+di,data,size);
}
di+=size;
break;
}
}
if(dsize==0) // return real decompressed size if not given
dsize = di;
return (si==ssize) && (di==dsize); // return sFALSE if something is strange
}
/****************************************************************************/
/*** ***/
/*** Hooks ***/
/*** ***/
/****************************************************************************/
sHooksBase::sHooksBase(sInt max)
{
Hooks = new sStaticArray<HookInfo>;
Hooks->HintSize(max);
}
sHooksBase::~sHooksBase()
{
if(this) delete Hooks;
}
void sHooksBase::_Add(void *h,void *user)
{
if(this)
{
HookInfo nfo;
nfo.Hook = h;
nfo.User = user;
Hooks->AddTail(nfo);
}
}
void sHooksBase::_Rem(void *h,void *user)
{
if(this)
{
HookInfo *hi;
sFORALL(*Hooks,hi)
{
if(hi->Hook == h && (!user || hi->User == user))
{
Hooks->RemAtOrder(_i);
break;
}
}
}
}
/****************************************************************************/
/*** ***/
/*** Arithmetic Functions ***/
/*** ***/
/****************************************************************************/
#include <math.h>
#include <string.h>
#ifndef sHASINTRINSIC_SETMEM
void sSetMem(void *dd,sInt s,sInt c) { memset(dd,s,c); }
#endif
#ifndef sHASINTRINSIC_COPYMEM
void sCopyMem(void *dd,const void *ss,sInt c) { memcpy(dd,ss,c); }
#endif
void sMoveMem(void *dd,const void *ss,sInt c) { memmove(dd,ss,c); }
#ifndef sHASINTRINSIC_CMPMEM
sInt sCmpMem(const void *dd,const void *ss,sInt c) { return memcmp(dd,ss,c); }
#endif
// int
#ifndef sHASINTRINSIC_MULDIV
sInt sMulDiv(sInt a,sInt b,sInt c) { return sS64(a)*b/c; }
#endif
#ifndef sHASINTRINSIC_MULDIVU
sU32 sMulDivU(sU32 a,sU32 b,sU32 c) { return sU64(a)*b/c; }
#endif
#ifndef sHASINTRINSIC_MULSHIFT
sInt sMulShift(sInt a,sInt b) { return (sS64(a)*b)>>16; }
#endif
#ifndef sHASINTRINSIC_MULSHIFTU
sU32 sMulShiftU(sU32 a,sU32 b) { return (sU64(a)*b)>>16; }
#endif
#ifndef sHASINTRINSIC_DIVSHIFT
sInt sDivShift(sInt a,sInt b) { return (sS64(a)<<16)/b; }
#endif
// float fiddling
#ifndef sHASINTRINSIC_ABSF
sF32 sAbs(sF32 f) { return fabs(f); }
#endif
#ifndef sHASINTRINSIC_MINF
sF32 sMinF(sF32 a,sF32 b) { return sMin(a,b); }
#endif
#ifndef sHASINTRINSIC_MAXF
sF32 sMaxF(sF32 a,sF32 b) { return sMax(a,b); }
#endif
#ifndef sHASINTRINSIC_MOD
sF32 sMod(sF32 over,sF32 under) { return fmod(over,under); }
#endif
sInt sAbsMod(sInt over,sInt under) { return (over >= 0)?(over % under):(under-(-over % under)); }
sF32 sAbsMod(sF32 over,sF32 under) { return (over >= 0.0f)?sMod(over,under):(under-sMod(-over,under)); }
#ifndef sHASINTRINSIC_DIVMOD
void sDivMod(sF32 over,sF32 under,sF32 &div,sF32 &mod)
{
div = over/under;
mod = sMod(over,under);
}
#endif
#ifndef sHASINTRINSIC_SETFLOATLP
void sSetFloat()
{
#if !sCONFIG_OPTION_NPP
#if sPLATFORM==sPLAT_WINDOWS
#if sCONFIG_64BIT
#pragma warning(push)
#pragma warning(disable : 4996)
_clearfp();
_controlfp(_RC_NEAR|_EM_INVALID|_EM_DENORMAL|_EM_ZERODIVIDE|_EM_OVERFLOW|_EM_UNDERFLOW|_EM_INEXACT,_MCW_RC|_MCW_EM);
#pragma warning(pop)
#else
__asm
{
fclex;
push 0103fh; // round to nearest even + single precision
fldcw [esp];
pop eax;
}
#endif
#endif
#endif
}
#ifndef sHASINTRINSIC_FRAC_FF
void sFrac(sF32 val,sF32 &frac,sF32 &full)
{
frac = modff(val,&full);
}
#endif
#ifndef sHASINTRINSIC_FRAC_FI
void sFrac(sF32 val,sF32 &frac,sInt &full)
{
sF32 f;
frac = modff(val,&f);
full = sInt(f);
}
#endif
#ifndef sHASINTRINSIC_FRAC
sF32 sFrac(sF32 val)
{
sF32 f;
return modff(val,&f);
}
#endif
#ifndef sHASINTRINSIC_ROUNDDOWN
sF32 sRoundDown(sF32 f) { return floorf(f); }
#endif
#ifndef sHASINTRINSIC_ROUNDUP
sF32 sRoundUp(sF32 f) { return ceilf(f); }
#endif
#ifndef sHASINTRINSIC_ROUNDZERO
sF32 sRoundZero(sF32 f) { return (f>=0.0f)?sFFloor(f):sFCeil(f); }
#endif
#ifndef sHASINTRINSIC_ROUNDNEAR
sF32 sRoundNear(sF32 f) { return floorf(f+0.5f); }
#endif
#ifndef sHASINTRINSIC_ROUNDDOWNI
sInt sRoundDownInt(sF32 f) { return sInt(floorf(f)); }
#endif
#ifndef sHASINTRINSIC_ROUNDUPI
sInt sRoundUpInt(sF32 f) { return sInt(ceilf(f)); }
#endif
#ifndef sHASINTRINSIC_ROUNDZEROI
sInt sRoundZeroInt(sF32 f) { return sInt(f); }
#endif
#ifndef sHASINTRINSIC_ROUNDNEARI
sInt sRoundNearInt(sF32 f) { return sInt(floorf(f+0.5f)); }
#endif
#ifndef sHASINTRINSIC_SELECT
#ifndef sHASINTRINSIC_SELECTEQ
sF32 sSelectEQ(sF32 a,sF32 b,sF32 t,sF32 f) { return (a==b) ? t : f; }
#endif
#ifndef sHASINTRINSIC_SELECTNE
sF32 sSelectNE(sF32 a,sF32 b,sF32 t,sF32 f) { return (a!=b) ? t : f; }
#endif
#ifndef sHASINTRINSIC_SELECTGT
sF32 sSelectGT(sF32 a,sF32 b,sF32 t,sF32 f) { return (a>=b) ? t : f; }
#endif
#ifndef sHASINTRINSIC_SELECTGE
sF32 sSelectGE(sF32 a,sF32 b,sF32 t,sF32 f) { return (a> b) ? t : f; }
#endif
#ifndef sHASINTRINSIC_SELECTLT
sF32 sSelectLT(sF32 a,sF32 b,sF32 t,sF32 f) { return (a<=b) ? t : f; }
#endif
#ifndef sHASINTRINSIC_SELECTLE
sF32 sSelectLE(sF32 a,sF32 b,sF32 t,sF32 f) { return (a< b) ? t : f; }
#endif
#endif
#ifndef sHASINTRINSIC_SELECT0
#ifndef sHASINTRINSIC_SELECTEQ0
sF32 sSelectEQ(sF32 a,sF32 b,sF32 t) { return (a==b) ? t : 0; }
#endif
#ifndef sHASINTRINSIC_SELECTNE0
sF32 sSelectNE(sF32 a,sF32 b,sF32 t) { return (a!=b) ? t : 0; }
#endif
#ifndef sHASINTRINSIC_SELECTGT0
sF32 sSelectGT(sF32 a,sF32 b,sF32 t) { return (a>=b) ? t : 0; }
#endif
#ifndef sHASINTRINSIC_SELECTGE0
sF32 sSelectGE(sF32 a,sF32 b,sF32 t) { return (a> b) ? t : 0; }
#endif
#ifndef sHASINTRINSIC_SELECTLT0
sF32 sSelectLT(sF32 a,sF32 b,sF32 t) { return (a<=b) ? t : 0; }
#endif
#ifndef sHASINTRINSIC_SELECTLE0
sF32 sSelectLE(sF32 a,sF32 b,sF32 t) { return (a< b) ? t : 0; }
#endif
#endif
// non-trivial float
#ifndef sHASINTRINSIC_SQRT
sF32 sSqrt(sF32 f) { return sqrtf(f); }
#endif
#ifndef sHASINTRINSIC_RSQRT
sF32 sRSqrt(sF32 f) { return 1.0f/sqrtf(f); }
#endif
#ifndef sHASINTRINSIC_LOG
sF32 sLog(sF32 f) { return logf(f); }
#endif
#ifndef sHASINTRINSIC_LOG2
sF32 sLog2(sF32 f) { return logf(f)/0.69314718055994530941723212145818f; }
#endif
#ifndef sHASINTRINSIC_LOG10
sF32 sLog10(sF32 f) { return log10f(f); }
#endif
#ifndef sHASINTRINSIC_EXP
sF32 sExp(sF32 f) { return expf(f); }
#endif
#ifndef sHASINTRINSIC_POW
sF32 sPow(sF32 a,sF32 b) { return powf(a,b); }
#endif
#ifndef sHASINTRINSIC_SIN
sF32 sSin(sF32 f) { return sinf(f); }
#endif
#ifndef sHASINTRINSIC_COS
sF32 sCos(sF32 f) { return cosf(f); }
#endif
#ifndef sHASINTRINSIC_TAN
sF32 sTan(sF32 f) { return tanf(f); }
#endif
#ifndef sHASINTRINSIC_SINCOS
void sSinCos(sF32 f,sF32 &s,sF32 &c) { s=sinf(f); c=cosf(f); }
#endif
#ifndef sHASINTRINSIC_ASIN
sF32 sASin(sF32 f) { return asinf(f); }
#endif
#ifndef sHASINTRINSIC_ACOS
sF32 sACos(sF32 f) { return acosf(f); }
#endif
#ifndef sHASINTRINSIC_ATAN
sF32 sATan(sF32 f) { return (sF32) atan(f); }
#endif
#ifndef sHASINTRINSIC_ATAN2
sF32 sATan2(sF32 a,sF32 b){ return (sF32) atan2(a,b); }
#endif
// non-trivial float, fast
#ifndef sHASINTRINSIC_FSQRT
sF32 sFSqrt(sF32 f) { return sqrtf(f); }
#endif
#ifndef sHASINTRINSIC_FRSQRT
sF32 sFRSqrt(sF32 f) { return 1.0f/sqrtf(f); }
#endif
#ifndef sHASINTRINSIC_FLOG
sF32 sFLog(sF32 f) { return logf(f); }
#endif
#ifndef sHASINTRINSIC_FLOG2
sF32 sFLog2(sF32 f) { return logf(f)/0.69314718055994530941723212145818f; }
#endif
#ifndef sHASINTRINSIC_FLOG10
sF32 sFLog10(sF32 f) { return log10f(f); }
#endif
#ifndef sHASINTRINSIC_FEXP
sF32 sFExp(sF32 f) { return expf(f); }
#endif
#ifndef sHASINTRINSIC_FPOW
sF32 sFPow(sF32 a,sF32 b) { return powf(a,b); }
#endif
#ifndef sHASINTRINSIC_FSIN
sF32 sFSin(sF32 f) { return sinf(f); }
#endif
#ifndef sHASINTRINSIC_FCOS
sF32 sFCos(sF32 f) { return cosf(f); }
#endif
#ifndef sHASINTRINSIC_FTAN
sF32 sFTan(sF32 f) { return tanf(f); }
#endif
#ifndef sHASINTRINSIC_FSINCOS
void sFSinCos(sF32 f,sF32 &s,sF32 &c) { s=sinf(f); c=cosf(f); }
#endif
#ifndef sHASINTRINSIC_FASIN
sF32 sFASin(sF32 f) { return asinf(f); }
#endif
#ifndef sHASINTRINSIC_FACOS
sF32 sFACos(sF32 f) { return acosf(f); }
#endif
#ifndef sHASINTRINSIC_FATAN
sF32 sFATan(sF32 f) { return atanf(f); }
#endif
#ifndef sHASINTRINSIC_FATAN2
sF32 sFATan2(sF32 a,sF32 b){ return atan2f(a,b); }
#endif
#endif
/****************************************************************************/