From 23ee5ec59c6f2a4045f7b2b0dbc625562ce80ae7 Mon Sep 17 00:00:00 2001 From: Alexis Naveros Date: Sat, 21 Nov 2015 04:07:11 -0500 Subject: [PATCH] ecere/gfx/newFonts: Initial eC port of font engine by Alexis Naveros - Includes FontManager, DrawManager, TextureManager, FontRenderer and AtlasBuilder - Also includes Common Code and Memory Manager (still in C) - Will be integrated into our existing font engine ( Font look up with FontConfig / Windows Registry, Font substitution, Script shaping with HarfBuzz, etc.) - Will also need to be integrated into our graphics API-agnostic Display system for our next generation graphics engine --- ecere/ecere.epj | 29 + ecere/src/gfx/newFonts/atlasBuilder.ec | 225 ++ ecere/src/gfx/newFonts/cc/cc.c | 2999 ++++++++++++++++++++++++++ ecere/src/gfx/newFonts/cc/cc.h | 1516 +++++++++++++ ecere/src/gfx/newFonts/cc/cchybridsort.h | 302 +++ ecere/src/gfx/newFonts/cc/ccmergesort.h | 215 ++ ecere/src/gfx/newFonts/cc/cpuconfig.h | 104 + ecere/src/gfx/newFonts/cc/mm.c | 3430 ++++++++++++++++++++++++++++++ ecere/src/gfx/newFonts/cc/mm.h | 786 +++++++ ecere/src/gfx/newFonts/cc/mmatomic.h | 2475 +++++++++++++++++++++ ecere/src/gfx/newFonts/cc/mmbitmap.c | 334 +++ ecere/src/gfx/newFonts/cc/mmbitmap.h | 211 ++ ecere/src/gfx/newFonts/cc/mmthread.h | 530 +++++ ecere/src/gfx/newFonts/drawManager.ec | 862 ++++++++ ecere/src/gfx/newFonts/fontManager.ec | 1666 +++++++++++++++ ecere/src/gfx/newFonts/fontRenderer.ec | 196 ++ ecere/src/gfx/newFonts/textureManager.ec | 154 ++ 17 files changed, 16034 insertions(+) create mode 100644 ecere/src/gfx/newFonts/atlasBuilder.ec create mode 100644 ecere/src/gfx/newFonts/cc/cc.c create mode 100644 ecere/src/gfx/newFonts/cc/cc.h create mode 100644 ecere/src/gfx/newFonts/cc/cchybridsort.h create mode 100644 ecere/src/gfx/newFonts/cc/ccmergesort.h create mode 100644 ecere/src/gfx/newFonts/cc/cpuconfig.h create mode 100644 ecere/src/gfx/newFonts/cc/mm.c create mode 100644 ecere/src/gfx/newFonts/cc/mm.h create mode 100644 ecere/src/gfx/newFonts/cc/mmatomic.h create mode 100644 ecere/src/gfx/newFonts/cc/mmbitmap.c create mode 100644 ecere/src/gfx/newFonts/cc/mmbitmap.h create mode 100644 ecere/src/gfx/newFonts/cc/mmthread.h create mode 100644 ecere/src/gfx/newFonts/drawManager.ec create mode 100644 ecere/src/gfx/newFonts/fontManager.ec create mode 100644 ecere/src/gfx/newFonts/fontRenderer.ec create mode 100644 ecere/src/gfx/newFonts/textureManager.ec diff --git a/ecere/ecere.epj b/ecere/ecere.epj index c978092..2ca0c6c 100644 --- a/ecere/ecere.epj +++ b/ecere/ecere.epj @@ -2150,6 +2150,35 @@ if distributed with the Ecere SDK Windows installer. } ] }, + { + "Folder" : "newFonts", + "Files" : [ + { + "Folder" : "cc", + "Files" : [ + "cc.c", + "cc.h", + "cchybridsort.h", + "ccmergesort.h", + "cpuconfig.h", + "mm.c", + "mm.h", + "mmatomic.h", + "mmthread.h" + ] + }, + "atlasBuilder.ec", + "drawManager.ec", + "fontManager.ec", + "fontRenderer.ec", + "textureManager.ec" + ], + "Options" : { + "IncludeDirs" : [ + "src/gfx/newFonts/cc" + ] + } + }, "Bitmap.ec", "BitmapResource.ec", "Color.ec", diff --git a/ecere/src/gfx/newFonts/atlasBuilder.ec b/ecere/src/gfx/newFonts/atlasBuilder.ec new file mode 100644 index 0000000..2ee8353 --- /dev/null +++ b/ecere/src/gfx/newFonts/atlasBuilder.ec @@ -0,0 +1,225 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +import "instance" + +struct AtlasNode { short x, y, width; }; + +class AtlasBuilder +{ + int width, height; + AtlasNode *nodes; + int nodecount; + int nodealloc; + + // Create atlas of given width and height, internal node count will grow as necessary + bool create( int w, int h, int nodealloc ) + { + bool result = false; + + width = w; + height = h; + + // Allocate space for skyline nodes + if( nodealloc < 32 ) + nodealloc = 32; + if( ( nodes = new0 AtlasNode[nodealloc] ) ) + { + nodecount = 0; + this.nodealloc = nodealloc; + + // Init root node. + nodes[0].width = (short)w; + nodecount++; + + result = true; + } + return result; + } + + ~AtlasBuilder( ) + { + delete nodes; + } + + static bool insertNode( int nodeindex, int x, int y, int width ) + { + bool result = false; + + // Insert node + if( nodecount >= nodealloc ) + { + nodealloc <<= 1; + nodes = renew nodes AtlasNode[nodealloc]; + } + if(nodes) + { + int i; + for( i = nodecount; i > nodeindex ; i-- ) + nodes[i] = nodes[i-1]; + nodes[nodeindex].x = (short)x; + nodes[nodeindex].y = (short)y; + nodes[nodeindex].width = (short)width; + nodecount++; + + result = true; + } + return result; + } + + static void removeNode( int nodeindex ) + { + if( nodecount) + { + int i; + for( i = nodeindex ; i < nodecount-1 ; i++ ) + nodes[i] = nodes[i+1]; + nodecount--; + } + } + + static bool addSkylineLevel( int nodeindex, int x, int y, int width, int height ) + { + int i, shrink; + + // Insert new node + if( insertNode( nodeindex, x, y+height, width ) == 0 ) + return false; + + // Delete skyline segments that fall under the shadow of the new segment. + for( i = nodeindex+1 ; i < nodecount ; i++ ) + { + if( nodes[i].x < ( nodes[i-1].x + nodes[i-1].width ) ) + { + shrink = nodes[i-1].x + nodes[i-1].width - nodes[i].x; + nodes[i].x += (short)shrink; + nodes[i].width -= (short)shrink; + if( nodes[i].width <= 0 ) + { + removeNode( i ); + i--; + } + else + break; + } + else + break; + } + + // Merge same height skyline segments that are next to each other. + for( i = 0 ; i < nodecount - 1 ; ) + { + if( nodes[i].y != nodes[i+1].y ) + i++; + else + { + nodes[i].width += nodes[i+1].width; + removeNode( i+1 ); + } + } + return 1; + } + + static int rectFits( int nodeindex, int width, int height ) + { + int x, y, rem; + /* Checks if there is enough space at the location of skyline span 'i', */ + /* and return the max height of all skyline spans under that at that location, */ + /* (think tetris block being dropped at that position). Or -1 if no space found. */ + x = nodes[nodeindex].x; + y = nodes[nodeindex].y; + if( ( x + width ) > this.width ) + return -1; + for( rem = width ; rem > 0 ; nodeindex++ ) + { + if( nodeindex == nodecount ) + return -1; + if( y < this.nodes[nodeindex].y ) + y = nodes[nodeindex].y; + if( ( y + height ) > this.height ) + return -1; + rem -= nodes[nodeindex].width; + } + return y; + } + + // Place a rectangle of specified dimensions, return 1 on success, retx&rety store offsets + bool addRect( int width, int height, int *retx, int *rety ) + { + int besth, bestw, besti, bestx, besty, y, nodeindex; + + besth = this.height; + bestw = this.width; + besti = -1; + bestx = -1; + besty = -1; + + // Bottom left fit heuristic. + for( nodeindex = 0 ; nodeindex < this.nodecount ; nodeindex++ ) + { + y = rectFits( nodeindex, width, height ); + if( y != -1 ) + { + if( ( ( y + height ) < besth ) || ( ( ( y + height ) == besth ) && ( this.nodes[nodeindex].width < bestw ) ) ) + { + besti = nodeindex; + bestw = this.nodes[nodeindex].width; + besth = y + height; + bestx = this.nodes[nodeindex].x; + besty = y; + } + } + } + + if( besti == -1 ) + return 0; + + // Perform the actual packing. + if( addSkylineLevel( besti, bestx, besty, width, height ) == 0 ) + return 0; + + *retx = bestx; + *rety = besty; + + return 1; + } + + // Expand atlas to given dimensions + void expand( int width, int height ) + { + // Insert node for empty space + if( width > this.width ) + insertNode( nodecount, this.width, 0, width - this.width ); + this.width = width; + this.height = height; + } + + // Clean up the atlas + void reset( int width, int height ) + { + this.width = width; + this.height = height; + nodecount = 0; + + // Init root node. + nodes[0].x = 0; + nodes[0].y = 0; + nodes[0].width = (short)width; + nodecount++; + } + + // Return the maximum y value of all allocated rectangles + int getAtlasMaxHeight() + { + int i, maxy = 0; + + for( i = 0 ; i < nodecount ; i++ ) + { + if( maxy < nodes[i].y ) + maxy = nodes[i].y; + } + return maxy; + } +} diff --git a/ecere/src/gfx/newFonts/cc/cc.c b/ecere/src/gfx/newFonts/cc/cc.c new file mode 100644 index 0000000..3b38b60 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cc.c @@ -0,0 +1,2999 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cc.h" + +#if CC_UNIX + #include + #include + #include + #include + #include + #include /* For uname() */ + #include /* For readdir() */ + #include /* For statvfs( ) */ +#elif CC_WINDOWS + #include + #include + #include + #include +#else + #error Unknown/Unsupported platform! +#endif + + + +//// + + + +const size_t ccTypeSize[CC_TYPE_COUNT] = +{ + [CC_TYPE_UINT8] = sizeof(uint8_t), + [CC_TYPE_INT8] = sizeof(int8_t), + [CC_TYPE_UINT16] = sizeof(uint16_t), + [CC_TYPE_INT16] = sizeof(int16_t), + [CC_TYPE_UINT32] = sizeof(uint32_t), + [CC_TYPE_INT32] = sizeof(int32_t), + [CC_TYPE_UINT64] = sizeof(uint64_t), + [CC_TYPE_INT64] = sizeof(int64_t), + [CC_TYPE_FLOAT] = sizeof(float), + [CC_TYPE_DOUBLE] = sizeof(double) +}; + + +//// + + +#define CC_HASH_READ8(d,o) ((uint32_t)(((uint8_t *)d)[o])) +#define CC_HASH_AREAD16(d,o) ((uint32_t)(*((uint16_t *)ADDRESS(d,o)))) +#define CC_HASH_UREAD16(d,o) ((((uint32_t)(((uint8_t *)(d))[o+1]))<<8)+(uint32_t)(((uint8_t *)(d))[o])) + +uint32_t ccHash32Data( void *data, int size ) +{ + uint32_t hash; + int rem; + rem = size & 3; + size >>= 2; + hash = 0; + if( !( ( (uintptr_t)data ) & 0x1 ) ) + { + for( ; size ; size-- ) + { + hash += CC_HASH_AREAD16( data, 0 ); + hash = ( hash << 16 ) ^ ( ( CC_HASH_AREAD16( data, 2 ) << 11 ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + } + else + { + for( ; size ; size-- ) + { + hash += CC_HASH_UREAD16( data, 0 ); + hash = ( hash << 16 ) ^ ( ( CC_HASH_UREAD16( data, 2 ) << 11 ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + } + switch( rem ) + { + case 3: + hash += CC_HASH_UREAD16( data, 0 ); + hash ^= hash << 16; + hash ^= CC_HASH_READ8( data, 2 ) << 18; + hash += hash >> 11; + break; + case 2: + hash += CC_HASH_UREAD16( data, 0 ); + hash ^= hash << 11; + hash += hash >> 17; + break; + case 1: + hash += CC_HASH_READ8( data, 0 ); + hash ^= hash << 10; + hash += hash >> 1; + break; + case 0: + break; + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Int32( uint32_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( i & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Int64( uint64_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( i >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( i >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( i & 0xFFFF000000000000LL ) >> 37 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Array32( uint32_t *data, int count ) +{ + uint32_t hash; + hash = 0; + for( ; count ; count-- ) + { + hash += *data & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( *data & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +uint32_t ccHash32Array64( uint64_t *data, int count ) +{ + uint32_t hash; + uint64_t v; + hash = 0; + for( ; count ; count-- ) + { + v = *data; + hash += v & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( v >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( v >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( v & 0xFFFF000000000000LL ) >> 37 ); + hash += hash >> 11; + data = ADDRESS( data, 8 ); + } + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + + +//// + + +int ccMemCmp( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + return 0; + } + return 1; +} + +int ccMemCmp32( uint32_t *s0, uint32_t *s1, int count ) +{ + int i; + for( i = 0 ; i < count ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + } + return 1; +} + +int ccMemCmp64( uint64_t *s0, uint64_t *s1, int count ) +{ + int i; + for( i = 0 ; i < count ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + } + return 1; +} + +int ccMemCmpSize( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + break; + } + return i; +} + + +//// + + +uint8_t ccLog2Int8( uint8_t v ) +{ + uint8_t r = 0; + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint16_t ccLog2Int16( uint16_t v ) +{ + uint16_t r = 0; + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint32_t ccLog2Int32( uint32_t v ) +{ + uint32_t r = 0; + if( v & 0xFFFF0000 ) + { + v >>= 16; + r |= 16; + } + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + +uint64_t ccLog2Int64( uint64_t v ) +{ + uint64_t r = 0; + if( v & 0xFFFFFFFF00000000LL ) + { + v >>= 32; + r |= 32; + } + if( v & 0xFFFF0000 ) + { + v >>= 16; + r |= 16; + } + if( v & 0xFF00 ) + { + v >>= 8; + r |= 8; + } + if( v & 0xF0 ) + { + v >>= 4; + r |= 4; + } + if( v & 0xC ) + { + v >>= 2; + r |= 2; + } + if( v & 0x2 ) + { + v >>= 1; + r |= 1; + } + return r; +} + + +//// + + +#define CC_CHAR_IS_CONTROL(c) ((c)<' ') +#define CC_CHAR_IS_DELIMITER(c) ((c)<=' ') + + +void ccStrLowCase( char *str, int length ) +{ + int i; + for( i = 0 ; i < length ; i++ ) + { + if( ( str[i] >= 'A' ) && ( str[i] <= 'Z' ) ) + str[i] += 'a' - 'A'; + } + return; +} + + +void ccStrLowCopy( char *dst, char *src, int length ) +{ + int i; + for( i = 0 ; i < length ; i++ ) + { + dst[i] = src[i]; + if( ( src[i] >= 'A' ) && ( src[i] <= 'Z' ) ) + dst[i] = src[i] + 'a' - 'A'; + } + dst[i] = 0; + return; +} + + +int ccStrCmpEqual( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? 0 : 1 ); + else if( !( s1 ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + + +int ccStrCmpEqualTest( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? 0 : 1 ); + else if( !( s1 ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + + +int ccStrCmpStdTest( char *s0, char *s1 ) +{ + int i; + if( !( s0 ) ) + return ( s1 ? -1 : 0 ); + else if( !( s1 ) ) + return 1; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return( s0[i] > s1[i] ? 1 : -1 ); + if( !( s0[i] ) ) + break; + } + return 0; +} + + +char *ccStrCmpWord( char *str, char *word ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( !( word[i] ) ) + return str + i; + if( str[i] != word[i] ) + break; + } + return 0; +} + + +char *ccStrCmpSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + return str + i; + if( str[i] != seq[i] ) + break; + } + return 0; +} + + +char *ccStrMatchSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + { + if( str[i] ) + break; + return str + i; + } + if( str[i] != seq[i] ) + break; + } + return 0; +} + + +char *ccSeqCmpSeq( char *s1, char *s2, int s1length, int s2length ) +{ + int i; + if( s1length != s2length ) + return 0; + for( i = 0 ; i < s1length ; i++ ) + { + if( s1[i] != s2[i] ) + break; + } + return s1 + i; +} + + +int ccStrWordCmpWord( char *s1, char *s2 ) +{ + int i; + for( i = 0 ; ; i++ ) + { + if( s1[i] != s2[i] ) + break; + if( CC_CHAR_IS_DELIMITER( s1[i] ) ) + { + if( CC_CHAR_IS_DELIMITER( s2[i] ) ) + return 1; + else + return 0; + } + } + if( ( CC_CHAR_IS_DELIMITER( s1[i] ) ) && ( CC_CHAR_IS_DELIMITER( s2[i] ) ) ) + return 1; + return 0; +} + + +char *ccStrLowCmpWord( char *str, char *word ) +{ + int i; + char c1, c2; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( !( word[i] ) ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str + i; + else + return 0; + } + c1 = str[i]; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + c2 = word[i]; + if( ( c2 >= 'A' ) && ( c2 <= 'Z' ) ) + c2 += 'a' - 'A'; + if( c1 != c2 ) + break; + } + return 0; +} + + +char *ccStrLowCmpSeq( char *str, char *seq, int seqlength ) +{ + int i; + char c1, c2; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str + i; + else + return 0; + } + c1 = str[i]; + if( ( c1 >= 'A' ) && ( c1 <= 'Z' ) ) + c1 += 'a' - 'A'; + c2 = seq[i]; + if( ( c2 >= 'A' ) && ( c2 <= 'Z' ) ) + c2 += 'a' - 'A'; + if( c1 != c2 ) + break; + } + return 0; +} + + +char *ccStrFindStr( char *str0, char *str1 ) +{ + int i; + if( !( str0 ) ) + return 0; + for( ; *str0 ; str0++ ) + { + for( i = 0 ; ; i++ ) + { + if( !( str1[i] ) ) + return str0; + if( str0[i] != str1[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindStrSkip( char *str0, char *str1 ) +{ + int i; + if( !( str0 ) ) + return 0; + for( ; *str0 ; str0++ ) + { + for( i = 0 ; ; i++ ) + { + if( !( str1[i] ) ) + return str0 + i; + if( str0[i] != str1[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindSeq( char *str, char *seq, int seqlength ) +{ + int i; + if( !( str ) ) + return 0; + for( ; *str ; str++ ) + { + for( i = 0 ; ; i++ ) + { + if( i >= seqlength ) + return str; + if( str[i] != seq[i] ) + break; + } + } + return 0; +} + + +char *ccStrFindWord( char *str, char *word, int wordlength ) +{ + int i, wordflag; + wordflag = 0; + if( !( str ) ) + return 0; + for( ; *str ; str++ ) + { + if( CC_CHAR_IS_DELIMITER( *str ) ) + { + wordflag = 0; + continue; + } + else if( wordflag ) + continue; + for( i = 0 ; ; i++ ) + { + if( i >= wordlength ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + return str; + return 0; + } + if( str[i] != word[i] ) + { + wordflag = 1; + str += i; + break; + } + } + } + return 0; +} + + +int ccStrWordLength( char *str ) +{ + int i; + if( !( str ) ) + return 0; + for( i = 0 ; ; i++ ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + break; + } + return i; +} + + +int ccStrFindChar( char *str, char c ) +{ + int i; + if( !( str ) ) + return -1; + for( i = 0 ; str[i] ; i++ ) + { + if( str[i] == c ) + return i; + } + return -1; +} + + +int ccSeqFindChar( char *seq, int seqlen, char c ) +{ + int i; + for( i = 0 ; i < seqlen ; i++ ) + { + if( seq[i] == c ) + return i; + } + return -1; +} + + +int ccStrFindCharLast( char *str, char c ) +{ + int i, last; + if( !( str ) ) + return -1; + last = -1; + for( i = 0 ; str[i] ; i++ ) + { + if( str[i] == c ) + last = i; + } + return last; +} + + +int ccSeqFindCharLast( char *seq, int seqlen, char c ) +{ + int i, last; + last = -1; + for( i = 0 ; i < seqlen ; i++ ) + { + if( seq[i] == c ) + last = i; + } + return last; +} + + +char *ccSeqFindStr( char *seq, int seqlen, char *str ) +{ + int i; + for( ; seqlen ; seqlen--, seq++ ) + { + for( i = 0 ; i < seqlen ; i++ ) + { + if( !str[i] ) + return seq; + if( seq[i] != str[i] ) + break; + } + } + return 0; +} + + +char *ccSeqFindStrSkip( char *seq, int seqlen, char *str ) +{ + int i; + for( ; seqlen ; seqlen--, seq++ ) + { + for( i = 0 ; i < seqlen ; i++ ) + { + if( !str[i] ) + return seq + i; + if( seq[i] != str[i] ) + break; + } + } + return 0; +} + + +char *ccStrParam( char *str, int *retparamlen, int *retskiplen ) +{ + int i; + if( !( str ) ) + return 0; + if( str[0] == '"' ) + { + for( i = 1 ; ; i++ ) + { + if( str[i] == '"' ) + break; + if( CC_CHAR_IS_CONTROL( str[i] ) ) + return 0; + } + if( !( CC_CHAR_IS_DELIMITER( str[i+1] ) ) ) + return 0; + *retparamlen = i-1; + *retskiplen = i+1; + return str+1; + } + if( CC_CHAR_IS_DELIMITER( str[0] ) ) + return 0; + for( i = 1 ; ; i++ ) + { + if( CC_CHAR_IS_DELIMITER( str[i] ) ) + break; + } + *retparamlen = i; + *retskiplen = i; + return str; +} + + +int ccParseParameters( char *str, char **argv, int argcountmax ) +{ + int argc, paramlen, skiplen; + char *param; + + if( !( str ) ) + return 0; + for( argc = 0 ; argc < argcountmax ; ) + { + if( !( str = ccStrNextWordSameLine( str ) ) ) + break; + param = ccStrParam( str, ¶mlen, &skiplen ); + if( argv ) + argv[argc] = param; + argc++; + if( !( param ) ) + break; + if( !( param[paramlen] ) ) + break; + str += skiplen + 1; + } + + return argc; +} + + +int ccParseParametersCut( char *str, char **argv, int argcountmax ) +{ + int argc, paramlen, skiplen; + char *param; + + if( !( str ) ) + return 0; + for( argc = 0 ; argc < argcountmax ; ) + { + if( !( str = ccStrNextWordSameLine( str ) ) ) + break; + param = ccStrParam( str, ¶mlen, &skiplen ); + if( argv ) + argv[argc] = param; + argc++; + if( !( param ) ) + break; + if( !( param[paramlen] ) ) + break; + param[paramlen] = 0; + str += skiplen + 1; + } + + return argc; +} + + +char *ccStrNextWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrSkipWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( !( *str ) ) + return 0; + if( ( *str == ' ' ) || ( *str == '\t' ) || ( *str == '\n' ) || ( *str == '\r' ) ) + break; + } + return str; +} + + +char *ccStrEndWord( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( !( *str ) || ( *str == ' ' ) || ( *str == '\t' ) || ( *str == '\n' ) || ( *str == '\r' ) ) + break; + } + return str; +} + + +char *ccStrNextWordSameLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return 0; + if( *str == '\n' ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrNextParam( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( ( *str == 0 ) || ( *str != '\n' ) ) + return 0; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + break; + } + return str; +} + + +char *ccStrNextLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( *str == 0 ) + return str; + if( *str == '\n' ) + break; + } + return str + 1; +} + + +char *ccStrPassLine( char *str ) +{ + if( !( str ) ) + return 0; + for( ; ; str++ ) + { + if( ( *str == 0 ) || ( *str == '\n' ) ) + break; + if( !( CC_CHAR_IS_DELIMITER( *str ) ) ) + return 0; + } + return str; +} + + +int ccStrParseInt32( char *str, int32_t *retint ) +{ + int retval; + int64_t i; + retval = ccStrParseInt64( str, &i ); + if( ( i >= ((((int64_t)1)<<31)-1) ) || ( i < -(((int64_t)1)<<31) ) ) + return 0; + *retint = (int32_t)i; + return retval; +} + + +int ccSeqParseInt32( char *seq, int seqlength, int32_t *retint ) +{ + int retval; + int64_t i; + retval = ccSeqParseInt64( seq, seqlength, &i ); + if( ( i >= ((((int64_t)1)<<31)-1) ) || ( i < -(((int64_t)1)<<31) ) ) + return 0; + *retint = (int32_t)i; + return retval; +} + + +int ccStrParseInt64( char *str, int64_t *retint ) +{ + int negflag; + char c; + int64_t workint; + + *retint = 0; + if( !( str ) ) + return 0; + negflag = 0; + if( *str == '-' ) + negflag = 1; + str += negflag; + + workint = 0; + for( ; ; str++ ) + { + c = *str; + if( CC_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + if( workint >= (int64_t)0xcccccccccccccccLL ) + return 0; + workint = ( workint * 10 ) + ( c - '0' ); + } + + if( negflag ) + workint = -workint; + *retint = workint; + return 1; +} + + +int ccSeqParseInt64( char *seq, int seqlength, int64_t *retint ) +{ + int i, negflag; + char c; + int64_t workint; + + *retint = 0; + if( !( seqlength ) ) + return 0; + negflag = 0; + i = 0; + if( *seq == '-' ) + { + negflag = 1; + i = 1; + } + + workint = 0; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( CC_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + if( workint >= (int64_t)0xcccccccccccccccLL ) + return 0; + workint = ( workint * 10 ) + ( c - '0' ); + } + + if( negflag ) + workint = -workint; + *retint = workint; + return 1; +} + + +int ccStrParseFloat( char *str, float *retfloat ) +{ + int retval; + double d; + retval = ccStrParseDouble( str, &d ); + *retfloat = (float)d; + return retval; +} + + +int ccSeqParseFloat( char *seq, int seqlength, float *retfloat ) +{ + int retval; + double d; + retval = ccSeqParseDouble( seq, seqlength, &d ); + *retfloat = (float)d; + return retval; +} + + +int ccStrParseDouble( char *str, double *retdouble ) +{ + int negflag; + char c; + double workdouble; + double decfactor; + + *retdouble = 0.0; + if( !( str ) ) + return 0; + negflag = 0; + if( *str == '-' ) + negflag = 1; + str += negflag; + + workdouble = 0.0; + for( ; ; str++ ) + { + c = *str; + if( CC_CHAR_IS_DELIMITER( c ) ) + goto done; + if( c == '.' ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble = ( workdouble * 10.0 ) + (double)( c - '0' ); + } + + str++; + decfactor = 0.1; + for( ; ; str++ ) + { + c = *str; + if( CC_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble += (double)( c - '0' ) * decfactor; + decfactor *= 0.1; + } + + done: + + if( negflag ) + workdouble = -workdouble; + *retdouble = (double)workdouble; + return 1; +} + + +int ccSeqParseDouble( char *seq, int seqlength, double *retdouble ) +{ + int i, negflag; + char c; + double workdouble; + double decfactor; + + *retdouble = 0.0; + i = 0; + if( !( seq ) ) + return 0; + negflag = 0; + if( seq[i] == '-' ) + negflag = 1; + i += negflag; + + workdouble = 0.0; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( CC_CHAR_IS_DELIMITER( c ) ) + goto done; + if( c == '.' ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble = ( workdouble * 10.0 ) + (double)( c - '0' ); + } + + i++; + decfactor = 0.1; + for( ; i < seqlength ; i++ ) + { + c = seq[i]; + if( CC_CHAR_IS_DELIMITER( c ) ) + break; + if( ( c < '0' ) || ( c > '9' ) ) + return 0; + workdouble += (double)( c - '0' ) * decfactor; + decfactor *= 0.1; + } + + done: + + if( negflag ) + workdouble = -workdouble; + *retdouble = (double)workdouble; + return 1; +} + + +int ccStrParseHex( char *str, int hexchars ) +{ + int index, value, charvalue; + value = 0; + for( index = 0 ; index < hexchars ; index++ ) + { + value <<= 4; + charvalue = ccCharHexBase( str[index] ); + if( charvalue == -1 ) + return -1; + value += charvalue; + } + return value; +} + + +char *ccStrAllocPrintf( char *format, ... ) +{ + char *str; + int strsize, allocsize; + va_list ap; + + allocsize = 512; + str = malloc( allocsize ); + for( ; ; ) + { + va_start( ap, format ); + strsize = vsnprintf( str, allocsize, format, ap ); +#if CC_WINDOWS + if( strsize == -1 ) + strsize = allocsize << 1; +#endif + va_end( ap ); + if( strsize < allocsize ) + break; + allocsize = strsize + 2; + str = realloc( str, allocsize ); + } + + return str; +} + + +char *ccStrDup( char *str ) +{ + int len; + char *newstr; + if( !( str ) ) + return 0; + len = strlen( str ); + if( !( len ) ) + return 0; + newstr = malloc( ( len + 1 ) * sizeof(char) ); + memcpy( newstr, str, len + 1 ); + return newstr; +} + + +int ccUnicodeToUtf8( char *s, uint32_t unicode ) +{ + int retval; + retval = 0; + if( unicode < 0x80 ) + { + *s++ = unicode; + retval = 1; + } + else if( unicode < 0x800 ) + { + *s++ = 192 + ( unicode / 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 2; + } + else if( ( unicode - 0xd800u ) < 0x800 ) + retval = 0; + else if( unicode < 0x10000 ) + { + *s++ = 224 + ( unicode / 4096 ); + *s++ = 128 + ( ( unicode / 64 ) % 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 3; + } + else if( unicode < 0x110000 ) + { + *s++ = 240 + ( unicode / 262144 ); + *s++ = 128 + ( ( unicode / 4096 ) % 64 ); + *s++ = 128 + ( ( unicode / 64 ) % 64 ); + *s++ = 128 + ( unicode % 64 ); + retval = 4; + } + return retval; +} + + +/* Copyright (c) 2008-2010 Bjoern Hoehrmann */ +/* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */ + +/* Returns 1 when data is insufficient, send more bytes */ +uint32_t ccUtf8ToUnicode( uint32_t byte, uint32_t *state, uint32_t *retunicode ) +{ + uint32_t type; + static const unsigned char utf8d[] = + { + /* The first part of the table maps bytes to character classes that */ + /* to reduce the size of the transition table and create bitmasks. */ + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + /* The second part is a transition table that maps a combination */ + /* of a state of the automaton and a character class to a state. */ + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, + }; + type = utf8d[byte]; + *retunicode = ( (*state != 0) ? ( ( byte & 0x3fu ) | ( *retunicode << 6 ) ) : ( ( 0xff >> type ) & ( byte ) ) ); + *state = utf8d[ 256 + *state + type ]; + return *state; +} + + +//// + + +#define CC_SORT_SWAP(a,b) ({temp=table[b];table[b]=table[a];table[a]=temp;}) + +#define CC_SORT_STACK_DEPTH (512) + +#define CC_SORT_MIN_QSORT_COUNT (5) + +typedef struct +{ + void *table; + int count; +} ccQuickSortStack; + +static void ccQuickSortPart( void **table, int count, int (*sortfunc)( void *t0, void *t1 ) ) +{ + void *temp; + switch( count ) + { + case 4: + if( sortfunc( table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + if( sortfunc( table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( table[0], table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( sortfunc( table[2], table[3] ) ) + { + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( table[1], table[2] ) ) + CC_SORT_SWAP( 2, 1 ); + } + } + else + { + if( sortfunc( table[1], table[2] ) ) + { + CC_SORT_SWAP( 2, 1 ); + if( sortfunc( table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + } + } + break; + case 3: + if( sortfunc( table[0], table[1] ) ) + { + if( sortfunc( table[1], table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_SORT_SWAP( 2, 0 ); + } + else + { + if( sortfunc( table[0], table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_SORT_SWAP( 1, 0 ); + } + } + } + else + { + if( sortfunc( table[1], table[2] ) ) + { + if( sortfunc( table[0], table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_SORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + break; + case 2: + if( sortfunc( table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + break; + case 1: + case 0: + default: + break; + } + return; +} + + +void ccQuickSort( void **table, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ) +{ + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccQuickSortStack stack[CC_SORT_STACK_DEPTH]; + ccQuickSortStack *sp; + + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + return; + } + + sp = stack; + for( ; ; ) + { + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, leftcount, sortfunc ); + table += pivotindex; + count = rightcount; + if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + } + } + else if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( &table[pivotindex], rightcount, sortfunc ); + count = leftcount; + } + else if( leftcount < rightcount ) + { + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp++; + count = leftcount; + } + else + { + sp->table = table; + sp->count = leftcount; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + +static void ccQuickSortContextPart( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ) +{ + void *temp; + switch( count ) + { + case 4: + if( sortfunc( context, table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + if( sortfunc( context, table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( context, table[0], table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( sortfunc( context, table[2], table[3] ) ) + { + CC_SORT_SWAP( 3, 2 ); + if( sortfunc( context, table[1], table[2] ) ) + CC_SORT_SWAP( 2, 1 ); + } + } + else + { + if( sortfunc( context, table[1], table[2] ) ) + { + CC_SORT_SWAP( 2, 1 ); + if( sortfunc( context, table[2], table[3] ) ) + CC_SORT_SWAP( 3, 2 ); + } + } + break; + case 3: + if( sortfunc( context, table[0], table[1] ) ) + { + if( sortfunc( context, table[1], table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_SORT_SWAP( 2, 0 ); + } + else + { + if( sortfunc( context, table[0], table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_SORT_SWAP( 1, 0 ); + } + } + } + else + { + if( sortfunc( context, table[1], table[2] ) ) + { + if( sortfunc( context, table[0], table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_SORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + break; + case 2: + if( sortfunc( context, table[0], table[1] ) ) + CC_SORT_SWAP( 1, 0 ); + break; + case 1: + case 0: + default: + break; + } + return; +} + +void ccQuickSortContext( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ) +{ + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccQuickSortStack stack[CC_SORT_STACK_DEPTH]; + ccQuickSortStack *sp; + + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + return; + } + + sp = stack; + for( ; ; ) + { + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( context, table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( context, pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, leftcount, sortfunc, context ); + table += pivotindex; + count = rightcount; + if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + } + } + else if( rightcount < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( &table[pivotindex], rightcount, sortfunc, context ); + count = leftcount; + } + else if( leftcount < rightcount ) + { + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp++; + count = leftcount; + } + else + { + sp->table = table; + sp->count = leftcount; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +//// + + +typedef struct +{ + void **src; + void **dst; + int count; + int mergeflag; + int depthbit; +} ccMergeSortStack; + +int ccMergeSort( void **src, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ) ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t i, leftcount, rightcount; + void **dst, **sl, **sr, *temp, **swap; + ccMergeSortStack stack[CC_SORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + if( count <= 1 ) + return 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && sortfunc( src[2], src[3] ) ) + { + temp = src[2]; + src[2] = src[3]; + src[3] = temp; + } + if( sortfunc( src[0], src[1] ) ) + { + temp = src[0]; + src[0] = src[1]; + src[1] = temp; + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( sortfunc( src[2], src[3] ) ) + { + dst[2] = src[3]; + dst[3] = src[2]; + } + else + { + dst[2] = src[2]; + dst[3] = src[3]; + } + } + else if( count == 3 ) + dst[2] = src[2]; + if( sortfunc( src[0], src[1] ) ) + { + dst[0] = src[1]; + dst[1] = src[0]; + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + for( ; ; ) + { + if( sortfunc( *sl, *sr ) ) + { + *dst++ = *sr++; + if( --rightcount ) + continue; + for( i = 0 ; i < leftcount ; i++ ) + dst[i] = sl[i]; + break; + } + else + { + *dst++ = *sl++; + if( --leftcount ) + continue; + for( i = 0 ; i < rightcount ; i++ ) + dst[i] = sr[i]; + break; + } + } + + if( sp == stack ) + return swapflag ^ 1; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + +int ccMergeSortContext( void **src, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t i, leftcount, rightcount; + void **dst, **sl, **sr, *temp, **swap; + ccMergeSortStack stack[CC_SORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + if( count <= 1 ) + return 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && sortfunc( context, src[2], src[3] ) ) + { + temp = src[2]; + src[2] = src[3]; + src[3] = temp; + } + if( sortfunc( context, src[0], src[1] ) ) + { + temp = src[0]; + src[0] = src[1]; + src[1] = temp; + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( sortfunc( context, src[2], src[3] ) ) + { + dst[2] = src[3]; + dst[3] = src[2]; + } + else + { + dst[2] = src[2]; + dst[3] = src[3]; + } + } + else if( count == 3 ) + dst[2] = src[2]; + if( sortfunc( context, src[0], src[1] ) ) + { + dst[0] = src[1]; + dst[1] = src[0]; + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + for( ; ; ) + { + if( sortfunc( context, *sl, *sr ) ) + { + *dst++ = *sr++; + if( --rightcount ) + continue; + for( i = 0 ; i < leftcount ; i++ ) + dst[i] = sl[i]; + break; + } + else + { + *dst++ = *sl++; + if( --leftcount ) + continue; + for( i = 0 ; i < rightcount ; i++ ) + dst[i] = sr[i]; + break; + } + } + + if( sp == stack ) + return swapflag ^ 1; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + + +//// + + +typedef struct +{ + void *table; + int count; + int depth; +} ccHybridSortStack; + +void ccHybridSort( void **table, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ) +{ + int msortindex, depth, depthmax; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccHybridSortStack stack[CC_SORT_STACK_DEPTH]; + ccHybridSortStack *sp; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + + sp = stack; + for( ; ; ) + { + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortPart( table, count, sortfunc ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + msortindex = ccMergeSort( table, tmp, count, sortfunc ); + if( msortindex ) + memcpy( table, tmp, count * sizeof(void *) ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp->depth = depth; + sp++; + count = leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +void ccHybridSortContext( void **table, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ) +{ + int msortindex, depth, depthmax; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotstore; + void *temp; + void *pivot; + ccHybridSortStack stack[CC_SORT_STACK_DEPTH]; + ccHybridSortStack *sp; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + + sp = stack; + for( ; ; ) + { + if( count < CC_SORT_MIN_QSORT_COUNT ) + { + ccQuickSortContextPart( table, count, sortfunc, context ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + msortindex = ccMergeSortContext( table, tmp, count, sortfunc, context ); + if( msortindex ) + memcpy( table, tmp, count * sizeof(void *) ); + if( sp == stack ) + break; + sp--; + depth--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + if( sortfunc( context, table[pivotindex], table[count-1] ) ) + { + CC_SORT_SWAP( count-1, pivotindex ); + if( sortfunc( context, table[0], table[pivotindex] ) ) + CC_SORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotstore = highindex; + CC_SORT_SWAP( pivotstore, pivotindex ); + pivotindex = 1; + for( i = highindex ; --i ; ) + { + if( sortfunc( context, pivot, table[pivotindex] ) ) + pivotindex++; + else + { + highindex--; + CC_SORT_SWAP( highindex, pivotindex ); + } + } + CC_SORT_SWAP( pivotindex, pivotstore ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = rightcount; + sp->depth = depth; + sp++; + count = leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = rightcount; + } + } + + return; +} + + +//// + + +#define CC_DEBUG_LOG_SIZE (4096) + +void ccDebugLog( char *filename, char *string, ... ) +{ + int slen, bufsize; + char buffer[CC_DEBUG_LOG_SIZE]; + char *wbuf; + va_list ap; + FILE *file; + + if( !( file = fopen( filename, "a" ) ) ) + return; + + wbuf = buffer; + bufsize = CC_DEBUG_LOG_SIZE; + for( ; ; ) + { + va_start( ap, string ); + slen = vsnprintf( wbuf, bufsize, string, ap ); + va_end( ap ); +#if CC_WINDOWS + if( slen == -1 ) + slen = bufsize << 1; +#endif + if( slen < bufsize ) + break; + if( wbuf != buffer ) + free( wbuf ); + bufsize = slen + 2; + wbuf = malloc( bufsize ); + } + + fprintf( file, "%s", wbuf ); + + if( wbuf != buffer ) + free( wbuf ); + fclose( file ); + + return; +} + + +//// + + +void ccGrowthInit( ccGrowth *growth, int defaultsize ) +{ + growth->allocsize = CC_MAX( defaultsize, 512 ); + growth->offset = 0; + growth->data = malloc( growth->allocsize ); + return; +} + +int ccGrowthPrintf( ccGrowth *growth, char *format, ... ) +{ + int strsize, clampsize; + va_list ap; + + for( ; ; ) + { + va_start( ap, format ); + clampsize = growth->allocsize - growth->offset; + strsize = vsnprintf( (char *)ADDRESS( growth->data, growth->offset ), clampsize, format, ap ); + va_end( ap ); +#if CC_WINDOWS + if( strsize == -1 ) + strsize = growth->allocsize << 1; +#endif + if( strsize < clampsize ) + break; + growth->allocsize = CC_MAX( growth->offset + strsize + 1, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + growth->offset += strsize; + + return 1; +} + +int ccGrowthData( ccGrowth *growth, void *data, size_t size ) +{ + if( ( growth->offset + size ) >= growth->allocsize ) + { + growth->allocsize = CC_MAX( growth->offset + size, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + memcpy( ADDRESS( growth->data, growth->offset ), data, size ); + growth->offset += size; + return 1; +} + +int ccGrowthSeek( ccGrowth *growth, int offset ) +{ + if( offset >= growth->allocsize ) + { + growth->allocsize = CC_MAX( offset, growth->allocsize << 1 ); + growth->data = realloc( growth->data, growth->allocsize ); + } + if( offset > growth->offset ) + memset( ADDRESS( growth->data, growth->offset ), 0, offset - growth->offset ); + growth->offset = offset; + return 1; +} + +void ccGrowthFree( ccGrowth *growth ) +{ + free( growth->data ); + memset( growth, 0, sizeof(ccGrowth) ); + return; +} + +void ccGrowthElapsedTimeString( ccGrowth *growth, int64_t timecount, int maxfieldcount ) +{ + int fieldcount, unitcount; + fieldcount = 0; + if( timecount <= 0 ) + { + ccGrowthPrintf( growth, "Just now" ); + return; + } + if( timecount >= (24*60*60) ) + { + unitcount = timecount / (24*60*60); + timecount = timecount % (24*60*60); + ccGrowthPrintf( growth, "%d day%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= (60*60) ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount / (60*60); + timecount = timecount % (60*60); + ccGrowthPrintf( growth, "%d hour%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= (60) ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount / (60); + timecount = timecount % (60); + ccGrowthPrintf( growth, "%d minute%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( timecount >= 1 ) + { + if( fieldcount ) + ccGrowthPrintf( growth, ", " ); + unitcount = timecount; + ccGrowthPrintf( growth, "%d second%s", (int)unitcount, ( unitcount > 1 ? "s" : "" ) ); + fieldcount++; + if( fieldcount >= maxfieldcount ) + return; + } + if( !( fieldcount ) ) + ccGrowthPrintf( growth, "Long, long" ); + return; +} + + + +//// + + +void *ccFileLoad( const char *path, size_t maxsize, size_t *retsize ) +{ + FILE *file; + size_t size; + char *data; + + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fseek( file, 0, SEEK_END ); + size = ftell( file ); + fseek( file, 0, SEEK_SET ); + if( ( maxsize ) && ( size > maxsize ) ) + { + fclose( file ); + return 0; + } + data = malloc( size + 1 ); + data[size] = 0; + if( fread( data, size, 1, file ) != 1 ) + { + free( data ); + data = 0; + } + fclose( file ); + if( retsize ) + *retsize = size; + + return data; +} + + +size_t ccFileLoadDirect( const char *path, void *data, size_t minsize, size_t maxsize ) +{ + FILE *file; + size_t size; + + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fseek( file, 0, SEEK_END ); + size = ftell( file ); + fseek( file, 0, SEEK_SET ); + if( ( size < minsize ) || ( size > maxsize ) ) + size = 0; + else if( fread( data, size, 1, file ) != 1 ) + size = 0; + fclose( file ); + + return size; +} + + +int ccFileStore( const char *path, void *data, size_t datasize, int fsyncflag ) +{ + int retval; +#if CC_UNIX + int fd; + if( ( fd = open( path, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR ) ) == -1 ) + return 0; + retval = 1; + if( write( fd, data, datasize ) != datasize ) + retval = 0; + if( fsyncflag ) + { + #if CC_LINUX + fdatasync( fd ); + #else + fsync( fd ); + #endif + } + if( close( fd ) != 0 ) + retval = 0; +#elif CC_WINDOWS + HANDLE file; + DWORD byteswritten; + file = CreateFileA( path, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ); + if( file == INVALID_HANDLE_VALUE ) + return 0; + retval = 1; + if( !( WriteFile( file, data, datasize, &byteswritten, 0 ) ) ) + retval = 0; + if( fsyncflag ) + FlushFileBuffers( file ); + if( CloseHandle( file ) == 0 ) + retval = 0; +#else + FILE *file; + file = fopen( path, "wb" ); + if( !( file ) ) + return 0; + retval = 1; + if( fwrite( data, size, 1, file ) != 1 ) + retval = 0; + if( fclose( file ) != 0 ) + retval = 0; +#endif + return retval; +} + + +int ccFileExists( char *path ) +{ + int statret; +#if CC_UNIX + struct stat filestat; + statret = stat( path, &filestat ); + return ( statret == 0 ); +#elif CC_WINDOWS + struct _stat filestat; + statret = _stat( path, &filestat ); + return ( statret == 0 ); +#else + FILE *file; + file = fopen( path, "rb" ); + if( !( file ) ) + return 0; + fclose( file ); + return 1; +#endif +} + + +int ccFileStat( char *path, size_t *retfilesize, time_t *retfiletime ) +{ + int statret; +#if CC_UNIX + struct stat filestat; + statret = stat( path, &filestat ); + if( !( statret ) ) + { + if( retfilesize ) + *retfilesize = filestat.st_size; + if( retfiletime ) + *retfiletime = filestat.st_mtime; + return 1; + } +#elif CC_WINDOWS + struct _stat filestat; + statret = _stat( path, &filestat ); + if( !( statret ) ) + { + if( retfilesize ) + *retfilesize = filestat.st_size; + if( retfiletime ) + *retfiletime = filestat.st_mtime; + return 1; + } +#endif + if( retfilesize ) + *retfilesize = 0; + if( retfiletime ) + *retfiletime = 0; + return 0; +} + + +int ccRenameFile( char *oldpath, char *newpath ) +{ +#if CC_WINDOWS + int attemptindex, attemptcount; + /* On Windows, file indexing or anti-virus software could be scanning the file and prevent rename() */ + attemptcount = 16; + for( attemptindex = 0 ; ; attemptindex++ ) + { + if( MoveFileEx( oldpath, newpath, MOVEFILE_REPLACE_EXISTING ) ) + break; + if( attemptindex >= attemptcount ) + return 0; + ccSleep( 250 ); + } +#else + if( rename( oldpath, newpath ) ) + return 0; +#endif + return 1; +} + + +//// + + +struct _ccDir +{ +#if CC_UNIX + void *dirhandle; +#elif CC_WINDOWS + HANDLE dirhandle; + WIN32_FIND_DATA direntry; + int firstflag; +#endif +}; + + +ccDir *ccOpenDir( char *path ) +{ + ccDir *dir; + dir = malloc( sizeof(ccDir) ); +#if CC_UNIX + dir->dirhandle = opendir( path ); + if( !( dir->dirhandle ) ) + { + free( dir ); + return 0; + } + return dir; +#elif CC_WINDOWS + dir->dirhandle = FindFirstFile( path, &dir->direntry ); + if( dir->dirhandle == INVALID_HANDLE_VALUE ) + { + free( dir ); + return 0; + } + dir->firstflag = 1; + return dir; +#else + return 0; +#endif +} + +char *ccReadDir( ccDir *dir ) +{ +#if CC_UNIX + struct dirent *direntry; + direntry = readdir( dir->dirhandle ); + if( direntry ) + return direntry->d_name; + return 0; +#elif CC_WINDOWS + if( dir->firstflag ) + { + dir->firstflag = 0; + return (dir->direntry).cFileName; + } + if( FindNextFile( dir->dirhandle, &dir->direntry ) ) + return (dir->direntry).cFileName; + return 0; +#else + return 0; +#endif +} + +void ccCloseDir( ccDir *dir ) +{ +#if CC_UNIX + closedir( dir->dirhandle ); +#elif CC_WINDOWS + FindClose( dir->dirhandle ); +#endif + free( dir ); + return; +} + + +//// + + +int64_t ccGetFreeDiskSpace( char *dirpath ) +{ + int64_t freespace; +#if CC_UNIX + struct statvfs fsdata; + if( ( statvfs( dirpath, &fsdata ) ) != 0 ) + return -1; + if( ( fsdata.f_bfree == 0 ) || ( fsdata.f_bfree == -1 ) || ( fsdata.f_frsize == 0 ) || ( fsdata.f_frsize == -1 ) ) + return -1; + freespace = (int64_t)fsdata.f_bfree * (int64_t)fsdata.f_frsize; +#elif CC_WINDOWS + ULARGE_INTEGER winfreespace; + if( !( GetDiskFreeSpaceExA( dirpath, &winfreespace, 0, 0 ) ) ) + return -1; + freespace = (int64_t)winfreespace.QuadPart; +#else + freespace = -1; +#endif + return freespace; +} + + +//// + + +int ccGetTimeOfDay( struct timeval *tv ) +{ +#ifdef CC_WIN32 + #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + FILETIME ft; + uint64_t curtime; + if( tv ) + { + GetSystemTimeAsFileTime( &ft ); + curtime = ft.dwHighDateTime; + curtime <<= 32; + curtime |= ft.dwLowDateTime; + curtime /= 10; + curtime -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)( curtime / 1000000UL ); + tv->tv_usec = (long)( curtime % 1000000UL ); + } +#endif + if( tv ) + gettimeofday( tv, 0 ); + return 0; +} + + +void ccSleep( int milliseconds ) +{ +#if CC_UNIX + struct timespec nanosleeptime; + nanosleeptime.tv_sec = milliseconds / 1000; + nanosleeptime.tv_nsec = ( milliseconds % 1000 ) * 1000000; + nanosleep( &nanosleeptime, 0 ); +#elif CC_WINDOWS + Sleep( milliseconds ); +#else + sleep( (milliseconds+999)/1000 ); +#endif + return; +} + + +//// + + +/* Returned string must be free()d */ +char *ccGetSystemName() +{ + char *string; +#if CC_UNIX + struct utsname unamebuf; + if( uname( &unamebuf ) ) + return 0; + string = ccStrAllocPrintf( "%s %s, Build %s, %s", unamebuf.sysname, unamebuf.release, unamebuf.version, unamebuf.machine ); +#elif CC_WINDOWS + #ifndef VER_SUITE_WH_SERVER + #define VER_SUITE_WH_SERVER 0x00008000 + #endif + #ifndef PRODUCT_PROFESSIONAL + #define PRODUCT_PROFESSIONAL 0x00000030 + #endif + #ifndef PRODUCT_ULTIMATE + #define PRODUCT_ULTIMATE 0x00000001 + #endif + #ifndef PRODUCT_HOME_BASIC + #define PRODUCT_HOME_BASIC 0x00000002 + #endif + #ifndef PRODUCT_HOME_PREMIUM + #define PRODUCT_HOME_PREMIUM 0x00000003 + #endif + #ifndef PRODUCT_ENTERPRISE + #define PRODUCT_ENTERPRISE 0x00000004 + #endif + #ifndef PRODUCT_BUSINESS + #define PRODUCT_BUSINESS 0x00000006 + #endif + #ifndef PRODUCT_STANDARD_SERVER + #define PRODUCT_STANDARD_SERVER 0x00000007 + #endif + #ifndef PRODUCT_DATACENTER_SERVER + #define PRODUCT_DATACENTER_SERVER 0x00000008 + #endif + #ifndef PRODUCT_SMALLBUSINESS_SERVER + #define PRODUCT_SMALLBUSINESS_SERVER 0x00000009 + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER + #define PRODUCT_ENTERPRISE_SERVER 0x0000000A + #endif + #ifndef PRODUCT_STARTER + #define PRODUCT_STARTER 0x0000000B + #endif + #ifndef PRODUCT_DATACENTER_SERVER_CORE + #define PRODUCT_DATACENTER_SERVER_CORE 0x0000000C + #endif + #ifndef PRODUCT_STANDARD_SERVER_CORE + #define PRODUCT_STANDARD_SERVER_CORE 0x0000000D + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER_CORE + #define PRODUCT_ENTERPRISE_SERVER_CORE 0x0000000E + #endif + #ifndef PRODUCT_ENTERPRISE_SERVER_IA64 + #define PRODUCT_ENTERPRISE_SERVER_IA64 0x0000000F + #endif + #ifndef PRODUCT_WEB_SERVER + #define PRODUCT_WEB_SERVER 0x00000011 + #endif + #ifndef PRODUCT_CLUSTER_SERVER + #define PRODUCT_CLUSTER_SERVER 0x00000012 + #endif + #ifndef PRODUCT_SMALLBUSINESS_SERVER_PREMIUM + #define PRODUCT_SMALLBUSINESS_SERVER_PREMIUM 0x00000019 + #endif + + typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); + typedef BOOL (WINAPI *PGPI)(DWORD, DWORD, DWORD, DWORD, PDWORD); + + OSVERSIONINFOEX osvi; + SYSTEM_INFO si; + DWORD dwType; + PGPI pGPI; + PGNSI pGNSI; + char *sysname, *detailname, *packname, *archname; + int buildnumber; + + ZeroMemory( &si, sizeof(SYSTEM_INFO) ); + ZeroMemory( &osvi, sizeof(OSVERSIONINFOEX) ); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if( !( GetVersionEx( (OSVERSIONINFO*) &osvi ) ) ) + return 0; + + pGNSI = (PGNSI)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetNativeSystemInfo" ); + if( pGNSI ) + pGNSI( &si ); + else + GetSystemInfo( &si ); + if( ( osvi.dwPlatformId != VER_PLATFORM_WIN32_NT ) || ( osvi.dwMajorVersion <= 4 ) ) + return 0; + + sysname = "Unknown"; + detailname = 0; + packname = 0; + buildnumber = 0; + archname = 0; + + if( osvi.dwMajorVersion == 6 ) + { + if( osvi.dwMinorVersion == 0 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows Vista" : "Windows Server 2008" ); + else if ( osvi.dwMinorVersion == 1 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows 7" : "Windows Server 2008 R2" ); + else if ( osvi.dwMinorVersion == 2 ) + sysname = ( osvi.wProductType == VER_NT_WORKSTATION ? "Windows 8" : "Windows Server 2012" ); + else + sysname = "Windows 8 or more"; + + pGPI = (PGPI)GetProcAddress( GetModuleHandleA( "kernel32.dll" ), "GetProductInfo" ); + + pGPI( osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType ); + switch( dwType ) + { + case PRODUCT_ULTIMATE: + detailname = "Ultimate Edition"; + break; + case PRODUCT_PROFESSIONAL: + detailname = "Professional"; + break; + case PRODUCT_HOME_PREMIUM: + detailname = "Home Premium Edition"; + break; + case PRODUCT_HOME_BASIC: + detailname = "Home Basic Edition"; + break; + case PRODUCT_ENTERPRISE: + detailname = "Enterprise Edition"; + break; + case PRODUCT_BUSINESS: + detailname = "Business Edition"; + break; + case PRODUCT_STARTER: + detailname = "Starter Edition"; + break; + case PRODUCT_CLUSTER_SERVER: + detailname = "Cluster Server Edition"; + break; + case PRODUCT_DATACENTER_SERVER: + detailname = "Datacenter Edition"; + break; + case PRODUCT_DATACENTER_SERVER_CORE: + detailname = "Datacenter Edition (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER: + detailname = "Enterprise Edition"; + break; + case PRODUCT_ENTERPRISE_SERVER_CORE: + detailname = "Enterprise Edition (core installation)"; + break; + case PRODUCT_ENTERPRISE_SERVER_IA64: + detailname = "Enterprise Edition for Itanium-based Systems"; + break; + case PRODUCT_SMALLBUSINESS_SERVER: + detailname = "Small Business Server"; + break; + case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM: + detailname = "Small Business Server Premium Edition"; + break; + case PRODUCT_STANDARD_SERVER: + detailname = "Standard Edition"; + break; + case PRODUCT_STANDARD_SERVER_CORE: + detailname = "Standard Edition (core installation)"; + break; + case PRODUCT_WEB_SERVER: + detailname = "Web Server Edition"; + break; + default: + break; + } + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 2 ) ) + { + if( GetSystemMetrics(SM_SERVERR2) ) + sysname = "Windows Server 2003 R2, "; + else if ( osvi.wSuiteMask & VER_SUITE_STORAGE_SERVER ) + sysname = "Windows Storage Server 2003"; + else if ( osvi.wSuiteMask & VER_SUITE_WH_SERVER ) + sysname = "Windows Home Server"; + else if( ( osvi.wProductType == VER_NT_WORKSTATION ) && ( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) ) + sysname = "Windows XP Professional x64 Edition"; + else + sysname ="Windows Server 2003, "; + if( osvi.wProductType != VER_NT_WORKSTATION ) + { + if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Edition for Itanium-based Systems"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise Edition for Itanium-based Systems"; + } + else if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter x64 Edition"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise x64 Edition"; + else + detailname = "Standard x64 Edition"; + } + else + { + if( osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER ) + detailname = "Compute Cluster Edition"; + else if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Edition"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Enterprise Edition"; + else if ( osvi.wSuiteMask & VER_SUITE_BLADE ) + detailname = "Web Edition"; + else + detailname = "Standard Edition"; + } + } + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 1 ) ) + { + sysname = "Windows XP "; + if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) + sysname = "Home Edition"; + else + sysname = "Professional"; + } + else if( ( osvi.dwMajorVersion == 5 ) && ( osvi.dwMinorVersion == 0 ) ) + { + sysname = "Windows 2000 "; + if( osvi.wProductType == VER_NT_WORKSTATION ) + detailname = "Professional"; + else + { + if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) + detailname = "Datacenter Server"; + else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) + detailname = "Advanced Server"; + else + detailname = "Server"; + } + } + packname = osvi.szCSDVersion; + buildnumber = osvi.dwBuildNumber; + if( osvi.dwMajorVersion >= 6 ) + { + if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ) + archname = ", 64-bit"; + else if( si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL ) + archname = ", 32-bit"; + } + + /* Finally build the string */ + string = ccStrAllocPrintf( "%s%s%s%s%s (build %d )%s", sysname, ( detailname ? ", " : "" ), ( detailname ? detailname : "" ), ( packname ? ", " : "" ), ( packname ? packname : "" ), buildnumber, ( archname ? archname : "" ) ); + +#endif + return string; +} diff --git a/ecere/src/gfx/newFonts/cc/cc.h b/ecere/src/gfx/newFonts/cc/cc.h new file mode 100644 index 0000000..17d5c9c --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cc.h @@ -0,0 +1,1516 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include +#include + + +#if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) + #define CC_LINUX (1) + #define CC_UNIX (1) +#elif defined(__APPLE__) + #define CC_OSX (1) + #define CC_UNIX (1) +#elif defined(__unix__) || defined(__unix) || defined(unix) + #define CC_UNIX (1) +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) + #define CC_WIN64 (1) + #define CC_WIN32 (1) + #define CC_WINDOWS (1) +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define CC_WIN32 (1) + #define CC_WINDOWS (1) +#endif + + +#ifndef ADDRESS + #define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF + #define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + + +#define CC_SIZEOF_ALIGN4(x) ((sizeof(x)+0x3)&~0x3) +#define CC_SIZEOF_ALIGN8(x) ((sizeof(x)+0x7)&~0x7) +#define CC_SIZEOF_ALIGN16(x) ((sizeof(x)+0xF)&~0xF) +#define CC_SIZEOF_ALIGN32(x) ((sizeof(x)+0x1F)&~0x1F) +#define CC_SIZEOF_ALIGN64(x) ((sizeof(x)+0x3F)&~0x3F) + + +#define CC_MIN(x,y) ((x)>(y)?(y):(x)) +#define CC_MAX(x,y) ((x)<(y)?(y):(x)) +#define CC_CLAMP(x,min,max) ((x)<(min)?(min):((x)>(max)?(max):(x))) + + +#define CC_MAX_INT8(x,y) (((int8_t)x)-((((int8_t)x)-((int8_t)y))&((((int8_t)x)-((int8_t)y))>>7))) +#define CC_MAX_INT16(x,y) (((int16_t)x)-((((int16_t)x)-((int16_t)y))&((((int16_t)x)-((int16_t)y))>>15))) +#define CC_MAX_INT32(x,y) (((int32_t)x)-((((int32_t)x)-((int32_t)y))&((((int32_t)x)-((int32_t)y))>>31))) +#define CC_MAX_INT64(x,y) (((int64_t)x)-((((int64_t)x)-((int64_t)y))&((((int64_t)x)-((int64_t)y))>>63))) + +#define CC_MIN_INT8(x,y) (((int8_t)y)+((((int8_t)x)-((int8_t)y))&((((int8_t)x)-((int8_t)y))>>7))) +#define CC_MIN_INT16(x,y) (((int16_t)y)+((((int16_t)x)-((int16_t)y))&((((int16_t)x)-((int16_t)y))>>15))) +#define CC_MIN_INT32(x,y) (((int32_t)y)+((((int32_t)x)-((int32_t)y))&((((int32_t)x)-((int32_t)y))>>31))) +#define CC_MIN_INT64(x,y) (((int64_t)y)+((((int64_t)x)-((int64_t)y))&((((int64_t)x)-((int64_t)y))>>63))) + +#define CC_SHIFTDIV_INT8(value,shift) ({uint8_t _s=((uint8_t)value)>>7;((int8_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT16(value,shift) ({uint16_t _s=((uint16_t)value)>>15;((int16_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT32(value,shift) ({uint32_t _s=((uint32_t)value)>>31;((int32_t)((value)+(_s<>shift;}) +#define CC_SHIFTDIV_INT64(value,shift) ({uint64_t _s=((uint64_t)value)>>63;((int64_t)((value)+(_s<>shift;}) + +#define CC_SHIFTDIVROUND(value,shift) ((value>>shift)+(((value&((1<=(1<>shift)+((((value&((1<>7))<<1)>=(1<>shift)+((((value&((1<>15))<<1)>=(1<>shift)+((((value&((1<>31))<<1)>=(1<>shift)+((((value&((1<>63))<<1)>=(1<>2)):(CC_NUMBITS2(n))) +#define CC_NUMBITS8(n) ((n&0xF0)?(4+CC_NUMBITS4(n>>4)):(CC_NUMBITS4(n))) +#define CC_NUMBITS16(n) ((n&0xFF00)?(8+CC_NUMBITS8(n>>8)):(CC_NUMBITS8(n))) +#define CC_NUMBITS32(n) ((n&0xFFFF0000)?(16+CC_NUMBITS16(n>>16)):(CC_NUMBITS16(n))) +#define CC_NUMBITS(n) (n==0?0:CC_NUMBITS32(n)+1) + + +//// + + +#define CC_STRINGIFY(s) CC_STRINGIFY_IN(s) +#define CC_STRINGIFY_IN(s) #s + +#define CC_CONCATENATE(s,n) CC_CONCATENATE_IN(s,n) +#define CC_CONCATENATE_IN(s,n) s ## n + + +//// + + +enum +{ + CC_TYPE_UINT8, + CC_TYPE_INT8, + CC_TYPE_UINT16, + CC_TYPE_INT16, + CC_TYPE_UINT32, + CC_TYPE_INT32, + CC_TYPE_UINT64, + CC_TYPE_INT64, + CC_TYPE_FLOAT, + CC_TYPE_DOUBLE, + + CC_TYPE_COUNT +}; + +extern const size_t ccTypeSize[CC_TYPE_COUNT]; + + +//// + + +#if CC_UNIX + #define CC_DIR_SEPARATOR_CHAR '/' + #define CC_DIR_SEPARATOR_STRING "/" +#elif CC_WINDOWS + #define CC_DIR_SEPARATOR_CHAR '\\' + #define CC_DIR_SEPARATOR_STRING "\\" +#else + #define CC_DIR_SEPARATOR_CHAR '/' + #define CC_DIR_SEPARATOR_STRING "/" +#endif + +#if CC_WINDOWS + #define CC_LL "I64" + #define CC_LLD "%I64d" + #define CC_LLU "%I64u" + #define CC_LLX "%I64x" +#else + #define CC_LL "ll" + #define CC_LLD "%lld" + #define CC_LLU "%llu" + #define CC_LLX "%llx" +#endif + + +//// + + +uint32_t ccHash32Data( void *data, int size ); +uint32_t ccHash32Int32( uint32_t data ); +uint32_t ccHash32Int64( uint64_t data ); +uint32_t ccHash32Array32( uint32_t *data, int count ); +uint32_t ccHash32Array64( uint64_t *data, int count ); + +static inline uint32_t ccHash32Int16Inline( uint32_t i ) +{ + uint32_t hash; + hash = ( i << 16 ) ^ i; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Int32Inline( uint32_t i ) +{ + uint32_t hash; + hash = i & 0xFFFF; + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( i & 0xFFFF0000 ) >> 5 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Int64Inline( uint64_t i ) +{ + uint32_t hash; + hash = (uint32_t)(i & 0xFFFF); + hash = ( ( hash << 16 ) ^ hash ) ^ ( ( (uint32_t)( i >> 16 ) & 0xFFFF ) << 11 ); + hash += ( hash >> 11 ) + ( (uint32_t)( i >> 32 ) & 0xFFFF ); + hash = ( ( hash << 16 ) ^ hash ) ^ (uint32_t)( ( i & 0xFFFF000000000000LL ) >> 37 ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data3Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash ^= hash << 16; + hash ^= (uint32_t)data[2] << 18; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data4Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data5Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += (uint32_t)data[4]; + hash ^= hash << 10; + hash += hash >> 1; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data6Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash ^= hash << 11; + hash += hash >> 17; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data7Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + data = ADDRESS( data, 4 ); + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash ^= hash << 16; + hash ^= (uint32_t)data[6] << 18; + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +static inline uint32_t ccHash32Data8Inline( uint8_t *data ) +{ + uint32_t hash; + hash = 0; + hash += ( (uint32_t)data[1] << 8 ) | (uint32_t)data[0]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[3] << 19 ) | ( (uint32_t)data[2] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash += ( (uint32_t)data[5] << 8 ) | (uint32_t)data[4]; + hash = ( hash << 16 ) ^ ( ( ( (uint32_t)data[7] << 19 ) | ( (uint32_t)data[6] << 11 ) ) ^ hash ); + hash += hash >> 11; + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + + + +//// + + + +typedef struct +{ + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; +} ccQuickRandState32; + +static inline uint32_t ccQuickRand32( ccQuickRandState32 *randstate ) +{ + uint32_t e; + e = randstate->a - ( ( randstate->b << 27 ) | ( randstate->b >> (32-27) ) ); + randstate->a = randstate->b ^ ( ( randstate->c << 17 ) | ( randstate->c >> (32-17) ) ); + randstate->b = randstate->c + randstate->d; + randstate->c = randstate->d + e; + randstate->d = e + randstate->a; + return randstate->d; +} + +static inline void ccQuickRand32Seed( ccQuickRandState32 *randstate, uint32_t seed ) +{ + uint32_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed; + randstate->c = seed; + randstate->d = seed; + for( i = 0 ; i < 20 ; i++ ) + ccQuickRand32( randstate ); + return; +} + +static inline void ccQuickRand32SeedFast( ccQuickRandState32 *randstate, uint32_t seed0, uint32_t seed1, uint32_t seed2 ) +{ + uint32_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed0; + randstate->c = seed1; + randstate->d = seed2; + for( i = 0 ; i < 4 ; i++ ) + ccQuickRand32( randstate ); + return; +} + + +typedef struct +{ + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; +} ccQuickRandState64; + +static inline uint64_t ccQuickRand64( ccQuickRandState64 *randstate ) +{ + uint64_t e; + e = randstate->a - ( ( randstate->b << 7 ) | ( randstate->b >> (64-7) ) ); + randstate->a = randstate->b ^ ( ( randstate->c << 13 ) | ( randstate->c >> (64-13) ) ); + randstate->b = randstate->c + ( ( randstate->d << 37 ) | ( randstate->d >> (64-37) ) ); + randstate->c = randstate->d + e; + randstate->d = e + randstate->a; + return randstate->d; +} + +static inline void ccQuickRand64Seed( ccQuickRandState64 *randstate, uint64_t seed ) +{ + uint64_t i; + randstate->a = 0xf1ea5eed; + randstate->b = seed; + randstate->c = seed; + randstate->d = seed; + for( i = 0 ; i < 20 ; i++ ) + ccQuickRand64( randstate ); + return; +} + + + +//// + + + +int ccMemCmp( void *s0, void *s1, int size ); +int ccMemCmp32( uint32_t *s0, uint32_t *s1, int count ); +int ccMemCmp64( uint64_t *s0, uint64_t *s1, int count ); +int ccMemCmpRetSize( void *s0, void *s1, int size ); + +static inline int ccMemCmpInline( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + return 0; + } + return 1; +} + +static inline int ccMemCmpSizeInline( void *s0, void *s1, int size ) +{ + int i; + uint8_t *t0, *t1; + t0 = s0; + t1 = s1; + for( i = 0 ; i < size ; i++ ) + { + if( t0[i] != t1[i] ) + break; + } + return i; +} + + +//// + + +uint8_t ccLog2Int8( uint8_t i ); +uint16_t ccLog2Int16( uint16_t i ); +uint32_t ccLog2Int32( uint32_t i ); +uint64_t ccLog2Int64( uint64_t i ); +#if CPUCONF_LONG_SIZE == 8 + #define ccLog2IntL(v) ccLog2Int64(v) +#else + #define ccLog2IntL(v) ccLog2Int32(v) +#endif + + +//// + + +static inline int8_t ccPowInt8( int8_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return (int8_t)result; +} + +static inline int16_t ccPowInt16( int16_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return (int16_t)result; +} + +static inline int32_t ccPowInt32( int32_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return result; +} + +static inline int64_t ccPowInt64( int64_t base, int exp ) +{ + int result; + result = 1; + while( exp ) + { + if( exp & 1 ) + result *= base; + exp >>= 1; + base *= base; + } + return result; +} + + +//// + + +static inline uint8_t ccMergeIntMask8( uint8_t i0, uint8_t i1, uint8_t mask ) +{ + return (uint8_t)(i0 ^ ( ( i0 ^ i1 ) & mask )); +} + +static inline uint16_t ccMergeIntMask16( uint16_t i0, uint16_t i1, uint16_t mask ) +{ + return (uint16_t)(i0 ^ ( ( i0 ^ i1 ) & mask )); +} + +static inline uint32_t ccMergeIntMask32( uint32_t i0, uint32_t i1, uint32_t mask ) +{ + return i0 ^ ( ( i0 ^ i1 ) & mask ); +} + +static inline uint64_t ccMergeIntMask64( uint64_t i0, uint64_t i1, uint64_t mask ) +{ + return i0 ^ ( ( i0 ^ i1 ) & mask ); +} + +#if CPUCONF_LONG_SIZE == 8 + #define ccMergeIntMaskL(v) ccMergeIntMask64(v) +#else + #define ccMergeIntMaskL(v) ccMergeIntMask32(v) +#endif + + + +//// + + + +static inline int ccCountBits64( uint64_t i ) +{ + int r; + for( r = 0 ; i ; r++ ) + i &= i - 1; + return r; +} + + +static inline int ccCountBits32( uint32_t v ) +{ + int c; + v = v - ( ( v >> 1 ) & 0x55555555 ); + v = ( v & 0x33333333 ) + ( ( v >> 2 ) & 0x33333333 ); + c = ( ( ( v + ( v >> 4 ) ) & 0xF0F0F0F ) * 0x1010101 ) >> 24; + return c; +} + + +//// + + +static inline int ccTrailingCount32( uint32_t v ) +{ + int c; + if( v & 0x1 ) + c = 0; + else + { + c = 1; + if( !( v & 0xffff ) ) + { + v >>= 16; + c += 16; + } + if( !( v & 0xff ) ) + { + v >>= 8; + c += 8; + } + if( !( v & 0xf ) ) + { + v >>= 4; + c += 4; + } + if( !( v & 0x3 ) ) + { + v >>= 2; + c += 2; + } + c -= v & 0x1; + } + return c; +} + + +static inline int ccTrailingCount64( uint64_t v ) +{ + int c; + if( v & 0x1 ) + c = 0; + else + { + c = 1; + if( !( v & 0xffffffff ) ) + { + v >>= 32; + c += 32; + } + if( !( v & 0xffff ) ) + { + v >>= 16; + c += 16; + } + if( !( v & 0xff ) ) + { + v >>= 8; + c += 8; + } + if( !( v & 0xf ) ) + { + v >>= 4; + c += 4; + } + if( !( v & 0x3 ) ) + { + v >>= 2; + c += 2; + } + c -= v & 0x1; + } + return c; +} + + +//// + + +static inline uint32_t ccReverseBits32( uint32_t value ) +{ + uint32_t result; + int shift; + + result = value; + shift = 32-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + + return result; +} + +static inline uint64_t ccReverseBits64( uint64_t value ) +{ + uint64_t result; + int shift; + + result = value; + shift = 64-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + + return result; +} + +static inline uint32_t ccReverseBitsVar32( uint32_t value, int numbits ) +{ + uint32_t result; + int shift; + + value &= ( ((uint32_t)1) << numbits ) - 1; + result = value; + shift = 32-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + result >>= 32 - numbits; + + return result; +} + +static inline uint64_t ccReverseBitsVar64( uint64_t value, int numbits ) +{ + uint64_t result; + int shift; + + value &= ( ((uint64_t)1) << numbits ) - 1; + result = value; + shift = 64-1; + for( value >>= 1 ; value ; value >>= 1 ) + { + result <<= 1; + result |= value & 1; + shift--; + } + result <<= shift; + result >>= 64 - numbits; + + return result; +} + + + +//// + + + +static inline uint8_t ccIsPow2Int8( uint8_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline uint16_t ccIsPow2Int16( uint16_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline uint32_t ccIsPow2Int32( uint32_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + +static inline uint64_t ccIsPow2Int64( uint64_t v ) +{ + return ( ( v & ( v - 1 ) ) == 0 ); +} + + +static inline uint8_t ccPow2Round8( uint8_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v++; + return v; +} + +static inline uint16_t ccPow2Round16( uint16_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v++; + return v; +} + +static inline uint32_t ccPow2Round32( uint32_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline uint64_t ccPow2Round64( uint64_t v ) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +#if CPUCONF_LONG_SIZE == 8 + #define ccPow2RoundL(v) ccPow2Round64(v) +#else + #define ccPow2RoundL(v) ccPow2Round32(v) +#endif + + + +//// + + + +static inline uint32_t ccTestNullByte32( uint32_t v ) +{ + return ( v - 0x01010101 ) & ~v & 0x80808080; +} + +static inline uint64_t ccTestNullByte64( uint64_t v ) +{ + return ( v - 0x0101010101010101ULL ) & ~v & 0x8080808080808080ULL; +} + +static inline uint32_t ccSignBit32( uint32_t v ) +{ + return v >> 31; +} + +static inline uint64_t ccSignBit64( uint64_t v ) +{ + return v >> 63; +} + +static inline uint32_t ccAbs32( int32_t v ) +{ + int32_t mask; + mask = v >> 31; + return ( v ^ mask ) - mask; +} + +static inline uint64_t ccAbs64( int64_t v ) +{ + int32_t mask; + mask = (int32_t)(v >> 63); + return ( v ^ mask ) - mask; +} + + + +//// + + + +static inline int32_t ccMortonNumber32( int32_t x, int32_t y ) +{ + int i; + uint32_t z; + z = 0; + for( i = 0 ; i < 16 ; i++ ) + { + z |= ( x & ( ((uint32_t)1) << i ) ) << i; + z |= ( y & ( ((uint32_t)1) << i ) ) << ( i + 1 ); + } + return z; +} + +static inline int64_t ccMortonNumber64( int32_t x, int32_t y ) +{ + int i; + uint64_t z; + z = 0; + for( i = 0 ; i < 16 ; i++ ) + { + z |= ( x & ( ((uint64_t)1) << i ) ) << i; + z |= ( y & ( ((uint64_t)1) << i ) ) << ( i + 1 ); + } + return z; +} + + + +//// + + + +static inline int ccStrCmpEqualInline( char *s0, char *s1 ) +{ + int i; + for( i = 0 ; ; i++ ) + { + if( s0[i] != s1[i] ) + return 0; + if( !( s0[i] ) ) + break; + } + return 1; +} + +static inline int ccIsAlphaNum( char c ) +{ + if( ( c >= 'a' ) && ( c <= 'z' ) ) + return 1; + if( ( c >= 'A' ) && ( c <= 'Z' ) ) + return 1; + if( ( c >= '0' ) && ( c <= '9' ) ) + return 1; + return 0; +} + +static inline int ccIsAlphaNumExtended( unsigned char c ) +{ + if( ( c >= 'a' ) && ( c <= 'z' ) ) + return 1; + if( ( c >= 'A' ) && ( c <= 'Z' ) ) + return 1; + if( ( c >= '0' ) && ( c <= '9' ) ) + return 1; + if( c >= 128 ) + return 1; + return 0; +} + +static inline int ccCharHexBase( char c ) +{ + int hex; + if( ( c >= '0' ) && ( c <= '9' ) ) + hex = c - '0'; + else if( ( c >= 'A' ) && ( c <= 'F' ) ) + hex = c - ('A'-10); + else if( ( c >= 'a' ) && ( c <= 'f' ) ) + hex = c - ('a'-10); + else + hex = -1; + return hex; +} + + +void ccStrLowCase( char *str, int length ); +void ccStrLowCopy( char *dst, char *src, int length ); +int ccStrCmpEqual( char *s0, char *s1 ); +int ccStrCmpEqualTest( char *s0, char *s1 ); +int ccStrCmpStdTest( char *s0, char *s1 ); +char *ccStrCmpWord( char *str, char *word ); +char *ccStrCmpSeq( char *str, char *seq, int seqlength ); +char *ccStrMatchSeq( char *str, char *seq, int seqlength ); +char *ccSeqCmpSeq( char *s1, char *s2, int s1length, int s2length ); +int ccStrWordCmpWord( char *s1, char *s2 ); +char *ccStrLowCmpWord( char *str, char *word ); +char *ccStrLowCmpSeq( char *str, char *seq, int seqlength ); +char *ccStrFindStr( char *str0, char *str1 ); +char *ccStrFindStrSkip( char *str0, char *str1 ); +char *ccStrFindSeq( char *str, char *seq, int seqlength ); +char *ccStrFindWord( char *str, char *word, int wordlength ); +int ccStrWordLength( char *str ); +int ccStrFindChar( char *str, char c ); +int ccSeqFindChar( char *seq, int seqlen, char c ); +int ccStrFindCharLast( char *str, char c ); +int ccSeqFindCharLast( char *seq, int seqlen, char c ); +char *ccSeqFindStr( char *seq, int seqlen, char *str ); +char *ccSeqFindStrSkip( char *seq, int seqlen, char *str ); +char *ccStrParam( char *str, int *retparamlen, int *retskiplen ); +int ccParseParameters( char *str, char **argv, int argcountmax ); +int ccParseParametersCut( char *str, char **argv, int argcountmax ); +char *ccStrNextWord( char *str ); +char *ccStrSkipWord( char *str ); +char *ccStrEndWord( char *str ); +char *ccStrNextWordSameLine( char *str ); +char *ccStrNextParam( char *str ); +char *ccStrNextLine( char *str ); +char *ccStrPassLine( char *str ); +int ccStrParseInt32( char *str, int32_t *retint ); +int ccSeqParseInt32( char *seq, int seqlength, int32_t *retint ); +int ccStrParseInt64( char *str, int64_t *retint ); +int ccSeqParseInt64( char *seq, int seqlength, int64_t *retint ); +int ccStrParseFloat( char *str, float *retfloat ); +int ccSeqParseFloat( char *seq, int seqlength, float *retfloat ); +int ccStrParseDouble( char *str, double *retdouble ); +int ccSeqParseDouble( char *seq, int seqlength, double *retdouble ); +int ccStrParseHex( char *str, int hexchars ); +char *ccStrAllocPrintf( char *format, ... ); +char *ccStrDup( char *str ); +int ccUnicodeToUtf8( char *s, uint32_t unicode ); +/* Returns 1 when data is insufficient, send more bytes ; state must be initialized to zero */ +uint32_t ccUtf8ToUnicode( uint32_t byte, uint32_t *state, uint32_t *retunicode ); + + +//// + + +#define CC_FLT_INT_MAPPING + +#if CPUCONF_FLOAT_SIZE == 4 +typedef uint32_t ccuintf; +#elif CPUCONF_FLOAT_SIZE == 8 +typedef uint64_t ccuintf; +#else + #undef CC_FLT_INT_MAPPING +#endif + +#if CPUCONF_DOUBLE_SIZE == 4 +typedef uint32_t ccuintd; +#elif CPUCONF_DOUBLE_SIZE == 8 +typedef uint64_t ccuintd; +#else + #undef CC_FLT_INT_MAPPING +#endif + + +#ifdef CC_FLT_INT_MAPPING + +static inline ccuintf ccFloatToUint( float f ) +{ + void *p = &f; + return *((ccuintf *)p); +} + +static inline float ccUintToFloat( ccuintf f ) +{ + void *p = &f; + return *((float *)p); +} + + +static inline ccuintd ccDoubleToUint( double d ) +{ + void *p = &d; + return *((ccuintd *)p); +} + +static inline double ccUintToDouble( ccuintd d ) +{ + void *p = &d; + return *((double *)p); +} + +#endif + + + +//// + + + +#define CC_LOG2_E 1.4426950408889634073599246810018921 + +static inline float ccFastExpFloat( float x ) +{ + union + { + uint32_t i; + float f; + } u; + if( x > 88.0 ) + return expf( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int32_t)( x * ( (float)0x800000 * (float)CC_LOG2_E ) ) + ( 0x3f800000 - 486408 ); + return u.f; +} + +static inline float ccFastExpFloatNearZero( float x ) +{ + union + { + uint32_t i; + float f; + } u; + if( x > 88.0 ) + return expf( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int32_t)( x * ( (float)0x800000 * (float)CC_LOG2_E ) ) + 0x3f800000; + return u.f; +} + +static inline double ccFastExpDouble( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + union + { + uint64_t i; + double d; + } u; + if( x > 88.0 ) + return exp( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int64_t)( x * ( (double)0x10000000000000 * CC_LOG2_E ) ) + ( (uint64_t)0x3ff0000000000000 - (uint64_t)261138306564096 ); + return u.d; +#else + union + { + uint32_t i[2]; + double d; + } u; + if( x > 88.0 ) + return expf( (float)x ); + else if( x < -80.0 ) + return 0.0; + #ifdef CPUCONF_LITTLE_ENDIAN + u.i[1] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + ( 0x3ff00000 - 60801 ); + u.i[0] = 0; + #else + u.i[0] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + ( 0x3ff00000 - 60801 ); + u.i[1] = 0; + #endif + return u.d; +#endif +} + +static inline double ccFastExpDoubleNearZero( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + union + { + uint64_t i; + double d; + } u; + if( x > 88.0 ) + return expf( x ); + else if( x < -80.0 ) + return 0.0; + u.i = (int64_t)( x * ( (double)0x10000000000000 * CC_LOG2_E ) ) + (uint64_t)0x3ff0000000000000; + return u.d; +#else + union + { + uint32_t i[2]; + double d; + } u; + if( x > 88.0 ) + return expf( (float)x ); + else if( x < -80.0 ) + return 0.0; + #ifdef CPUCONF_LITTLE_ENDIAN + u.i[1] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + 0x3ff00000; + u.i[0] = 0; + #else + u.i[0] = (int32_t)( x * ( (double)0x100000 * CC_LOG2_E ) ) + 0x3ff00000; + u.i[1] = 0; + #endif + return u.d; +#endif +} + + + +//// + + + +static inline float ccFastLog2Float( float x ) +{ + int base; + union + { + uint32_t i; + float f; + } u; + u.f = x; + base = ( ( u.i >> 23 ) & 0xff ) - 0x80; + u.i &= ~( (uint32_t)0xff << 23 ); + u.i += (uint32_t)0x7f << 23; + return (float)base + ( u.f * ( 2.0f + u.f * ( -1.0f/3.0f ) ) ) - ( 2.0f/3.0f ); +} + +static inline float ccFastLog2Double( double x ) +{ +#if CPUCONF_WORD_SIZE >= 64 + int base; + union + { + uint64_t i; + double f; + } u; + u.f = x; + base = ( ( u.i >> 52 ) & 0x7ff ) - 0x400; + u.i &= ~( (uint64_t)0x7ff << 52 ); + u.i += (uint64_t)0x3ff << 52; +#else + int base; + union + { + uint32_t i[2]; + double f; + } u; + u.f = x; + base = ( ( u.i[1] >> 20 ) & 0x7ff ) - 0x400; + u.i[1] &= ~( (uint32_t)0x7ff << 20 ); + u.i[1] += (uint32_t)0x3ff << 20; +#endif + return (float)(base + ( u.f * ( 2.0f + u.f * ( -1.0f/3.0f ) ) ) - ( 2.0f/3.0f )); +} + + + +//// + + +/* Only valid between -M_PI and M_PI */ +static inline float ccFastSinFloat( float x ) +{ + float s; + s = (float)(( 1.27323954474 * x ) + ( -0.405284734569 * x * fabsf( x ) )); + return s; +} + +/* Only valid between -M_PI and M_PI */ +static inline double ccFastSinDouble( double x ) +{ + double s; + s = ( 1.27323954474 * x ) + ( -0.405284734569 * x * fabs( x ) ); + return s; +} + + + +//// + + + +#define CC_INT16_BSWAP(i) (__extension__({uint16_t bsw=(i);((bsw&0xff)<<8)|(bsw>>8);})) +#define CC_INT32_BSWAP(i) (__extension__({uint32_t bsw=(i);(bsw<<24)|((bsw&0xff00)<<8)|((bsw>>8)&0xff00)|(bsw>>24);})) +#define CC_INT64_BSWAP(i) (__extension__({uint64_t bsw=(i);(bsw>>56)|((bsw&0x00ff000000000000LL)>>40)|((bsw&0x0000ff0000000000LL)>>24)|((bsw&0x000000ff00000000LL)>>8)|((bsw&0x00000000ff000000LL)<<8)|((bsw&0x0000000000ff0000LL)<<24)|((bsw&0x000000000000ff00LL)<<40)|(bsw<<56);})) + + +static inline uint16_t ccByteSwap16( uint16_t i ) +{ + return (uint16_t)(CC_INT16_BSWAP( i )); +} + +#if defined(__GNUC__) && defined(__i386__) + +static inline uint32_t ccByteSwap32( uint32_t i ) +{ + __asm__( "bswap %0" : "=r" (i) : "0" (i) ); + return i; +} + +static inline uint64_t ccByteSwap64( uint64_t i ) +{ + union { + uint32_t s[2]; + uint64_t i; + } u; + u.i = i; + __asm__( "bswapl %0 ; bswapl %1 ; xchgl %0,%1" : "=r" (u.s[0]), "=r" (u.s[1]) : "0" (u.s[0]), "1" (u.s[1]) ); + return u.i; +} + +#elif defined(__GNUC__) && defined(__x86_64__) + +static inline uint32_t ccByteSwap32( uint32_t i ) +{ + __asm__( "bswapl %0" : "=r" (i) : "0" (i) ); + return i; +} + +static inline uint64_t ccByteSwap64( uint64_t i ) +{ + __asm__( "bswapq %0" : "=r" (i) : "0" (i) ); + return i; +} + +#else + +static inline uint32_t ccByteSwap32( uint32_t i ) +{ + return CC_INT32_BSWAP( i ); +} + +static inline uint64_t ccByteSwap64( uint64_t i ) +{ + return CC_INT64_BSWAP( i ); +} + +#endif + +static inline float ccByteSwapf( float f ) +{ + uint32_t i; + void *p; + p = &f; + i = ccByteSwap32( *((uint32_t *)p) ); + p = &i; + return *((float *)p); +} + +static inline double ccByteSwapd( double f ) +{ + uint64_t i; + void *p; + p = &f; + i = ccByteSwap64( *((uint64_t *)p) ); + p = &i; + return *((double *)p); +} + + +static inline uint32_t ccAlignInt32( uint32_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + return i + 1; +} + +static inline uint64_t ccAlignInt64( uint64_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + i |= i >> 32; + return i + 1; +} + +static inline uintptr_t ccAlignIntPtr( uintptr_t i ) +{ + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; +#if CPUCONF_INTPTR_BITS > 32 + i |= i >> 32; +#endif + return i + 1; +} + + + +//// + + + +static inline uint8_t ccRotateLeft8( uint8_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 8 - bits ) ); +} + +static inline uint16_t ccRotateLeft16( uint16_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 16 - bits ) ); +} + +static inline uint32_t ccRotateLeft32( uint32_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 32 - bits ) ); +} + +static inline uint64_t ccRotateLeft64( uint64_t x, int bits ) +{ + return ( x << bits ) | ( x >> ( 64 - bits ) ); +} + + +static inline uint8_t ccRotateRight8( uint8_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 8 - bits ) ); +} + +static inline uint16_t ccRotateRight16( uint16_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 16 - bits ) ); +} + +static inline uint32_t ccRotateRight32( uint32_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 32 - bits ) ); +} + +static inline uint64_t ccRotateRight64( uint64_t x, int bits ) +{ + return ( x >> bits ) | ( x << ( 64 - bits ) ); +} + + +//// + + +#define CC_INT32_MAX ((((uint32_t)1)<<31)-1) + +static inline int32_t ccFloatToInt32Sat( float f ) +{ + if( f >= (float)CC_INT32_MAX ) + return CC_INT32_MAX; + else if( f <= -(float)CC_INT32_MAX ) + return -CC_INT32_MAX; + else + return (int32_t)f; +} + + +static inline int32_t ccDoubleToInt32Sat( double f ) +{ + if( f >= (double)CC_INT32_MAX ) + return CC_INT32_MAX; + else if( f <= -(double)CC_INT32_MAX ) + return -CC_INT32_MAX; + else + return (int32_t)f; +} + + +#define CC_INT64_MAX ((((uint64_t)1)<<63)-1) + +static inline int64_t ccFloatToInt64Sat( float f ) +{ + if( f >= (float)CC_INT64_MAX ) + return CC_INT64_MAX; + else if( f <= -(float)CC_INT64_MAX ) + return -CC_INT64_MAX; + else + return (int64_t)f; +} + + +static inline int64_t ccDoubleToInt64Sat( double f ) +{ + if( f >= (double)CC_INT64_MAX ) + return CC_INT64_MAX; + else if( f <= -(double)CC_INT64_MAX ) + return -CC_INT64_MAX; + else + return (int64_t)f; +} + + +//// + + + +void ccQuickSort( void **table, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ); +void ccQuickSortContext( void **table, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ); + +int ccMergeSort( void **src, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ) ); +int ccMergeSortContext( void **src, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context ); + +void ccHybridSort( void **table, void **tmp, int count, int (*sortfunc)( void *t0, void *t1 ), uint32_t randmask ); +void ccHybridSortContext( void **table, void **tmp, int count, int (*sortfunc)( void *context, void *t0, void *t1 ), void *context, uint32_t randmask ); + + + +//// + + + +void ccDebugLog( char *filename, char *string, ... ); + + +//// + + +typedef struct +{ + size_t allocsize; + size_t offset; + char *data; +} ccGrowth; + +void ccGrowthInit( ccGrowth *growth, int defaultsize ); +int ccGrowthPrintf( ccGrowth *growth, char *format, ... ); +int ccGrowthData( ccGrowth *growth, void *data, size_t size ); +int ccGrowthSeek( ccGrowth *growth, int offset ); +void ccGrowthFree( ccGrowth *growth ); +void ccGrowthElapsedTimeString( ccGrowth *growth, int64_t timecount, int maxfieldcount ); + + +//// + + +void *ccFileLoad( const char *path, size_t maxsize, size_t *retsize ); +size_t ccFileLoadDirect( const char *path, void *data, size_t minsize, size_t maxsize ); +int ccFileStore( const char *path, void *data, size_t datasize, int fsyncflag ); +int ccFileExists( char *path ); +int ccFileStat( char *path, size_t *retfilesize, time_t *retfiletime ); +int ccRenameFile( char *oldpath, char *newpath ); + + +//// + + +typedef struct _ccDir ccDir; + +ccDir *ccOpenDir( char *path ); +char *ccReadDir( ccDir *dir ); +void ccCloseDir( ccDir *dir ); + + +//// + + +int64_t ccGetFreeDiskSpace( char *dirpath ); + + +//// + + +int ccGetTimeOfDay( struct timeval *tv ); + +void ccSleep( int milliseconds ); + +static inline uint64_t ccGetMillisecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000 ) + ( (uint64_t)lntime.tv_usec / 1000 ); +} + +static inline uint64_t ccGetMicrosecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000000 ) + (uint64_t)lntime.tv_usec; +} + +static inline uint64_t ccGetNanosecondsTime() +{ + struct timeval lntime; + ccGetTimeOfDay( &lntime ); + return ( (uint64_t)lntime.tv_sec * 1000000000 ) + ( (uint64_t)lntime.tv_usec * 1000 ); +} + + +//// + + +/* Returned string must be free()d */ +char *ccGetSystemName(); diff --git a/ecere/src/gfx/newFonts/cc/cchybridsort.h b/ecere/src/gfx/newFonts/cc/cchybridsort.h new file mode 100644 index 0000000..9be1696 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cchybridsort.h @@ -0,0 +1,302 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/* + +Templates C style! + +#include this whole file with the following definitions set: + +#define HSORT_MAIN MyInlinedSortFunction +#define HSORT_CMP MyComparisonFunction +#define HSORT_TYPE int + +*/ + + +#ifndef CC_HSORT_INLINE + + #define CC_HSORT_SWAP(a,b) (__extension__({temp=table[b];table[b]=table[a];table[a]=temp;})) + #define CC_HSORT_STACK_DEPTH (512) + #define CC_HSORT_MIN_QSORT_COUNT (5) + +typedef struct +{ + void *table; + int count; + int depth; +} ccHybridSortStack; + + #define CC_HSORT_INLINE + #define CC_HSORT_STACK_DEPTH (512) + +#endif + + +#ifndef HSORT_COPY + #define HSORT_COPY(d,s) (*(d)=*(s)) + #define CC_HSORT_COPY +#endif + + +#ifdef HSORT_CONTEXT + #define HSORT_CONTEXT_PARAM , HSORT_CONTEXT context + #define HSORT_CONTEXT_PASS context, + #define HSORT_CONTEXT_PASSLAST , context +#else + #define HSORT_CONTEXT_PARAM + #define HSORT_CONTEXT_PASS + #define HSORT_CONTEXT_PASSLAST +#endif + + +/* Build merge sort pipeline */ +#define HSORT_MERGE_FUNCX(n) n##Merge +#define HSORT_MERGE_FUNC(n) HSORT_MERGE_FUNCX(n) +#define MSORT_MAIN HSORT_MERGE_FUNC(HSORT_MAIN) +#define MSORT_CMP HSORT_CMP +#define MSORT_TYPE HSORT_TYPE +#ifdef HSORT_CONTEXT + #define MSORT_CONTEXT HSORT_CONTEXT +#endif +#include "ccmergesort.h" +#undef MSORT_MAIN +#undef MSORT_CMP +#undef MSORT_TYPE +#ifdef HSORT_CONTEXT + #undef MSORT_CONTEXT +#endif + + +#define HSORT_PART_FUNCX(n) n##Part +#define HSORT_PART_FUNC(n) HSORT_PART_FUNCX(n) + +static void HSORT_PART_FUNC(HSORT_MAIN)( HSORT_TYPE *table, int count HSORT_CONTEXT_PARAM ) +{ + HSORT_TYPE temp; + if( count == 4 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + CC_HSORT_SWAP( 1, 0 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + CC_HSORT_SWAP( 3, 2 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + { + CC_HSORT_SWAP( 3, 2 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + CC_HSORT_SWAP( 2, 1 ); + } + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + CC_HSORT_SWAP( 2, 1 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) ) + CC_HSORT_SWAP( 3, 2 ); + } + } + } + else if( count == 3 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + /* [1]>[0], [2]>[1] = [2]>[1]>[0] */ + CC_HSORT_SWAP( 2, 0 ); + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */ + temp = table[0]; + table[0] = table[1]; + table[1] = table[2]; + table[2] = temp; + } + else + { + /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */ + CC_HSORT_SWAP( 1, 0 ); + } + } + } + else + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) ) + { + /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */ + temp = table[2]; + table[2] = table[1]; + table[1] = table[0]; + table[0] = temp; + } + else + { + /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */ + CC_HSORT_SWAP( 1, 2 ); + } + } + else + { + /* [1]<[0], [2]<[1] = [0]>[1]>[2] */ + } + } + } + else if( count == 2 ) + { + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) ) + CC_HSORT_SWAP( 1, 0 ); + } + return; +} + + +static void HSORT_MAIN( HSORT_TYPE *table, HSORT_TYPE *tmp, int count HSORT_CONTEXT_PARAM, uint32_t randmask ) +{ + int depth, depthmax, tmpcountalloc; + ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotoffset; + HSORT_TYPE temp; + HSORT_TYPE *mergesrc; + HSORT_TYPE pivot; + ccHybridSortStack stack[CC_HSORT_STACK_DEPTH]; + ccHybridSortStack *sp; +#ifdef HSORT_PIVOT_STORE + HSORT_PIVOT_STORE pivotstore; +#endif + + if( count <= 1 ) + return; + + depth = 0; + depthmax = 1 + ccLog2Int32( count ); + tmpcountalloc = count; + + sp = stack; + for( ; ; ) + { + if( count < CC_HSORT_MIN_QSORT_COUNT ) + { + HSORT_PART_FUNC(HSORT_MAIN)( table, count HSORT_CONTEXT_PASSLAST ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Pathological case, switch to merge sort */ + if( depth >= depthmax ) + { + if( !( tmp ) ) + { + tmp = malloc( tmpcountalloc * sizeof(HSORT_TYPE) ); + tmpcountalloc = 0; + } + mergesrc = HSORT_MERGE_FUNC(HSORT_MAIN)( table, tmp, count HSORT_CONTEXT_PASSLAST ); + if( mergesrc != table ) + memcpy( table, mergesrc, count * sizeof(HSORT_TYPE) ); + if( sp == stack ) + break; + sp--; + table = sp->table; + count = sp->count; + depth = sp->depth; + continue; + } + + /* Select pivot */ + randmask += count; + pivotindex = 1 + ( randmask % ( count-2 ) ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) ) + CC_HSORT_SWAP( pivotindex, 0 ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[pivotindex], &table[count-1] ) ) + { + CC_HSORT_SWAP( count-1, pivotindex ); + if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) ) + CC_HSORT_SWAP( pivotindex, 0 ); + } + + /* Quick sort on both sides of the pivot */ + pivot = table[pivotindex]; + highindex = count - 2; + pivotoffset = highindex; + CC_HSORT_SWAP( pivotoffset, pivotindex ); + pivotindex = 1; +#ifdef HSORT_PIVOT_STORE + HSORT_PIVOT_INIT( HSORT_CONTEXT_PASS &pivotstore, &pivot ); +#endif + for( i = highindex ; --i ; ) + { + /* Optional optimization for constant pivot */ +#ifdef HSORT_PIVOT_STORE + if( HSORT_PIVOT_CMP( HSORT_CONTEXT_PASS &pivotstore, &pivot, &table[pivotindex] ) ) +#else + if( HSORT_CMP( HSORT_CONTEXT_PASS &pivot, &table[pivotindex] ) ) +#endif + pivotindex++; + else + { + highindex--; + CC_HSORT_SWAP( highindex, pivotindex ); + } + } + CC_HSORT_SWAP( pivotindex, pivotoffset ); + + /* Count of entries on both sides of the pivot */ + leftcount = pivotindex; + pivotindex++; + rightcount = count - pivotindex; + + /* Fast sort small chunks, iterate */ + if( leftcount < rightcount ) + { + depth++; + sp->table = &table[pivotindex]; + sp->count = (int)rightcount; + sp->depth = depth; + sp++; + count = (int)leftcount; + } + else + { + depth++; + sp->table = table; + sp->count = (int)leftcount; + sp->depth = depth; + sp++; + table += pivotindex; + count = (int)rightcount; + } + } + + if( !( tmpcountalloc ) ) + free( tmp ); + + return; +} + + +#ifdef CC_HSORT_COPY + #undef HSORT_COPY + #undef CC_HSORT_COPY +#endif + +#undef HSORT_CONTEXT_PARAM +#undef HSORT_CONTEXT_PASS +#undef HSORT_CONTEXT_PASSLAST + diff --git a/ecere/src/gfx/newFonts/cc/ccmergesort.h b/ecere/src/gfx/newFonts/cc/ccmergesort.h new file mode 100644 index 0000000..4c95358 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/ccmergesort.h @@ -0,0 +1,215 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/* + +Templates C style! + +#include this whole file with the following definitions set: + +#define MSORT_MAIN MyInlinedSortFunction +#define MSORT_CMP MyComparisonFunction +#define MSORT_TYPE int + +*/ + + +#ifndef CC_MSORT_INLINE + +typedef struct +{ + void *src; + void *dst; + int count; + int mergeflag; + int depthbit; +} ccMergeSortStack; + + #define CC_MSORT_INLINE + #define CC_MSORT_STACK_DEPTH (512) + +#endif + + +#ifndef MSORT_COPY + #define MSORT_COPY(d,s) (*(d)=*(s)) + #define CC_MSORT_COPY +#endif + + +#ifdef MSORT_CONTEXT + #define MSORT_CONTEXT_PARAM , MSORT_CONTEXT context + #define MSORT_CONTEXT_PASS context, + #define MSORT_CONTEXT_PASSLAST , context +#else + #define MSORT_CONTEXT_PARAM + #define MSORT_CONTEXT_PASS + #define MSORT_CONTEXT_PASSLAST +#endif + + +static MSORT_TYPE *MSORT_MAIN( MSORT_TYPE *src, MSORT_TYPE *tmp, int count MSORT_CONTEXT_PARAM ) +{ + int swapflag, depthbit, maxdepthbit; + ssize_t leftcount, rightcount; + MSORT_TYPE *dst; + MSORT_TYPE *sl; + MSORT_TYPE *sr; + MSORT_TYPE *dstend; + MSORT_TYPE *dstbase; + MSORT_TYPE *swap; + MSORT_TYPE temp; + ccMergeSortStack stack[CC_MSORT_STACK_DEPTH]; + ccMergeSortStack *sp; + + if( count <= 1 ) + return src; + + dst = tmp; + sp = stack; + swapflag = 0; + depthbit = 0; + + leftcount = count; + for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 ) + { + leftcount = leftcount - ( leftcount >> 1 ); + if( leftcount <= 4 ) + break; + } + + for( ; ; ) + { + if( count <= 4 ) + { + if( !( depthbit ^ maxdepthbit ) ) + { + if( ( count == 4 ) && MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) ) + { + MSORT_COPY( &temp, &src[2] ); + MSORT_COPY( &src[2], &src[3] ); + MSORT_COPY( &src[3], &temp ); + } + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) ) + { + MSORT_COPY( &temp, &src[0] ); + MSORT_COPY( &src[0], &src[1] ); + MSORT_COPY( &src[1], &temp ); + } + swapflag = 0; + } + else + { + if( count == 4 ) + { + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) ) + { + MSORT_COPY( &dst[2], &src[3] ); + MSORT_COPY( &dst[3], &src[2] ); + } + else + { + MSORT_COPY( &dst[2], &src[2] ); + MSORT_COPY( &dst[3], &src[3] ); + } + } + else if( count == 3 ) + MSORT_COPY( &dst[2], &src[2] ); + if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) ) + { + MSORT_COPY( &dst[0], &src[1] ); + MSORT_COPY( &dst[1], &src[0] ); + } + else + { + MSORT_COPY( &dst[0], &src[0] ); + MSORT_COPY( &dst[1], &src[1] ); + } + swap = src; + src = dst; + dst = swap; + swapflag = 1; + } + } + else + { + rightcount = count >> 1; + leftcount = count - rightcount; + sp->src = src; + sp->dst = dst; + sp->count = count; + sp->mergeflag = 1; + sp->depthbit = depthbit; + depthbit ^= 1; + sp++; + sp->src = src + leftcount; + sp->dst = dst + leftcount; + sp->count = (int)rightcount; + sp->mergeflag = 0; + sp->depthbit = depthbit; + sp++; + count = (int)leftcount; + continue; + } + + for( ; ; ) + { + rightcount = count >> 1; + leftcount = count - rightcount; + sl = src; + sr = src + leftcount; + dstbase = dst; + for( ; ; ) + { + if( MSORT_CMP( MSORT_CONTEXT_PASS sl, sr ) ) + { + MSORT_COPY( dst++, sr++ ); + if( --rightcount ) + continue; + for( dstend = &dst[leftcount] ; dst < dstend ; ) + MSORT_COPY( dst++, sl++ ); + break; + } + else + { + MSORT_COPY( dst++, sl++ ); + if( --leftcount ) + continue; + for( dstend = &dst[rightcount] ; dst < dstend ; ) + MSORT_COPY( dst++, sr++ ); + break; + } + } + if( sp == stack ) + return dstbase; + sp--; + src = sp->src; + dst = sp->dst; + count = sp->count; + depthbit = sp->depthbit; + if( !( sp->mergeflag ) ) + break; + swapflag ^= 1; + if( swapflag ) + { + src = sp->dst; + dst = sp->src; + } + } + } + + return 0; +} + + +#ifdef CC_MSORT_COPY + #undef MSORT_COPY + #undef CC_MSORT_COPY +#endif + +#undef MSORT_CONTEXT_PARAM +#undef MSORT_CONTEXT_PASS +#undef MSORT_CONTEXT_PASSLAST + diff --git a/ecere/src/gfx/newFonts/cc/cpuconfig.h b/ecere/src/gfx/newFonts/cc/cpuconfig.h new file mode 100644 index 0000000..fd817be --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/cpuconfig.h @@ -0,0 +1,104 @@ +#ifndef __CPUCONFIG_H__ +#define __CPUCONFIG_H__ + +/* Automatically generated CPU information header */ + +#define CPUCONF_CHAR_SIZE (1) +#define CPUCONF_SHORT_SIZE (2) +#define CPUCONF_INT_SIZE (4) +#define CPUCONF_LONG_SIZE (4) +#define CPUCONF_LONG_LONG_SIZE (8) +#define CPUCONF_INTPTR_SIZE (8) +#define CPUCONF_POINTER_SIZE (8) +#define CPUCONF_FLOAT_SIZE (4) +#define CPUCONF_DOUBLE_SIZE (8) +#define CPUCONF_LONG_DOUBLE_SIZE (16) + +#define CPUCONF_CHAR_BITS (8) +#define CPUCONF_SHORT_BITS (16) +#define CPUCONF_INT_BITS (32) +#define CPUCONF_LONG_BITS (32) +#define CPUCONF_LONG_LONG_BITS (64) +#define CPUCONF_INTPTR_BITS (64) +#define CPUCONF_POINTER_BITS (64) +#define CPUCONF_FLOAT_BITS (32) +#define CPUCONF_DOUBLE_BITS (64) +#define CPUCONF_LONG_DOUBLE_BITS (128) + +#define CPUCONF_CHAR_SIZESHIFT (0) +#define CPUCONF_SHORT_SIZESHIFT (1) +#define CPUCONF_INT_SIZESHIFT (2) +#define CPUCONF_LONG_SIZESHIFT (2) +#define CPUCONF_LONG_LONG_SIZESHIFT (3) +#define CPUCONF_INTPTR_SIZESHIFT (3) +#define CPUCONF_POINTER_SIZESHIFT (3) +#define CPUCONF_FLOAT_SIZESHIFT (2) +#define CPUCONF_DOUBLE_SIZESHIFT (3) +#define CPUCONF_LONG_DOUBLE_SIZESHIFT (4) + +#define CPUCONF_CHAR_BITSHIFT (3) +#define CPUCONF_SHORT_BITSHIFT (4) +#define CPUCONF_INT_BITSHIFT (5) +#define CPUCONF_LONG_BITSHIFT (5) +#define CPUCONF_LONG_LONG_BITSHIFT (6) +#define CPUCONF_INTPTR_BITSHIFT (6) +#define CPUCONF_POINTER_BITSHIFT (6) +#define CPUCONF_FLOAT_BITSHIFT (5) +#define CPUCONF_DOUBLE_BITSHIFT (6) +#define CPUCONF_LONG_DOUBLE_BITSHIFT (7) + +#define CPUCONF_LITTLE_ENDIAN +#define CPUCONF_ARCH_AMD64 +#define CPUCONF_VENDOR_INTEL +#define CPUCONF_IDENTIFIER "Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz" +//#define CPUCONF_CLASS_COREI7-AVX2 +#define CPUCONF_SOCKET_LOGICAL_CORES (16) +#define CPUCONF_SOCKET_PHYSICAL_CORES (8) +#define CPUCONF_TOTAL_CORE_COUNT (8) +#define CPUCONF_SYSTEM_MEMORY (17072009216LL) +#define CPUCONF_WORD_SIZE (64) +#define CPUCONF_CACHE_LINE_SIZE (64) +#define CPUCONF_CACHE_L1CODE_SIZE (32768) +#define CPUCONF_CACHE_L1CODE_LINE (64) +#define CPUCONF_CACHE_L1CODE_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L1CODE_SHARED (2) +#define CPUCONF_CACHE_L1DATA_SIZE (32768) +#define CPUCONF_CACHE_L1DATA_LINE (64) +#define CPUCONF_CACHE_L1DATA_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L1DATA_SHARED (2) +#define CPUCONF_CACHE_L1_UNIFIED_FLAG (0) +#define CPUCONF_CACHE_L2_SIZE (262144) +#define CPUCONF_CACHE_L2_LINE (64) +#define CPUCONF_CACHE_L2_ASSOCIATIVITY (8) +#define CPUCONF_CACHE_L2_SHARED (2) +#define CPUCONF_CACHE_L3_SIZE (8388608) +#define CPUCONF_CACHE_L3_LINE (64) +#define CPUCONF_CACHE_L3_ASSOCIATIVITY (16) +#define CPUCONF_CACHE_L3_SHARED (16) +#define CPUCONF_CAP_GPREGS (16) +#define CPUCONF_CAP_FPREGS (16) + +#define CPUCONF_CAP_CMOV +#define CPUCONF_CAP_CLFLUSH +#define CPUCONF_CAP_TSC +#define CPUCONF_CAP_MMX +#define CPUCONF_CAP_SSE +#define CPUCONF_CAP_SSE2 +#define CPUCONF_CAP_SSE3 +#define CPUCONF_CAP_SSSE3 +#define CPUCONF_CAP_SSE4_1 +#define CPUCONF_CAP_SSE4_2 +#define CPUCONF_CAP_AVX +#define CPUCONF_CAP_AVX2 +#define CPUCONF_CAP_AES +#define CPUCONF_CAP_PCLMUL +#define CPUCONF_CAP_CMPXCHG16B +#define CPUCONF_CAP_MOVBE +#define CPUCONF_CAP_RDTSCP +#define CPUCONF_CAP_CONSTANTTSC +#define CPUCONF_CAP_HYPERTHREADING +#define CPUCONF_CAP_MWAIT +#define CPUCONF_CAP_THERMALSENSOR +#define CPUCONF_CAP_CLOCKMODULATION + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mm.c b/ecere/src/gfx/newFonts/cc/mm.c new file mode 100644 index 0000000..c44012f --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mm.c @@ -0,0 +1,3430 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Global memory management routines. + * + * This file includes all the generic memory management routines used + * throughout the code : linked lists, balanced trees, block memory allocation, + * growing memory allocation, page-based pointer directories, aligned memory + * allocation, memory volume management, memory leak or buffer overrun + * tracking, etc. + */ + + +#define MM_THREADING (1) + + +#ifdef MM_THREADING + #define _GNU_SOURCE + #include + #include +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "cpuconfig.h" +#include "cc.h" +#include "mm.h" + + +#if defined(MM_UNIX) + #include + #include + #include + #include + #include +/* + #include +*/ +#elif defined(MM_WINDOWS) + #include +#endif + +#if _POSIX_MAPPED_FILES > 0 + #include + #define MM_ZONE_SUPPORT 1 +#endif + +#if defined(MM_LINUX) + #include + #include +#endif + +#if defined(MM_WIN32) + #include + #include +#endif + +#if MM_OSX + #ifndef MAP_ANONYMOUS + #define MAP_ANONYMOUS MAP_ANON + #endif +#endif + + +#ifndef ADDRESS +#define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF +#define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + + +#if MM_DEBUG + #define MM_PASSPARAMS , file, line +#else + #define MM_PASSPARAMS +#endif + + +//// + + +int mmInitStatus = 0; + +mmContext mmcontext; + +#ifdef MT_MUTEX_INITIALIZER +static mtMutex mmGlobalMutex = MT_MUTEX_INITIALIZER; +#else +static mtMutex mmGlobalMutex; +#endif + +#ifdef MM_NUMA + +int mmNumaInit() +{ + int nodeindex, cpuindex, cpurange; + int nodecpucount; + struct bitmask *cpus; + if( numa_available() == -1 ) + return 0; + mmcontext.numaflag = 1; + cpurange = numa_num_configured_cpus(); + if( cpurange >= MM_CPU_COUNT_MAXIMUM ) + { + fprintf( stderr, "CPU count %d exceeds %d, increase MM_CPU_COUNT_MAXIMUM in mm.h and try again.\n", cpurange, MM_CPU_COUNT_MAXIMUM ); + exit( 1 ); + } + mmcontext.nodecount = numa_num_configured_nodes(); + if( mmcontext.nodecount >= MM_NODE_COUNT_MAXIMUM ) + { + fprintf( stderr, "Node count %d exceeds %d, increase MM_NODE_COUNT_MAXIMUM in mm.h and try again.\n", mmcontext.nodecount, MM_NODE_COUNT_MAXIMUM ); + exit( 1 ); + } + mmcontext.cpucount = 0; + mmcontext.sysmemory = 0; + for( nodeindex = 0 ; nodeindex < mmcontext.nodecount ; nodeindex++ ) + { + mmcontext.nodesize[nodeindex] = numa_node_size64( nodeindex, 0 ); + mmcontext.sysmemory += mmcontext.nodesize[nodeindex]; + mmcontext.nodecpucount[nodeindex] = 0; + if( !( numa_bitmask_isbitset( numa_all_nodes_ptr, nodeindex ) ) ) + continue; + nodecpucount = 0; + cpus = numa_bitmask_alloc( cpurange ); + if( numa_node_to_cpus( nodeindex, cpus ) >= 0 ) + { + for( cpuindex = 0 ; cpuindex < cpurange ; cpuindex++ ) + { + if( numa_bitmask_isbitset( cpus, cpuindex ) ) + { + mmcontext.cpunode[ cpuindex ] = nodeindex; + nodecpucount++; + } + } + } + numa_bitmask_free( cpus ); + mmcontext.nodecpucount[nodeindex] = nodecpucount; + mmcontext.cpucount += nodecpucount; + } + return 1; +} + +#endif + +void numa_warn( int num, char *fmt, ... ) +{ + return; +} + +void mmInit() +{ + int64_t sysmemory; + if( mmInitStatus ) + return; + mmcontext.numaflag = 0; +#ifdef MM_NUMA + mmNumaInit(); +#endif + if( !( mmcontext.numaflag ) ) + { + mmcontext.nodecount = 1; + sysmemory = -1; + #if defined(MM_LINUX) + mmcontext.cpucount = get_nprocs(); + mmcontext.pagesize = sysconf(_SC_PAGESIZE); + #elif defined(MM_UNIX) + mmcontext.cpucount = sysconf( _SC_NPROCESSORS_CONF ); + mmcontext.pagesize = sysconf(_SC_PAGESIZE); + #elif defined(MM_WIN32) + SYSTEM_INFO sysinfo; + GetSystemInfo( &sysinfo ); + mmcontext.cpucount = sysinfo.dwNumberOfProcessors; + mmcontext.pagesize = sysinfo.dwPageSize; + #else + mmcontext.cpucount = 1; + #endif + #if defined(MM_UNIX) && defined(_SC_PHYS_PAGES) + sysmemory = sysconf( _SC_PHYS_PAGES ); + if( sysmemory > 0 ) + sysmemory *= mmcontext.pagesize; + #endif + mmcontext.sysmemory = sysmemory; + mmcontext.nodesize[0] = mmcontext.sysmemory; + mmcontext.nodecpucount[0] = mmcontext.cpucount; + } + mtMutexInit( &mmGlobalMutex ); + mmInitStatus = 1; +/* + { + int nodeindex, cpuindex; + printf( "NUMA nodes : %d\n", mmcontext.nodecount ); + for( nodeindex = 0 ; nodeindex < mmcontext.nodecount ; nodeindex++ ) + printf( " NUMA node %d, size %lld, CPU count %d\n", nodeindex, (long long)mmcontext.nodesize[nodeindex], mmcontext.nodecpucount[nodeindex] ); + printf( "CPUs : %d\n", mmcontext.cpucount ); + for( cpuindex = 0 ; cpuindex < mmcontext.cpucount ; cpuindex++ ) + printf( " CPU %d on node %d\n", cpuindex, mmcontext.cpunode[ cpuindex ] ); + } +*/ + return; +} + + +void mmEnd() +{ + mtMutexDestroy( &mmGlobalMutex ); + return; +} + + +//// + + +void mmThreadBindToNode( int nodeindex ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_run_on_node( nodeindex ); + return; + } +#endif +#if defined(MM_LINUX) + int cpuindex; + cpu_set_t cpuset; + CPU_ZERO( &cpuset ); + for( cpuindex = 0 ; cpuindex < mmcontext.cpucount ; cpuindex++ ) + CPU_SET( cpuindex, &cpuset ); + sched_setaffinity( 0, sizeof(cpu_set_t), &cpuset ); +#endif + return; +} + +void mmThreadBindToCpu( int cpuindex ) +{ +#if defined(MM_LINUX) + cpu_set_t cpuset; + CPU_ZERO( &cpuset ); + CPU_SET( cpuindex, &cpuset ); + sched_setaffinity( 0, sizeof(cpu_set_t), &cpuset ); +#elif defined(MM_WIN32) + HANDLE *handle; + handle = GetCurrentThread(); + SetThreadAffinityMask( handle, 1 << cpuindex ); +#endif + return; +} + +int mmCpuGetNode( int cpuindex ) +{ + return mmcontext.cpunode[ cpuindex ]; +} + + +//// + + +void *mmNodeAlloc( int nodeindex, size_t size ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( size, nodeindex ); +#endif + return malloc( size ); +} + +void mmNodeFree( int nodeindex, void *v, size_t size ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, size ); + return; + } +#endif + free( v ); + return; +} + +void mmNodeMap( int nodeindex, void *start, size_t bytes ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + numa_tonode_memory( start, bytes, nodeindex ); +#endif + return; +} + +static void *mmNodeRelayAlloc( void *head, size_t bytes MM_PARAMS ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( bytes, (int)((intptr_t)head) ); +#endif + return malloc( bytes ); +} + +static void mmNodeRelayFree( void *head, void *v, size_t bytes MM_PARAMS ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, bytes ); + return; + } +#endif + free( v ); + return; +} + + +void *mmNodeAlignAlloc( int nodeindex, size_t bytes, intptr_t align ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + return numa_alloc_onnode( bytes, nodeindex ); +#endif + return mmAlignAlloc( bytes, align ); +} + +void mmNodeAlignFree( int nodeindex, void *v, size_t bytes ) +{ +#ifdef MM_NUMA + if( mmcontext.numaflag ) + { + numa_free( v, bytes ); + return; + } +#endif + mmAlignFree( v ); + return; +} + + + + +//// + + +#ifndef MM_INLINE_LIST_FUNCTIONS + +/** + * Add the item to a linked list. + * + * The head of the linked list should be defined as a void pointer. The list + * parameter can be a pointer to it, or a pointer to the mmListNode.next + * variable of the preceeding item. The offset parameter should be the offset + * of the mmListNode structure within the structure of the item. It can be + * easily obtained with the offsetof(,) macro. + */ +void mmListAdd( void **list, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = list; + node->next = *list; + if( *list ) + { + next = ADDRESS( *list, offset ); + next->prev = &(node->next); + } + *list = item; + return; +} + +/** + * Remove the item from a linked list. + * + * The offset parameter should be the offset of the mmListNode structure + * within the structure of the item. It can be easily obtained with the + * offsetof(,) macro. + */ +void mmListRemove( void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + return; +} + + + +void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + return; + for( item = *listdst ; item ; item = node->next ) + { + node = ADDRESS( item, offset ); + listdst = &node->next; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + + +void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + { + *listdst = 0; + return; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + + + +/** + * Initialize a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead + * structure, with the head parameter being a pointer to it. + */ +void mmListDualInit( mmListDualHead *head ) +{ + head->first = 0; + head->last = &head->first; + return; +} + + +/** + * Add the item to the beginning of a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = &head->first; + node->next = head->first; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + head->first = item; + return; +} + + +/** + * Add the item to the end of a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + void **prev; + prev = head->last; + *prev = item; + node = ADDRESS( item, offset ); + node->prev = head->last; + head->last = &(node->next); + node->next = 0; + return; +} + + +void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = prevnext; + node->next = *prevnext; + if( *prevnext ) + { + next = ADDRESS( *prevnext, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + *prevnext = item; + return; +} + + +/** + * Remove the item from a dual-direction linked list. + * + * The head of the linked list should be defined as a mmListDualHead structure, + * with the head parameter being a pointer to it. The offset parameter should + * be the offset of the mmListNode structure within the structure of the item. + * It can be easily obtained with the offsetof(,) macro. + */ +void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + else + head->last = node->prev; + return; +} + + +void *mmListDualLast( mmListDualHead *head, intptr_t offset ) +{ + if( !( head->first ) ) + return 0; + return ADDRESS( head->last, -( offset + offsetof(mmListNode,next) ) ); +} + + +void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + if( item == head->first ) + return 0; + node = ADDRESS( item, offset ); + return ADDRESS( node->prev, -( offset + offsetof(mmListNode,next) ) ); +} + + +#endif + + +void mmListLoopInit( mmListLoopHead *head ) +{ + head->first = 0; + head->last = 0; + return; +} + + +void mmListLoopAddFirst( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + node->prev = head->last; + node->next = head->first; + next = ADDRESS( head->first, offset ); + next->prev = item; + prev = ADDRESS( head->last, offset ); + prev->next = item; + head->first = item; + return; +} + + +void mmListLoopAddLast( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + prev = ADDRESS( head->last, offset ); + prev->next = item; + next = ADDRESS( head->first, offset ); + next->prev = item; + node->prev = head->last; + node->next = head->first; + head->last = item; + return; +} + + +void mmListLoopInsert( mmListLoopHead *head, void *previtem, void *item, intptr_t offset ) +{ + mmListNode *prev, *node, *next; + node = ADDRESS( item, offset ); + if( !( head->first ) ) + { + head->first = item; + head->last = item; + node->prev = item; + node->next = item; + return; + } + node->prev = previtem; + prev = ADDRESS( previtem, offset ); + node->next = prev->next; + prev->next = item; + next = ADDRESS( node->next, offset ); + next->prev = item; + if( head->last == previtem ) + head->last = item; + return; +} + + +void mmListLoopRemove( mmListLoopHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *prev, *next; + node = ADDRESS( item, offset ); + prev = ADDRESS( node->prev, offset ); + prev->next = node->next; + if( head->first == item ) + { + head->first = node->next; + if( head->first == item ) + head->first = 0; + } + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + if( head->last == item ) + { + head->last = node->prev; + if( head->last == item ) + head->last = 0; + } + return; +} + + + +//// + + + +/** + * Private function to balance a branch of the tree after an insertion. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeInsertBalance( void *item, intptr_t offset, void **root ) +{ + void *parent, *relative, *ancestor, *link; + mmBTreeNode *node, *pnode, *rnode, *anode, *lnode, *tnode; + + node = ADDRESS( item, offset ); + parent = node->parent; + + if( !( parent ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + *root = item; + return; + } + + pnode = ADDRESS( parent, offset ); + if( pnode->flags & MM_BTREE_FLAGS_STEP ) + return; + + ancestor = pnode->parent; + anode = ADDRESS( ancestor, offset ); + + relative = anode->child[ ( pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) ^ 1 ]; + if( ( relative ) && !( ( rnode = ADDRESS( relative, offset ) )->flags & MM_BTREE_FLAGS_STEP ) ) + { + anode->flags &= ~MM_BTREE_FLAGS_STEP; + pnode->flags |= MM_BTREE_FLAGS_STEP; + rnode->flags |= MM_BTREE_FLAGS_STEP; + mmBTreeInsertBalance( ancestor, offset, root ); + return; + } + + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) != ( pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) ) + { + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + { + node->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = item; + anode->flags = MM_BTREE_FLAGS_RIGHT; + anode->child[0] = node->child[1]; + if( anode->child[0] ) + { + tnode = ADDRESS( anode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->parent = item; + pnode->child[1] = node->child[0]; + if( pnode->child[1] ) + { + tnode = ADDRESS( pnode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = parent; + } + + if( relative ) + ( (mmBTreeNode *)ADDRESS( relative, offset ) )->flags |= MM_BTREE_FLAGS_STEP; + + node->child[0] = parent; + node->child[1] = ancestor; + node->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = item; + return; + } + *root = item; + return; + } + else + { + node->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = item; + anode->flags = 0; + anode->child[1] = node->child[0]; + if( anode->child[1] ) + { + tnode = ADDRESS( anode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->parent = item; + pnode->child[0] = node->child[1]; + if( pnode->child[0] ) + { + tnode = ADDRESS( pnode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = parent; + } + + if( relative ) + ( (mmBTreeNode *)ADDRESS( relative, offset ) )->flags |= MM_BTREE_FLAGS_STEP; + + node->child[0] = ancestor; + node->child[1] = parent; + node->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = item; + return; + } + *root = item; + return; + } + } + + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + { + pnode->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = parent; + anode->flags = 0; + anode->child[1] = pnode->child[0]; + if( anode->child[1] ) + { + tnode = ADDRESS( anode->child[1], offset ); + tnode->flags |= MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->child[0] = ancestor; + pnode->child[1] = item; + pnode->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = parent; + return; + } + *root = parent; + return; + } + else + { + pnode->flags = ( anode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + link = anode->parent; + + anode->parent = parent; + anode->flags = MM_BTREE_FLAGS_RIGHT; + anode->child[0] = pnode->child[1]; + if( anode->child[0] ) + { + tnode = ADDRESS( anode->child[0], offset ); + tnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + tnode->parent = ancestor; + } + + pnode->child[0] = item; + pnode->child[1] = ancestor; + pnode->parent = link; + if( link ) + { + lnode = ADDRESS( link, offset ); + lnode->child[ pnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = parent; + return; + } + *root = parent; + return; + } + + return; +} + + +/** + * Insert an item within the balanced tree + * + * Inserts the item specified at the position specified by the parent pointer + * and the itemflag, which can be either MM_BTREE_FLAGS_LEFT or + * MM_BTREE_FLAGS_RIGHT to indicate on which side of the parent to insert. The + * parent pointer can be null to indicate the top of the tree. The offset + * parameter should be the offset of the mmBTreeNode structure within the + * structure of the item. The root parameter is a pointer to the root of the + * tree. + */ +void mmBTreeInsert( void *item, void *parent, int itemflag, intptr_t offset, void **root ) +{ + mmBTreeNode *node, *pnode; + + node = ADDRESS( item, offset ); + node->parent = parent; + node->child[0] = 0; + node->child[1] = 0; + node->flags = itemflag; + if( parent ) + { + pnode = ADDRESS( parent, offset ); + pnode->child[itemflag] = item; + } + + mmBTreeInsertBalance( item, offset, root ); + + return; +} + + +/** + * Find lowest left gap to use as pivot for removal. + */ +static void *mmBTreeRemoveSeek( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + + node = ADDRESS( item, offset ); + item = node->child[1]; + for( ; ; ) + { + node = ADDRESS( item, offset ); + if( !( node->child[0] ) ) + break; + item = node->child[0]; + } + + return item; +} + + +static void mmBTreeRemoveBalanceLeft( void *item, intptr_t offset, void **root ); +static void mmBTreeRemoveBalanceRight( void *item, intptr_t offset, void **root ); + + +/** + * Private function to balance a left branch of the tree after a removal. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeRemoveBalanceLeft( void *item, intptr_t offset, void **root ) +{ + int mask; + void **plink; + void *litem, *llitem, *lritem, *lrritem, *lrlitem; + mmBTreeNode *node, *lnode, *llnode, *lrnode, *lrrnode, *lrlnode, *tempnode; + + node = ADDRESS( item, offset ); + litem = node->child[0]; + lnode = ADDRESS( litem, offset ); + lritem = lnode->child[1]; + lrnode = ADDRESS( lritem, offset ); + + plink = root; + if( node->parent ) + plink = &( (mmBTreeNode *)ADDRESS( node->parent, offset ) )->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ]; + + if( !( lnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrlitem = lrnode->child[0]; + lrlnode = ADDRESS( lrlitem, offset ); + lrritem = lrnode->child[1]; + lrrnode = ADDRESS( lrritem, offset ); + + if( !( lrlitem ) ) + { + if( lrritem ) + { + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = lrritem; + *plink = litem; + + lrrnode->flags = MM_BTREE_FLAGS_RIGHT; + lrrnode->parent = litem; + lrrnode->child[0] = lritem; + lrrnode->child[1] = item; + + lrnode->flags = MM_BTREE_FLAGS_STEP; + lrnode->parent = lrritem; + lrnode->child[1] = 0; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lrritem; + node->child[0] = 0; + } + else + goto lshift; + } + else if( !( lrlnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrnode->flags = node->flags; + lrnode->parent = node->parent; + lrnode->child[0] = litem; + lrnode->child[1] = item; + *plink = lritem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lritem; + node->child[0] = lrritem; + if( lrritem ) + { + lrrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + lrrnode->parent = item; + } + + lnode->flags = 0; + lnode->parent = lritem; + lnode->child[1] = lrlitem; + lrlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + lrlnode->parent = litem; + } + else if( lrrnode->flags & MM_BTREE_FLAGS_STEP ) + { + lshift: + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = item; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = litem; + node->child[0] = lritem; + lrnode->flags = 0; + lrnode->parent = item; + } + else + { + lnode->flags = node->flags; + lnode->parent = node->parent; + lnode->child[1] = lrritem; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT; + node->parent = lrritem; + node->child[0] = lrrnode->child[1]; + if( node->child[0] ) + { + tempnode = ADDRESS( node->child[0], offset ); + tempnode->flags = MM_BTREE_FLAGS_STEP; + tempnode->parent = item; + } + + lrnode->flags = 0; + lrnode->parent = lrritem; + lrnode->child[1] = lrrnode->child[0]; + if( lrnode->child[1] ) + { + tempnode = ADDRESS( lrnode->child[1], offset ); + tempnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + tempnode->parent = lritem; + } + + lrrnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + lrrnode->parent = litem; + lrrnode->child[0] = lritem; + lrrnode->child[1] = item; + } + + return; + } + + mask = node->flags & MM_BTREE_FLAGS_STEP; + llitem = lnode->child[0]; + llnode = ADDRESS( llitem, offset ); + + if( ( lritem ) && !( lrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lrlitem = lrnode->child[0]; + lrritem = lrnode->child[1]; + + lrnode->flags = node->flags; + lrnode->parent = node->parent; + lrnode->child[0] = litem; + lrnode->child[1] = item; + *plink = lritem; + + node->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + node->parent = lritem; + node->child[0] = lrritem; + if( lrritem ) + { + lrrnode = ADDRESS( lrritem, offset ); + lrrnode->parent = item; + lrrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + } + + lnode->flags = MM_BTREE_FLAGS_STEP; + lnode->parent = lritem; + lnode->child[1] = lrlitem; + if( lrlitem ) + { + lrlnode = ADDRESS( lrlitem, offset ); + lrlnode->parent = litem; + lrlnode->flags |= MM_BTREE_FLAGS_RIGHT; + } + } + else if( ( llitem ) && !( llnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + lnode->flags = node->flags | MM_BTREE_FLAGS_STEP; + lnode->parent = node->parent; + lnode->child[1] = item; + *plink = litem; + + node->flags = MM_BTREE_FLAGS_RIGHT | mask; + node->parent = litem; + node->child[0] = lritem; + if( lritem ) + { + lrnode->parent = item; + lrnode->flags = MM_BTREE_FLAGS_STEP; + } + + llnode->flags = mask; + } + else if( !( mask ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + lnode->flags = 0; + } + else + { + lnode->flags = 0; + if( node->parent ) + ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( node->parent, offset, root ); + } + + return; +} + + +/** + * Private function to balance a right branch of the tree after a removal. + * + * The details of the implementation are left as an exercise to the reader. + */ +static void mmBTreeRemoveBalanceRight( void *item, intptr_t offset, void **root ) +{ + int mask; + void **plink; + void *ritem, *rritem, *rlitem, *rllitem, *rlritem; + mmBTreeNode *node, *rnode, *rrnode, *rlnode, *rllnode, *rlrnode, *tempnode; + + node = ADDRESS( item, offset ); + ritem = node->child[1]; + rnode = ADDRESS( ritem, offset ); + rlitem = rnode->child[0]; + rlnode = ADDRESS( rlitem, offset ); + + plink = root; + if( node->parent ) + plink = &( (mmBTreeNode *)ADDRESS( node->parent, offset ) )->child[ node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ]; + + if( !( rnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlritem = rlnode->child[1]; + rlrnode = ADDRESS( rlritem, offset ); + rllitem = rlnode->child[0]; + rllnode = ADDRESS( rllitem, offset ); + + if( !( rlritem ) ) + { + if( rllitem ) + { + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = rllitem; + *plink = ritem; + + rllnode->flags = 0; + rllnode->parent = ritem; + rllnode->child[1] = rlitem; + rllnode->child[0] = item; + + rlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + rlnode->parent = rllitem; + rlnode->child[0] = 0; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rllitem; + node->child[1] = 0; + } + else + goto rshift; + } + else if( !( rlrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlnode->flags = node->flags; + rlnode->parent = node->parent; + rlnode->child[1] = ritem; + rlnode->child[0] = item; + *plink = rlitem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rlitem; + node->child[1] = rllitem; + if( rllitem ) + { + rllnode->flags |= MM_BTREE_FLAGS_RIGHT; + rllnode->parent = item; + } + + rnode->flags = MM_BTREE_FLAGS_RIGHT; + rnode->parent = rlitem; + rnode->child[0] = rlritem; + rlrnode->flags = MM_BTREE_FLAGS_STEP; + rlrnode->parent = ritem; + } + else if( rllnode->flags & MM_BTREE_FLAGS_STEP ) + { + rshift: + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = item; + *plink = ritem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = ritem; + node->child[1] = rlitem; + rlnode->flags = MM_BTREE_FLAGS_RIGHT; + rlnode->parent = item; + } + else + { + rnode->flags = node->flags; + rnode->parent = node->parent; + rnode->child[0] = rllitem; + *plink = ritem; + + node->flags = 0; + node->parent = rllitem; + node->child[1] = rllnode->child[0]; + if( node->child[1] ) + { + tempnode = ADDRESS( node->child[1], offset ); + tempnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + tempnode->parent = item; + } + + rlnode->flags = MM_BTREE_FLAGS_RIGHT; + rlnode->parent = rllitem; + rlnode->child[0] = rllnode->child[1]; + if( rlnode->child[0] ) + { + tempnode = ADDRESS( rlnode->child[0], offset ); + tempnode->flags = MM_BTREE_FLAGS_STEP; + tempnode->parent = rlitem; + } + + rllnode->flags = MM_BTREE_FLAGS_STEP; + rllnode->parent = ritem; + rllnode->child[1] = rlitem; + rllnode->child[0] = item; + } + + return; + } + + mask = node->flags & MM_BTREE_FLAGS_STEP; + rritem = rnode->child[1]; + rrnode = ADDRESS( rritem, offset ); + + if( ( rlitem ) && !( rlnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rlritem = rlnode->child[1]; + rllitem = rlnode->child[0]; + + rlnode->flags = node->flags; + rlnode->parent = node->parent; + rlnode->child[1] = ritem; + rlnode->child[0] = item; + *plink = rlitem; + + node->flags = MM_BTREE_FLAGS_STEP; + node->parent = rlitem; + node->child[1] = rllitem; + if( rllitem ) + { + rllnode = ADDRESS( rllitem, offset ); + rllnode->parent = item; + rllnode->flags |= MM_BTREE_FLAGS_RIGHT; + } + + rnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + rnode->parent = rlitem; + rnode->child[0] = rlritem; + if( rlritem ) + { + rlrnode = ADDRESS( rlritem, offset ); + rlrnode->parent = ritem; + rlrnode->flags &= ~MM_BTREE_FLAGS_RIGHT; + } + } + else if( ( rritem ) && !( rrnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + rnode->flags = node->flags | MM_BTREE_FLAGS_STEP; + rnode->parent = node->parent; + rnode->child[0] = item; + *plink = ritem; + + node->flags = mask; + node->parent = ritem; + node->child[1] = rlitem; + if( rlitem ) + { + rlnode->parent = item; + rlnode->flags = MM_BTREE_FLAGS_RIGHT | MM_BTREE_FLAGS_STEP; + } + + rrnode->flags = MM_BTREE_FLAGS_RIGHT | mask; + } + else if( !( mask ) ) + { + node->flags |= MM_BTREE_FLAGS_STEP; + rnode->flags = MM_BTREE_FLAGS_RIGHT; + } + else + { + rnode->flags = MM_BTREE_FLAGS_RIGHT; + if( node->parent ) + ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( node->parent, offset, root ); + } + + return; +} + + +/** + * Remove an item within the balanced tree + * + * Remove the item specified from the balanced tree. The offset parameter + * should be the offset of the mmBTreeNode structure within the structure + * of the item. The root parameter is a pointer to the root of the tree. + */ +void mmBTreeRemove( void *item, intptr_t offset, void **root ) +{ + void *target, *parent, *child; + mmBTreeNode *node, *tnode, *pnode, *cnode; + + node = ADDRESS( item, offset ); + if( !( node->child[0] ) || !( node->child[1] ) ) + target = item; + else + target = mmBTreeRemoveSeek( item, offset ); + + tnode = ADDRESS( target, offset ); + child = tnode->child[0]; + if( tnode->child[1] ) + child = tnode->child[1]; + cnode = ADDRESS( child, offset ); + + parent = tnode->parent; + pnode = ADDRESS( parent, offset ); + + if( !( tnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + if( child ) + { + cnode->parent = parent; + cnode->flags = ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + } + if( parent ) + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + else + *root = child; + } + else if( ( child ) && !( cnode->flags & MM_BTREE_FLAGS_STEP ) ) + { + cnode->parent = parent; + cnode->flags = ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) | MM_BTREE_FLAGS_STEP; + if( parent ) + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + else + *root = child; + } + else + { + if( child ) + { + cnode->parent = parent; + cnode->flags = tnode->flags; + } + if( parent ) + { + pnode->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = child; + ( tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ? mmBTreeRemoveBalanceLeft : mmBTreeRemoveBalanceRight )( parent, offset, root ); + } + else + *root = child; + } + + if( item != target ) + { + memcpy( tnode, node, sizeof(mmBTreeNode) ); + if( tnode->parent ) + ( (mmBTreeNode *)ADDRESS( tnode->parent, offset ) )->child[ tnode->flags & MM_BTREE_FLAGS_DIRECTION_MASK ] = target; + else + *root = target; + if( tnode->child[0] ) + ( (mmBTreeNode *)ADDRESS( tnode->child[0], offset ) )->parent = target; + if( tnode->child[1] ) + ( (mmBTreeNode *)ADDRESS( tnode->child[1], offset ) )->parent = target; + } + + return; +} + + +void *mmBtreeMostLeft( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + while( node->child[MM_BTREE_FLAGS_LEFT] ) + { + root = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( root, offset ); + } + return root; +} + + +void *mmBtreeMostRight( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + while( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + root = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( root, offset ); + } + return root; +} + + +void *mmBtreeNeighbourLeft( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + node = ADDRESS( item, offset ); + if( node->child[MM_BTREE_FLAGS_LEFT] ) + { + item = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( item, offset ); + while( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + item = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( item, offset ); + } + return item; + } + while( node->parent ) + { + node = ADDRESS( item, offset ); + item = node->parent; + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_RIGHT ) + return item; + } + return 0; +} + + +void *mmBtreeNeighbourRight( void *item, intptr_t offset ) +{ + mmBTreeNode *node; + node = ADDRESS( item, offset ); + if( node->child[MM_BTREE_FLAGS_RIGHT] ) + { + item = node->child[MM_BTREE_FLAGS_RIGHT]; + node = ADDRESS( item, offset ); + while( node->child[MM_BTREE_FLAGS_LEFT] ) + { + item = node->child[MM_BTREE_FLAGS_LEFT]; + node = ADDRESS( item, offset ); + } + return item; + } + while( node->parent ) + { + node = ADDRESS( item, offset ); + item = node->parent; + if( ( node->flags & MM_BTREE_FLAGS_DIRECTION_MASK ) == MM_BTREE_FLAGS_LEFT ) + return item; + } + return 0; +} + + +int mmBtreeListOrdered( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ) +{ + mmBTreeNode *node; + node = ADDRESS( root, offset ); + if( !( root ) ) + return 1; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_LEFT], offset, callback, v ) ) ) + return 0; + if( !( callback( root, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_RIGHT], offset, callback, v ) ) ) + return 0; + return 1; +} + + +int mmBtreeListBalanced( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ) +{ + mmBTreeNode *node; + node = ADDRESS( root, offset ); + if( !( root ) ) + return 1; + if( !( callback( root, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_LEFT], offset, callback, v ) ) ) + return 0; + if( !( mmBtreeListOrdered( node->child[MM_BTREE_FLAGS_RIGHT], offset, callback, v ) ) ) + return 0; + return 1; +} + + +intptr_t mmBtreeItemCount( void *root, intptr_t offset ) +{ + mmBTreeNode *node; + if( !( root ) ) + return 0; + node = ADDRESS( root, offset ); + return 1 + mmBtreeItemCount( node->child[0], offset ) + mmBtreeItemCount( node->child[1], offset ); +} + + + +//// + + + +/** + * Initialize the memory block head specified. + * + * The parameters chunksize and chunkperblock define the size of each memory + * chunk and the number of chunks per block. + */ +void MM_FUNC(BlockInit)( mmBlockHead *head, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ) +{ + memset( head, 0, sizeof(mmBlockHead) ); + head->alignment = 0; + + head->chunksize = chunksize; + if( head->chunksize < sizeof(mmListNode) ) + head->chunksize = sizeof(mmListNode); + if( alignment >= 0x10 ) + { + head->alignment = alignment - 1; + head->chunksize = ( chunksize + head->alignment ) & ~head->alignment; + } + head->chunkfreecount = 0; + head->relayalloc = mmAlloc; + head->relayfree = mmFree; + head->relayvalue = 0; + head->chunkperblock = chunkperblock; + head->allocsize = sizeof(mmBlock) + head->chunksize * head->chunkperblock; + head->keepfreecount = keepfreecount + chunkperblock; + mtSpinInit( &head->spinlock ); + return; +} + + +void MM_FUNC(BlockNodeInit)( mmBlockHead *head, int nodeindex, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ) +{ + MM_FUNC(BlockInit)( head, chunksize, chunkperblock, keepfreecount, alignment MM_PASSPARAMS ); + head->relayalloc = mmNodeRelayAlloc; + head->relayfree = mmNodeRelayFree; + head->relayvalue = (void *)((intptr_t)nodeindex); + return; +} + + +static void mmBlockTreeInsert( mmBlock *block, void **treeroot ) +{ + mmBlock *root = *treeroot; + if( !( root ) ) + { + mmBTreeInsert( block, 0, 0, offsetof(mmBlock,node), treeroot ); + return; + } + for( ; ; ) + { + if( block < root ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( block, root, MM_BTREE_FLAGS_LEFT, offsetof(mmBlock,node), treeroot ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( block, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmBlock,node), treeroot ); + break; + } + } + return; +} + + +static mmBlock *mmBlockResolveChunk( void *p, mmBlock *root ) +{ + mmBlock *best = 0; + for( ; root ; ) + { + if( p < (void *)root ) + root = root->node.child[0]; + else + { + best = root; + root = root->node.child[1]; + } + } + return best; +} + + +/** + * Allocates a chunk of memory from the block. + * + * The size of the memory chunk returned was defined during the initialisation + * of the specified memory block head. + */ +void *MM_FUNC(BlockAlloc)( mmBlockHead *head MM_PARAMS ) +{ + int a; + mmBlock *block; + void *chunk; + mtSpinLock( &head->spinlock ); + if( !( head->freelist ) ) + { + if( head->alignment ) + block = MM_FUNC(AlignRelayAlloc)( head->relayalloc, head->relayvalue, head->allocsize, head->alignment, sizeof(mmBlock) MM_PASSPARAMS ); + else + block = head->relayalloc( head->relayvalue, head->allocsize MM_PASSPARAMS ); + if( !( block ) ) + { + fprintf( stderr, "ERROR %s:%d\n", __FILE__, __LINE__ ); + return 0; + } + block->freecount = head->chunkperblock; + mmListAdd( &head->blocklist, block, offsetof(mmBlock,listnode) ); + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( a = 0 ; a < head->chunkperblock ; a++, chunk = ADDRESS( chunk, head->chunksize ) ) + mmListAdd( &head->freelist, chunk, 0 ); + mmBlockTreeInsert( block, &head->treeroot ); + head->chunkfreecount += head->chunkperblock; + } + chunk = head->freelist; + block = mmBlockResolveChunk( chunk, head->treeroot ); + mmListRemove( chunk, 0 ); + block->freecount--; + head->chunkfreecount--; + mtSpinUnlock( &head->spinlock ); + return chunk; +} + + +/** + * Free a chunk of memory previously allocated by mmBlockAlloc(). + */ +void MM_FUNC(BlockFree)( mmBlockHead *head, void *v MM_PARAMS ) +{ + int a; + mmBlock *block; + void *chunk; + chunk = v; + mtSpinLock( &head->spinlock ); + block = mmBlockResolveChunk( chunk, head->treeroot ); + block->freecount++; + head->chunkfreecount++; + mmListAdd( &head->freelist, chunk, 0 ); + if( ( block->freecount == head->chunkperblock ) && ( head->chunkfreecount >= head->keepfreecount ) ) + { + mmListRemove( block, offsetof(mmBlock,listnode) ); + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( a = 0 ; a < head->chunkperblock ; a++, chunk = ADDRESS( chunk, head->chunksize ) ) + mmListRemove( chunk, 0 ); + mmBTreeRemove( block, offsetof(mmBlock,node), &head->treeroot ); + if( head->alignment ) + MM_FUNC(AlignRelayFree)( head->relayfree, head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + else + head->relayfree( head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + head->chunkfreecount -= head->chunkperblock; + } + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Release a chunk of memory previously allocated by mmBlockAlloc(). + * + * Unlike mmBlockFree(), this function will collect but never free unused + * memory, improving performance if the memory pages are to be soon reused. + */ +void MM_FUNC(BlockRelease)( mmBlockHead *head, void *v MM_PARAMS ) +{ + mmBlock *block; + void *chunk; + chunk = v; + mtSpinLock( &head->spinlock ); + block = mmBlockResolveChunk( chunk, head->treeroot ); + block->freecount++; + head->chunkfreecount++; + mmListAdd( &head->freelist, chunk, 0 ); + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free all memory allocated by a block head. + */ +void MM_FUNC(BlockFreeAll)( mmBlockHead *head MM_PARAMS ) +{ + mmBlock *block, *blocknext; + mtSpinLock( &head->spinlock ); + for( block = head->blocklist ; block ; block = blocknext ) + { + blocknext = block->listnode.next; + if( head->alignment ) + MM_FUNC(AlignRelayFree)( head->relayfree, head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + else + head->relayfree( head->relayvalue, block, head->allocsize MM_PASSPARAMS ); + } + head->blocklist = 0; + head->freelist = 0; + head->treeroot = 0; + mtSpinUnlock( &head->spinlock ); + mtSpinDestroy( &head->spinlock ); + return; +} + + +void MM_FUNC(BlockProcessList)( mmBlockHead *head, void *userpointer, int (*processchunk)( void *chunk, void *userpointer ) MM_PARAMS ) +{ + int i, blockcount, blockrefsize, chunkperblock; + intptr_t **bitsref; + intptr_t *blockmask; + intptr_t blockindex, chunkindex; + size_t chunksize; + void *p, *chunk; + mmBlock *block; + mmListNode *list; + + mtSpinLock( &head->spinlock ); + + blockcount = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + block->blockindex = blockcount++; + + chunksize = head->chunksize; + chunkperblock = head->chunkperblock; + blockrefsize = ( ( chunkperblock + CPUCONF_INTPTR_BITS - 1 ) >> CPUCONF_INTPTR_BITSHIFT ) * sizeof(intptr_t); + bitsref = malloc( blockcount * ( sizeof(intptr_t *) + blockrefsize ) ); + memset( bitsref, 0, blockcount * ( sizeof(intptr_t *) + blockrefsize ) ); + + p = ADDRESS( bitsref, blockcount * sizeof(intptr_t *) ); + for( i = 0 ; i < blockcount ; i++ ) + { + bitsref[i] = p; + p = ADDRESS( p, blockrefsize ); + } + for( list = head->freelist ; list ; list = list->next ) + { + block = mmBlockResolveChunk( list, head->treeroot ); + chunkindex = ADDRESSDIFF( list, ADDRESS( block, sizeof(mmBlock) ) ) / chunksize; + bitsref[ block->blockindex ][ chunkindex >> CPUCONF_INTPTR_BITSHIFT ] |= (intptr_t)1 << ( chunkindex & (CPUCONF_INTPTR_BITS-1) ); + } + + blockindex = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + { + blockmask = bitsref[ blockindex ]; + chunk = ADDRESS( block, sizeof(mmBlock) ); + for( chunkindex = 0 ; chunkindex < chunkperblock ; chunkindex++, chunk = ADDRESS( chunk, chunksize ) ) + { + if( blockmask[ chunkindex >> CPUCONF_INTPTR_BITSHIFT ] & ( (intptr_t)1 << ( chunkindex & (CPUCONF_INTPTR_BITS-1) ) ) ) + continue; + if( processchunk( chunk, userpointer ) ) + goto end; + } + blockindex++; + } + + end: + free( bitsref ); + mtSpinUnlock( &head->spinlock ); + + return; +} + + +int MM_FUNC(BlockUseCount)( mmBlockHead *head MM_PARAMS ) +{ + int blockcount, chunkcount; + mmBlock *block; + mmListNode *list; + + mtSpinLock( &head->spinlock ); + + blockcount = 0; + for( block = head->blocklist ; block ; block = block->listnode.next ) + blockcount++; + chunkcount = blockcount * head->chunkperblock; + for( list = head->freelist ; list ; list = list->next ) + chunkcount--; + + mtSpinUnlock( &head->spinlock ); + + return chunkcount; +} + + +int MM_FUNC(BlockFreeCount)( mmBlockHead *head MM_PARAMS ) +{ + int chunkcount; + + mtSpinLock( &head->spinlock ); + chunkcount = head->chunkfreecount; + mtSpinUnlock( &head->spinlock ); + + return chunkcount; +} + + + +//// + + + +typedef struct +{ + intptr_t index; + mmBTreeNode node; +} mmIndex; + +void mmIndexInit( mmIndexHead *head, int indexesperblock ) +{ + mmBlockInit( &head->indexblock, sizeof(mmIndex), indexesperblock, indexesperblock, 0x10 ); + head->indextree = 0; + mtSpinInit( &head->spinlock ); + return; +} + +void mmIndexFreeAll( mmIndexHead *head ) +{ + mmBlockFreeAll( &head->indexblock ); + head->indextree = 0; + mtSpinDestroy( &head->spinlock ); + return; +} + +void mmIndexAdd( mmIndexHead *head, intptr_t index ) +{ + mmIndex *indexnode, *root; + mtSpinLock( &head->spinlock ); + indexnode = mmBlockAlloc( &head->indexblock ); + indexnode->index = index; + if( !( head->indextree ) ) + { + mmBTreeInsert( indexnode, 0, 0, offsetof(mmIndex,node), &head->indextree ); + mtSpinUnlock( &head->spinlock ); + return; + } + for( root = head->indextree ; ; ) + { + if( index < root->index ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( indexnode, root, MM_BTREE_FLAGS_LEFT, offsetof(mmIndex,node), &head->indextree ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( indexnode, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmIndex,node), &head->indextree ); + break; + } + } + mtSpinUnlock( &head->spinlock ); + return; +} + +intptr_t mmIndexGet( mmIndexHead *head ) +{ + intptr_t index; + mmIndex *indexnode; + mtSpinLock( &head->spinlock ); + if( !( head->indextree ) ) + index = head->indexlimit++; + else + { + for( indexnode = head->indextree ; indexnode->node.child[0] ; ) + indexnode = indexnode->node.child[0]; + index = indexnode->index; + mmBTreeRemove( indexnode, offsetof(mmIndex,node), &head->indextree ); + mmBlockFree( &head->indexblock, indexnode ); + } + mtSpinUnlock( &head->spinlock ); + return index; +} + +int mmIndexRemove( mmIndexHead *head, intptr_t index ) +{ + intptr_t limit; + mmIndex *indexnode; + mtSpinLock( &head->spinlock ); + if( index >= head->indexlimit ) + { + for( limit = head->indexlimit ; limit < index ; limit++ ) + mmIndexAdd( head, index ); + head->indexlimit = index + 1; + mtSpinUnlock( &head->spinlock ); + return 1; + } + for( indexnode = head->indextree ; ; ) + { + if( !( indexnode ) ) + { + mtSpinUnlock( &head->spinlock ); + return 0; + } + if( index == indexnode->index ) + break; + if( index < indexnode->index ) + indexnode = indexnode->node.child[0]; + else + indexnode = indexnode->node.child[1]; + } + mmBTreeRemove( indexnode, offsetof(mmIndex,node), &head->indextree ); + mmBlockFree( &head->indexblock, indexnode ); + mtSpinUnlock( &head->spinlock ); + return 1; +} + +size_t mmIndexCount( mmIndexHead *head ) +{ + intptr_t count; + mtSpinLock( &head->spinlock ); + count = mmBtreeItemCount( head->indextree, offsetof(mmIndex,node) ); + mtSpinUnlock( &head->spinlock ); + return count; +} + + + +//// + + + +#define MM_BITTABLE_SIZEBYTES(head) ((((head->mapsize)<bitshift))>>CPUCONF_CHAR_BITSHIFT) +#define MM_BITTABLE_MINALIGN (4096) + +void mmBitTableInit( mmBitTableHead *head, int bitsperentry, int chunksize, int initmask ) +{ + int i; + head->bitshift = ccLog2Int32( ccAlignInt32( bitsperentry ) ); + head->bitmask = ( 1 << bitsperentry ) - 1; + head->countalign = ccAlignIntPtr( chunksize ); + if( head->countalign < MM_BITTABLE_MINALIGN ) + head->countalign = MM_BITTABLE_MINALIGN; + head->indexshift = CPUCONF_INTPTR_BITSHIFT >> head->bitshift; + head->indexmask = ( 1 << head->indexshift ) - 1; + head->mapsize = 0; + head->map = 0; + initmask &= ( 1 << head->bitmask ) - 1; + head->initmask = 0; + for( i = 0 ; i < CPUCONF_INTPTR_BITS ; i += head->bitmask ) + head->initmask |= initmask << i; + mtSpinInit( &head->spinlock ); + return; +} + +void mmBitTableFreeAll( mmBitTableHead *head ) +{ + free( head->map ); + head->map = 0; + head->mapsize = 0; + mtSpinDestroy( &head->spinlock ); + return; +} + +void mmBitTableSet( mmBitTableHead *head, uintptr_t index, int flags, int editmask ) +{ + uintptr_t *map; + uintptr_t offset, shift, mapindex, mapindexend; + mtSpinLock( &head->spinlock ); + map = head->map; + if( index >= head->mapsize ) + { + mapindex = head->mapsize >> CPUCONF_INTPTR_BITSHIFT; + head->mapsize = ( index + head->countalign ) & ( head->countalign - 1 ); + head->map = realloc( head->map, MM_BITTABLE_SIZEBYTES( head ) ); + mapindexend = head->mapsize >> CPUCONF_INTPTR_BITSHIFT; + map = head->map; + for( ; mapindex < mapindexend ; mapindex++ ) + map[mapindex] = head->initmask; + } + offset = index >> head->indexshift; + shift = ( index & head->indexmask ) << head->bitshift; + map[ offset ] &= ~( editmask << shift ); + map[ offset ] |= ( ( flags & editmask ) << shift ); + mtSpinUnlock( &head->spinlock ); + return; +} + +uintptr_t mmBitTableGet( mmBitTableHead *head, uintptr_t index ) +{ + uintptr_t *map; + uintptr_t offset, shift, value; + mtSpinLock( &head->spinlock ); + if( index >= head->mapsize ) + { + mtSpinUnlock( &head->spinlock ); + return head->initmask; + } + map = head->map; + offset = index >> head->indexshift; + shift = ( index & head->indexmask ) << head->bitshift; + value = ( map[ offset ] >> shift ) & head->bitmask; + mtSpinUnlock( &head->spinlock ); + return value; +} + + + +//// + + + +#define MM_GROW_NODE_MEM(x,b) ADDRESS(x,sizeof(mmGrowNode)+b) + +/** + * Private function to add a new node to the packed growing memory allocator + */ +static int mmGrowAdd( mmGrow *mgrow, int size MM_PARAMS ) +{ + mmGrowNode *mnode; + if( !( mnode = mmAlloc( 0, sizeof(mmGrowNode) + size MM_PASSPARAMS ) ) ) + return 0; + mnode->size = size; + mnode->used = 0; + mnode->next = mgrow->first; + mgrow->first = mnode; + return 1; +} + + +/** + * Initialize a packed growing memory allocator. + * + * While no space is wasted between allocated chunks of memory, it is + * impossible to free individual allocations. A growing memory allocator + * must be freed entirely at once. + */ +int MM_FUNC(GrowInit)( mmGrow *mgrow, size_t nodesize MM_PARAMS ) +{ + mgrow->first = 0; + mgrow->nodesize = nodesize; + mtSpinInit( &mgrow->spinlock ); + return mmGrowAdd( mgrow, nodesize MM_PASSPARAMS ); +} + + +/** + * Free all chunks of a growing memory allocator. + */ +void MM_FUNC(GrowFreeAll)( mmGrow *mgrow MM_PARAMS ) +{ + mmGrowNode *mnode, *next; + mtSpinLock( &mgrow->spinlock ); + for( mnode = mgrow->first ; mnode ; mnode = next ) + { + next = mnode->next; + mmFree( 0, mnode, 0 MM_PASSPARAMS ); + } + mgrow->first = 0; + mtSpinUnlock( &mgrow->spinlock ); + mtSpinDestroy( &mgrow->spinlock ); + return; +} + + +/** + * Allocate a chunk of memory from a growing memory allocator. + * + * The memory can only be freed with mmGrowFreeAll() for the whole allocator. + * There are no memory alignment garantees for the allocations, yet as all + * memory is tightly packed, the alignment is directly dependant on the size + * of the previous allocations. + */ +void *MM_FUNC(GrowAlloc)( mmGrow *mgrow, size_t bytes MM_PARAMS ) +{ + size_t a, fbytes; + mmGrowNode *mnode; + mtSpinLock( &mgrow->spinlock ); + mnode = mgrow->first; + fbytes = mnode->size - mnode->used; + if( fbytes >= bytes ) + { + a = mnode->used; + mnode->used += bytes; + mtSpinUnlock( &mgrow->spinlock ); + return MM_GROW_NODE_MEM( mnode, a ); + } + mmGrowAdd( mgrow, ( bytes > mgrow->nodesize ) ? bytes : mgrow->nodesize MM_PASSPARAMS ); + mnode = mgrow->first; + mnode->used = bytes; + mtSpinUnlock( &mgrow->spinlock ); + return MM_GROW_NODE_MEM( mnode, 0 ); +} + + +/* Rewind last memory allocation by a count of bytes */ +void MM_FUNC(GrowRewindLast)( mmGrow *mgrow, size_t rewind MM_PARAMS ) +{ + mmGrowNode *mnode; + mtSpinLock( &mgrow->spinlock ); + mnode = mgrow->first; + mnode->used -= rewind; + mtSpinUnlock( &mgrow->spinlock ); + return; +} + + + +//// + + + +#if 0 + +/** + * Initialize a directory structure. + * + * The pageshift parameter indicates the power of two defining the count of + * entries per page. The pagecount parameter specifies the initial count of + * pages, which can grow as required. + */ +int MM_FUNC(DirInit)( mmDirectory *dir, intptr_t pageshift, intptr_t pagecount MM_PARAMS ) +{ + if( !( dir->table = mmAlloc( 0, pagecount * sizeof(void **) MM_PASSPARAMS ) ) ) + return 0; + memset( dir->table, 0, pagecount * sizeof(void **) ); + dir->pagecount = pagecount; + dir->pageshift = pageshift; + dir->pagesize = 1 << pageshift; + dir->pagemask = dir->pagesize - 1; + mtSpinInit( &dir->spinlock ); + return 1; +} + + +/** + * Private function to resize a directory page table. + */ +static inline void mmDirResizeTable( mmDirectory *dir, intptr_t newcount MM_PARAMS ) +{ + intptr_t page; + for( page = newcount ; page < dir->pagecount ; page++ ) + { + if( dir->table[ page ] ) + mmFree( 0, dir->table[ page ], 0 MM_PASSPARAMS ); + } + if( !( dir->table = mmRealloc( 0, dir->table, newcount * sizeof(void **) MM_PASSPARAMS ) ) ) + return; + if( newcount > dir->pagecount ) + memset( ADDRESS( dir->table, dir->pagecount * sizeof(void **) ), 0, ( newcount - dir->pagecount ) * sizeof(void **) ); + dir->pagecount = newcount; + return; +} + + +void MM_FUNC(DirSize)( mmDirectory *dir, intptr_t size MM_PARAMS ) +{ + intptr_t page, pagecount; + pagecount = ( size >> dir->pageshift ) + 1; + size &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( pagecount != dir->pagecount ) + mmDirResizeTable( dir, pagecount MM_PASSPARAMS ); + for( page = 0 ; page < pagecount ; page++ ) + { + if( dir->table[ page ] ) + continue; + if( !( dir->table[ page ] = mmAlloc( 0, dir->pagesize * sizeof(void *) MM_PASSPARAMS ) ) ) + return; + } + mtSpinUnlock( &dir->spinlock ); + return; +} + + +/** + * Sets the directory's item specified by the index to the pointer of the entry + * parameter. + * + * If the directory is too small to accept the new entry, the internal table of + * pages grow in size. The MM_DIR_ENTRY(dir,index) macro can be used to access + * directory entries, both for reading and writing, only if the entry is known + * to have been previously set. + */ +void MM_FUNC(DirSet)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ) +{ + intptr_t page; + page = index >> dir->pageshift; + index &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( page >= dir->pagecount ) + mmDirResizeTable( dir, ( dir->pagecount ? dir->pagecount << 1 : 1 ) MM_PASSPARAMS ); + if( !( dir->table[ page ] ) ) + { + if( !( dir->table[ page ] = mmAlloc( 0, dir->pagesize * sizeof(void *) MM_PASSPARAMS ) ) ) + return; + } + dir->table[ page ][ index ] = entry; + mtSpinUnlock( &dir->spinlock ); + return; +} + + +void *MM_FUNC(DirGet)( mmDirectory *dir, intptr_t index MM_PARAMS ) +{ + intptr_t page; + void *entry; + page = index >> dir->pageshift; + index &= dir->pagemask; + mtSpinLock( &dir->spinlock ); + if( (uintptr_t)page >= dir->pagecount ) + return 0; + if( !( dir->table[ page ] ) ) + return 0; + entry = dir->table[ page ][ index ]; + mtSpinUnlock( &dir->spinlock ); + return entry; +} + + +void MM_FUNC(DirSetFast)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ) +{ + mtSpinLock( &dir->spinlock ); + dir->table[ index >> dir->pageshift ][ index & dir->pagemask ] = entry; + mtSpinUnlock( &dir->spinlock ); + return; +} + +void *MM_FUNC(DirGetFast)( mmDirectory *dir, intptr_t index MM_PARAMS ) +{ + void *entry; + mtSpinLock( &dir->spinlock ); + entry = dir->table[ index >> dir->pageshift ][ index & dir->pagemask ]; + mtSpinUnlock( &dir->spinlock ); + return entry; +} + + +/** + * Free a directory structure. + */ +void MM_FUNC(DirFree)( mmDirectory *dir MM_PARAMS ) +{ + intptr_t page; + if( !( dir->table ) ) + return; + mtSpinLock( &dir->spinlock ); + for( page = dir->pagecount-1 ; page >= 0 ; page-- ) + { + if( dir->table[ page ] ) + mmFree( 0, dir->table[ page ], 0 MM_PASSPARAMS ); + } + dir->pagecount = 0; + mmFree( 0, dir->table, 0 MM_PASSPARAMS ); + dir->table = 0; + mtSpinUnlock( &dir->spinlock ); + mtSpinDestroy( &dir->spinlock ); + return; +} + +#endif + + + +//// + + + +typedef struct +{ + int padding; +} mmAlign; + + +/** + * Allocate a chunk of memory with the alignment specified. + * + * The alignment parameter must be a power of two. The allocated memory must be + * freed with the mmAlignFree() function. + */ +void *MM_FUNC(AlignAlloc)( size_t bytes, intptr_t align MM_PARAMS ) +{ + intptr_t i; + void *v; + mmAlign *malign; + align--; + if( !( v = mmAlloc( 0, bytes + align + sizeof(mmAlign) MM_PASSPARAMS ) ) ) + return 0; + i = ( (intptr_t)v + align + sizeof(mmAlign) ) & ~align; + malign = ADDRESS( (void *)i, -(int)sizeof(mmAlign) ); + malign->padding = ADDRESSDIFF( i, v ); + return (void *)i; +} + +/** + * Free a chunk of memory that was allocated by mmAlignAlloc(). + */ +void MM_FUNC(AlignFree)( void *v MM_PARAMS ) +{ + mmAlign *malign; + malign = ADDRESS( v, -(int)sizeof(mmAlign) ); + mmFree( 0, ADDRESS( v, -malign->padding ), 0 MM_PASSPARAMS ); + return; +} + +void *MM_FUNC(AlignGrow)( void *v, size_t bytes, size_t copybytes, intptr_t align MM_PARAMS ) +{ + void *newv; + newv = MM_FUNC(AlignAlloc)( bytes, align ); + memcpy( newv, v, copybytes ); + MM_FUNC(AlignFree)( v ); + return newv; +} + + +void *MM_FUNC(AlignRelayAlloc)( void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ), void *relayvalue, size_t bytes, intptr_t align, size_t displacement MM_PARAMS ) +{ + intptr_t i; + void *v; + mmAlign *malign; + align--; + if( !( v = relayalloc( relayvalue, bytes + align + sizeof(mmAlign) + displacement MM_PASSPARAMS ) ) ) + return 0; + i = ( (intptr_t)v + align + sizeof(mmAlign) + displacement ) & ~align; + i -= displacement; + malign = ADDRESS( (void *)i, -(int)sizeof(mmAlign) ); + malign->padding = ADDRESSDIFF( i, v ); + return (void *)i; +} + + +void MM_FUNC(AlignRelayFree)( void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ), void *relayvalue, void *v, size_t bytes MM_PARAMS ) +{ + mmAlign *malign; + malign = ADDRESS( v, -(int)sizeof(mmAlign) ); + relayfree( relayvalue, ADDRESS( v, -malign->padding ), bytes MM_PASSPARAMS ); + return; +} + + + +//// + + + +typedef struct +{ + size_t bigsize; + mmListNode list; + int alignment; +} mmVolume; + +typedef struct +{ + int32_t nextoffset; + int32_t prevoffset; +} mmVolumeChunkHeader; + +typedef struct +{ + mmVolumeChunkHeader h; + mmBTreeNode node; +} mmVolumeChunk; + +#define MM_VOLUME_CHUNK_FLAGS_FREE (0x1) +#define MM_VOLUME_CHUNK_FLAGS_LAST (0x2) +#define MM_VOLUME_CHUNK_FLAGS_MASK (0x3) + +#define MM_VOLUME_CHUNK_GET_FLAGS(c) ((c)->h.nextoffset&MM_VOLUME_CHUNK_FLAGS_MASK) +#define MM_VOLUME_CHUNK_SET_FLAGS(c,f) (c)->h.nextoffset=(((c)->h.nextoffset&~MM_VOLUME_CHUNK_FLAGS_MASK)|(f)) +#define MM_VOLUME_CHUNK_GET_NEXTOFFSET(c) ((c)->h.nextoffset&~MM_VOLUME_CHUNK_FLAGS_MASK) +#define MM_VOLUME_CHUNK_SET_NEXTOFFSET(c,n) (c)->h.nextoffset=((c)->h.nextoffset&MM_VOLUME_CHUNK_FLAGS_MASK)|(n) +#define MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS(c,n,f) (c)->h.nextoffset=((n)|(f)) + +#define MM_VOLUME_BIGCHUNK_THRESHOLD (1<<30) + + +/** + * Initialize a memory volume. + * + * The parameter volumesize defines the default size of each chunk of memory + * internally allocated by the volume manager, chunks which will be sliced + * accordingly to memory allocation needs. The parameter minchunksize defines + * the minimum size a chunk of memory must have not to be fused. + */ +void MM_FUNC(VolumeInit)( mmVolumeHead *head, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ) +{ + head->alignment = 3; + if( alignment > 4 ) + head->alignment = alignment - 1; + head->volumesize = volumesize; + if( head->volumesize > (((size_t)1)<<31)-1 ) + head->volumesize = (((size_t)1)<<31)-1; + head->volumeblocksize = ( sizeof(mmVolume) + head->alignment ) & ~head->alignment; + head->minchunksize = ( ( minchunksize > sizeof(mmVolumeChunk) ? minchunksize : sizeof(mmVolumeChunk) ) + head->alignment ) & ~head->alignment; + head->volumechunksize = ( sizeof(mmVolumeChunkHeader) + head->alignment ) & ~head->alignment; + head->keepfreesize = keepfreesize; + head->totalfreesize = 0; + head->freeroot = 0; + head->volumelist = 0; + head->relayalloc = mmAlloc; + head->relayfree = mmFree; + head->relayvalue = 0; + mtSpinInit( &head->spinlock ); + return; +} + + +void MM_FUNC(VolumeNodeInit)( mmVolumeHead *head, int nodeindex, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ) +{ + MM_FUNC(VolumeInit)( head, volumesize, minchunksize, keepfreesize, alignment MM_PASSPARAMS ); + head->relayalloc = mmNodeRelayAlloc; + head->relayfree = mmNodeRelayFree; + head->relayvalue = (void *)((intptr_t)nodeindex); + return; +} + + +/** + * Private function to search for the best match given a desired memory chunk size. + */ +static mmVolumeChunk *mmVolumeFindFreeChunk( int32_t bytes, mmVolumeChunk *root ) +{ + int32_t chunksize; + mmVolumeChunk *best = 0; + for( ; root ; ) + { + chunksize = MM_VOLUME_CHUNK_GET_NEXTOFFSET( root ); + if( bytes <= chunksize ) + { + best = root; + if( bytes == chunksize ) + return best; + root = root->node.child[0]; + } + else + root = root->node.child[1]; + } + return best; +} + + +/** + * Private function to add a new free chunk within the balanced binary tree. + */ +static void mmVolumeAddFreeChunk( mmVolumeChunk *chunk, void **rootfree ) +{ + int32_t nextoffset = chunk->h.nextoffset; + mmVolumeChunk *root = *rootfree; + if( !( root ) ) + { + mmBTreeInsert( chunk, 0, 0, offsetof(mmVolumeChunk,node), rootfree ); + return; + } + for( ; ; ) + { + if( nextoffset < root->h.nextoffset ) + { + if( root->node.child[0] ) + { + root = root->node.child[0]; + continue; + } + mmBTreeInsert( chunk, root, MM_BTREE_FLAGS_LEFT, offsetof(mmVolumeChunk,node), rootfree ); + break; + } + else + { + if( root->node.child[1] ) + { + root = root->node.child[1]; + continue; + } + mmBTreeInsert( chunk, root, MM_BTREE_FLAGS_RIGHT, offsetof(mmVolumeChunk,node), rootfree ); + break; + } + } + return; +} + + + +void mmVolumeDebugList( mmVolumeHead *volumehead ) +{ + int32_t prevoffset, nextoffset, chunkflags, volumesize, nextcheck; + mmVolume *volume; + mmVolumeChunk *chunk; + + printf( "\n==== BEGIN ====\n" ); + fflush( stdout ); + + for( volume = volumehead->volumelist ; volume ; volume = volume->list.next ) + { + chunk = ADDRESS( volume, volumehead->volumeblocksize ); + + volumesize = 0; + nextcheck = 0; + + for( ; ; ) + { + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + prevoffset = chunk->h.prevoffset; + + printf( "Chunk %p\n", chunk ); + printf( " Prevoffset : %d\n", prevoffset ); + printf( " Nextoffset : %d\n", nextoffset ); + printf( " Chunkflags : %d\n", chunkflags ); + fflush( stdout ); + + volumesize += nextoffset; + if( ( nextcheck ) && ( prevoffset != nextcheck ) ) + exit( 1 ); + nextcheck = nextoffset; + + if( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) + break; + + chunk = ADDRESS( chunk, nextoffset ); + } + + printf( "Volume size : %d\n", volumesize ); + printf( "\n" ); + } + + printf( "==== END ====\n\n" ); + fflush( stdout ); + + return; +} + + + + +/** + * Allocate memory from a volume. + * + * The function allocates a chunk of memory of arbitrary size from the + * specified volume manager. + */ +void *MM_FUNC(VolumeAlloc)( mmVolumeHead *head, size_t bytes MM_PARAMS ) +{ + int32_t chunkflags, nextoffset; + size_t allocsize, minsize, extrasize; + intptr_t vmem; + mmVolume *volume; + mmVolumeChunk *chunk, *newchunk, *nextchunk; + + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( bytes >= MM_VOLUME_BIGCHUNK_THRESHOLD ) + { + allocsize = bytes + ( head->volumeblocksize + head->volumechunksize ); + + vmem = (intptr_t)head->relayalloc( head->relayvalue, allocsize MM_PASSPARAMS ); + volume = (void *)( ( vmem + head->alignment ) & ~head->alignment ); + volume->bigsize = allocsize; + volume->alignment = (intptr_t)volume - vmem; + mmListAdd( &head->volumelist, volume, offsetof(mmVolume,list) ); + + chunk = ADDRESS( volume, head->volumeblocksize ); + chunk->h.prevoffset = 0; + chunk->h.nextoffset = 0; + + mtSpinUnlock( &head->spinlock ); + return ADDRESS( chunk, head->volumechunksize ); + } + + /* Find best match among free chunks */ + bytes += head->volumechunksize; + if( bytes < head->minchunksize ) + bytes = head->minchunksize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + chunk = mmVolumeFindFreeChunk( bytes, head->freeroot ); + + /* Allocate a new volume */ + if( !( chunk ) ) + { + allocsize = head->volumesize; + + /* Allocate new volume and add to list */ + minsize = bytes + ( head->volumeblocksize + head->volumechunksize ); + if( minsize > allocsize ) + allocsize = minsize; + + head->totalfreesize += allocsize - ( head->volumeblocksize + head->volumechunksize ); + + vmem = (intptr_t)head->relayalloc( head->relayvalue, allocsize MM_PASSPARAMS ); + volume = (void *)( ( vmem + head->alignment ) & ~head->alignment ); + volume->bigsize = 0; + volume->alignment = (intptr_t)volume - vmem; + mmListAdd( &head->volumelist, volume, offsetof(mmVolume,list) ); + + /* Add a free chunk to cover the whole volume */ + chunk = ADDRESS( volume, head->volumeblocksize ); + + chunk->h.prevoffset = 0; + allocsize -= head->volumeblocksize + volume->alignment; + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, allocsize, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + } + + /* Remove best match of free chunk from btree */ + mmBTreeRemove( chunk, offsetof(mmVolumeChunk,node), &head->freeroot ); + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + MM_VOLUME_CHUNK_SET_FLAGS( chunk, chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ); + + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + head->totalfreesize -= nextoffset - head->volumechunksize; + extrasize = nextoffset - bytes; + if( extrasize >= head->minchunksize ) + { + /* Split and spawn a new free chunk */ + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, bytes, 0 ); + newchunk = ADDRESS( chunk, bytes ); + newchunk->h.prevoffset = bytes; + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, extrasize, MM_VOLUME_CHUNK_FLAGS_FREE | ( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ); + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + nextchunk = ADDRESS( chunk, nextoffset ); + nextchunk->h.prevoffset = extrasize; + } + head->totalfreesize += extrasize - head->volumechunksize; + mmVolumeAddFreeChunk( newchunk, &head->freeroot ); + } + + mtSpinUnlock( &head->spinlock ); + return ADDRESS( chunk, head->volumechunksize ); +} + + +static mmVolumeChunk *mmVolumeMergeFree( mmVolumeHead *head, mmVolumeChunk *chunk ) +{ + int32_t nextoffset, chunkflags, next2offset; + mmVolumeChunk *prev, *next, *next2; + + prev = 0; + if( chunk->h.prevoffset ) + prev = ADDRESS( chunk, -chunk->h.prevoffset ); + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + head->totalfreesize += nextoffset - head->volumechunksize; + next = 0; + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + next = ADDRESS( chunk, nextoffset ); + if( ( prev ) && ( MM_VOLUME_CHUNK_GET_FLAGS( prev ) & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + { + /* Merge free space with previous chunk */ + mmBTreeRemove( prev, offsetof(mmVolumeChunk,node), &head->freeroot ); + prev->h.nextoffset += nextoffset; + MM_VOLUME_CHUNK_SET_FLAGS( prev, chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ); + chunk = prev; + head->totalfreesize += head->volumechunksize; +/* + prev = 0; + if( chunk->h.prevoffset ) + prev = ADDRESS( chunk, -chunk->h.prevoffset ); +*/ + if( next ) + { + next->h.prevoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + if( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_FREE ) + goto mergenext; + } + } + else if( ( next ) && ( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + { + /* Merge free space with next chunk */ + mergenext: + mmBTreeRemove( next, offsetof(mmVolumeChunk,node), &head->freeroot ); + next2offset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( next ); + chunk->h.nextoffset += next2offset; + head->totalfreesize += head->volumechunksize; + if( MM_VOLUME_CHUNK_GET_FLAGS( next ) & MM_VOLUME_CHUNK_FLAGS_LAST ) + MM_VOLUME_CHUNK_SET_FLAGS( chunk, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + else + { + MM_VOLUME_CHUNK_SET_FLAGS( chunk, MM_VOLUME_CHUNK_FLAGS_FREE ); + next2 = ADDRESS( next, next2offset ); + next2->h.prevoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + } + } + else + { + /* Solitary free chunk */ + MM_VOLUME_CHUNK_SET_FLAGS( chunk, chunkflags | MM_VOLUME_CHUNK_FLAGS_FREE ); + } + + return chunk; +} + + +void MM_FUNC(VolumeRelease)( mmVolumeHead *head, void *v MM_PARAMS ) +{ + mmVolumeChunk *chunk; + mmVolume *volume; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( !( chunk->h.nextoffset ) ) + { + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volume->bigsize MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + chunk = mmVolumeMergeFree( head, chunk ); + + /* Register the new free chunk */ + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free a chunk of memory that was allocated by mmVolumeAlloc(). + * + * The flags parameter may contain the flag MM_VOLUME_FLAGS_RELEASE, which + * instructs the volume manager not to free unused memory, improving + * performance if the blocks are to be soon reused. + */ +void MM_FUNC(VolumeFree)( mmVolumeHead *head, void *v MM_PARAMS ) +{ + size_t totalfreesize; + mmVolumeChunk *chunk; + mmVolume *volume; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + + /* Big chunk handling */ + if( !( chunk->h.nextoffset ) ) + { + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volume->bigsize MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + chunk = mmVolumeMergeFree( head, chunk ); + + totalfreesize = head->totalfreesize - ( MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize ); + + /* If our free chunk is alone in there, free the whole volume */ + if( ( MM_VOLUME_CHUNK_GET_FLAGS( chunk ) & MM_VOLUME_CHUNK_FLAGS_LAST ) && !( chunk->h.prevoffset ) && ( totalfreesize >= head->keepfreesize ) ) + { + head->totalfreesize = totalfreesize; + volume = ADDRESS( chunk, -head->volumeblocksize ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) MM_PASSPARAMS ); + mtSpinUnlock( &head->spinlock ); + return; + } + + /* Register the new free chunk */ + mmVolumeAddFreeChunk( chunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + + + +/** + * Shrink a chunk of memory that was allocated by mmVolumeAlloc(). + * + * The bytes parameter defines the new size of the chunk of memory. The + * specified pointer remains valid as the chunk is never relocated elsewhere. + * This function can not be used to attempt to increase the size of a chunk. + */ +void MM_FUNC(VolumeShrink)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ) +{ + int32_t nextoffset, newoffset, chunkflags, nextflags, next2offset; + size_t freesize; + mmVolumeChunk *chunk, *next, *next2, *newchunk; + + chunk = ADDRESS( v, -head->volumechunksize ); + mtSpinLock( &head->spinlock ); + next = 0; + chunkflags = MM_VOLUME_CHUNK_GET_FLAGS( chunk ); + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + if( !( chunkflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + next = ADDRESS( chunk, nextoffset ); + nextflags = MM_VOLUME_CHUNK_GET_FLAGS( next ); + next2offset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( next ); + } + + bytes += head->volumechunksize; + if( bytes < head->minchunksize ) + bytes = head->minchunksize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + freesize = nextoffset - bytes; + if( ( freesize < head->minchunksize ) && ( !( next ) || !( nextflags & MM_VOLUME_CHUNK_FLAGS_FREE ) ) ) + { + mtSpinUnlock( &head->spinlock ); + return; + } + newoffset = bytes; + + newchunk = ADDRESS( chunk, newoffset ); + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( chunk, newoffset, 0 ); + newchunk->h.prevoffset = newoffset; + head->totalfreesize += freesize - head->volumechunksize; + if( !( next ) ) + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize, MM_VOLUME_CHUNK_FLAGS_FREE | MM_VOLUME_CHUNK_FLAGS_LAST ); + else + { + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize, MM_VOLUME_CHUNK_FLAGS_FREE ); + if( !( nextflags & MM_VOLUME_CHUNK_FLAGS_FREE ) ) + next->h.prevoffset = freesize; + else + { + mmBTreeRemove( next, offsetof(mmVolumeChunk,node), &head->freeroot ); + MM_VOLUME_CHUNK_SET_NEXTOFFSET_AND_FLAGS( newchunk, freesize + next2offset, nextflags ); + if( !( nextflags & MM_VOLUME_CHUNK_FLAGS_LAST ) ) + { + next2 = ADDRESS( next, next2offset ); + next2->h.prevoffset = freesize + next2offset; + } + } + } + mmVolumeAddFreeChunk( newchunk, &head->freeroot ); + + mtSpinUnlock( &head->spinlock ); + return; +} + + +size_t MM_FUNC(VolumeGetAllocSize)( mmVolumeHead *head, void *v ) +{ + mmVolumeChunk *chunk; + chunk = ADDRESS( v, -head->volumechunksize ); + return MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize; +} + + +/** + * Inspect a volume manager and free unused pages of memory. + * + * This function may only free memory if MM_VOLUME_FLAGS_RELEASE was used when + * freeing chunks of memory allocated by the manager. + */ +void MM_FUNC(VolumeClean)( mmVolumeHead *head MM_PARAMS ) +{ + mmVolume *volume, *next; + mmVolumeChunk *chunk; + mtSpinLock( &head->spinlock ); + for( volume = head->volumelist ; volume ; volume = next ) + { + next = volume->list.next; + chunk = ADDRESS( volume, head->volumeblocksize ); + if( ( MM_VOLUME_CHUNK_GET_FLAGS(chunk) & MM_VOLUME_CHUNK_FLAGS_LAST ) && !( chunk->h.prevoffset ) ) + { + head->totalfreesize -= MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + mmListRemove( volume, offsetof(mmVolume,list) ); + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) MM_PASSPARAMS ); + } + } + mtSpinUnlock( &head->spinlock ); + return; +} + + +/** + * Free all memory allocated by the memory volume manager. + */ +void MM_FUNC(VolumeFreeAll)( mmVolumeHead *head MM_PARAMS ) +{ + int32_t nextoffset; + size_t volumesize; + mmVolume *volume, *next; + mmVolumeChunk *chunk; + mtSpinLock( &head->spinlock ); + for( volume = head->volumelist ; volume ; volume = next ) + { + next = volume->list.next; + chunk = ADDRESS( volume, head->volumeblocksize ); + volumesize = volume->bigsize; + if( chunk->h.nextoffset ) + { + volumesize = 0; + for( ; ; ) + { + nextoffset = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ); + volumesize += nextoffset; + if( MM_VOLUME_CHUNK_GET_FLAGS( chunk ) & MM_VOLUME_CHUNK_FLAGS_LAST ) + break; + chunk = ADDRESS( chunk, nextoffset ); + } + } + head->relayfree( head->relayvalue, ADDRESS( volume, -volume->alignment ), volumesize MM_PASSPARAMS ); + } + mtSpinUnlock( &head->spinlock ); + mtSpinDestroy( &head->spinlock ); + return; +} + + +void *MM_FUNC(VolumeRealloc)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ) +{ + size_t chunksize; + void *newv; + mmVolumeChunk *chunk; + if( !( v ) ) + return MM_FUNC(VolumeAlloc)( head, bytes MM_PASSPARAMS ); + chunk = ADDRESS( v, -head->volumechunksize ); + chunksize = MM_VOLUME_CHUNK_GET_NEXTOFFSET( chunk ) - head->volumechunksize; + if( bytes < chunksize ) + { + MM_FUNC(VolumeShrink)( head, v, bytes MM_PASSPARAMS ); + return v; + } + newv = MM_FUNC(VolumeAlloc)( head, bytes MM_PASSPARAMS ); + memcpy( newv, v, chunksize ); + MM_FUNC(VolumeFree)( head, v MM_PASSPARAMS ); + return newv; +} + + + +//// + + +#ifdef MM_ZONE_SUPPORT + + +typedef struct +{ + int32_t flags; + void *next, *prev; +} mmZoneChunk; + +#define MM_ZONE_CHUNK_FLAGS_FREE (0x1) +#define MM_ZONE_CHUNK_FLAGS_LAST (0x2) + + +int MM_FUNC(ZoneInit)( mmZoneHead *head, size_t zonesize, intptr_t alignment MM_PARAMS ) +{ + mmZoneChunk *chunk; + + memset( head, 0, sizeof(mmZoneHead) ); + mtSpinInit( &head->spinlock ); + head->pagesize = sysconf( _SC_PAGESIZE ) - 1; + head->pagealignment = head->pagesize - 1; + zonesize = ( zonesize + head->pagealignment ) & ~head->pagealignment; + head->zonesize = zonesize; + mtSpinLock( &head->spinlock ); + + head->address = mmap( 0x0, head->zonesize, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ); + if( head->address == MAP_FAILED ) + { +#ifdef MAP_NORESERVE + head->address = mmap( 0x0, head->zonesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0 ); + if( head->address == MAP_FAILED ) + return 0; +#else + return 0; +#endif + } + + head->alignment = 3; + if( alignment > 4 ) + head->alignment = alignment - 1; + head->chunkheadersize = ( sizeof(mmZoneChunk) + head->alignment ) & ~head->alignment; + + mprotect( head->address, head->pagesize, PROT_READ | PROT_WRITE ); + madvise( head->address, head->pagesize, MADV_NORMAL ); + chunk = head->address; + chunk->flags = MM_ZONE_CHUNK_FLAGS_FREE | MM_ZONE_CHUNK_FLAGS_LAST; + chunk->prev = 0; + chunk->next = ADDRESS( chunk, head->zonesize ); + head->chunklist = chunk; + + mtSpinUnlock( &head->spinlock ); + return 1; +} + + +void *MM_FUNC(ZoneAlloc)( mmZoneHead *head, size_t bytes MM_PARAMS ) +{ + int32_t lastflag, chunkflags; + mmZoneChunk *chunk, *newchunk, *nextchunk; + size_t chunknewsize, chunksize, unlocksize; + + bytes += head->chunkheadersize; + bytes = ( bytes + head->alignment ) & ~head->alignment; + + lastflag = 0; + for( chunk = head->chunklist ; ; chunk = chunk->next ) + { + if( lastflag ) + { + /* Our zone runs out of space? This is bad. Bad bad bad. */ + printf( "CRITICAL : Memory zone exhausted all virtual mapping space!\n" ); + return 0; + } + lastflag = chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + continue; + chunksize = ADDRESSDIFF( chunk->next, chunk ); + if( chunksize >= bytes ) + break; + } + + chunkflags = chunk->flags; + chunknewsize = ( bytes + head->pagealignment ) & ~head->pagealignment; + if( ( chunksize - chunknewsize ) >= head->pagesize ) + { + chunk->flags = 0; + newchunk = ADDRESS( chunk, chunknewsize ); + unlocksize = chunknewsize + head->chunkheadersize; + mprotect( chunk, unlocksize, PROT_READ | PROT_WRITE ); + madvise( chunk, unlocksize, MADV_NORMAL ); + newchunk->flags = chunkflags; + newchunk->prev = chunk; + newchunk->next = chunk->next; + if( !( chunkflags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + { + nextchunk = chunk->next; + nextchunk->prev = newchunk; + } + chunk->next = newchunk; + } + else + { + chunk->flags &= ~MM_ZONE_CHUNK_FLAGS_FREE; + mprotect( chunk, chunknewsize, PROT_READ | PROT_WRITE ); + madvise( chunk, chunknewsize, MADV_NORMAL ); + } + + return ADDRESS( chunk, head->chunkheadersize ); +} + + +void MM_FUNC(ZoneFree)( mmZoneHead *head, void *v MM_PARAMS ) +{ + mmZoneChunk *chunk, *prevchunk, *nextchunk, *next2chunk; + size_t extrasize; + void *page; + + chunk = ADDRESS( v, -head->chunkheadersize ); + page = (void *)( ( (intptr_t)v + head->pagealignment ) & ~head->pagealignment ); + extrasize = ADDRESSDIFF( chunk->next, page ); + mprotect( page, extrasize, PROT_READ ); + madvise( page, extrasize, MADV_DONTNEED ); + + chunk->flags |= MM_ZONE_CHUNK_FLAGS_FREE; + prevchunk = chunk->prev; + nextchunk = 0; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + nextchunk = chunk->next; + + if( ( nextchunk ) && ( nextchunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + { + chunk->next = nextchunk->next; + chunk->flags |= nextchunk->flags & MM_ZONE_CHUNK_FLAGS_LAST; + next2chunk = 0; + if( !( nextchunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + { + next2chunk = nextchunk->next; + next2chunk->prev = chunk; + } + mprotect( nextchunk, head->pagesize, PROT_READ ); + madvise( nextchunk, head->pagesize, MADV_DONTNEED ); + nextchunk = 0; + if( !( chunk->flags & MM_ZONE_CHUNK_FLAGS_LAST ) ) + nextchunk = chunk->next; + } + + if( ( prevchunk ) && ( prevchunk->flags & MM_ZONE_CHUNK_FLAGS_FREE ) ) + { + prevchunk->next = chunk->next; + if( nextchunk ) + nextchunk->prev = prevchunk; + mprotect( chunk, head->pagesize, PROT_READ ); + madvise( chunk, head->pagesize, MADV_DONTNEED ); + } + + return; +} + + +void MM_FUNC(ZoneFreeAll)( mmZoneHead *head MM_PARAMS ) +{ + mtSpinLock( &head->spinlock ); + munmap( head->address, head->zonesize ); + mtSpinUnlock( &head->spinlock ); + return; +} + + +#endif + + + +//// + + + +#if MM_DEBUG + +#undef malloc +#undef free +#undef realloc + +#endif + +#undef mmDebugAlloc +#undef mmDebugFree +#undef mmDebugRealloc +#undef mmListUses + + +typedef struct +{ + const char *file; + int line; + size_t size; + size_t count; + mmListNode list; +#if MM_DEBUG_GUARD_BYTES + char guard[MM_DEBUG_GUARD_BYTES]; +#endif +} mmChunk; + +static void *mmChunkList = 0; +static size_t mmCount = 0; +static size_t mmMaxCount = 0; + + +void *mmDebugAlloc( size_t bytes, const char *file, int line ) +{ +#if MM_DEBUG_GUARD_BYTES + int guard; + char *endguard; +#endif + mmChunk *chunk; + mtMutexLock( &mmGlobalMutex ); +#if MM_DEBUG_MMAP + if( !( chunk = mmap( 0, sizeof(mmChunk) + bytes + MM_DEBUG_GUARD_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0 ) ) ) +#else + if( !( chunk = malloc( sizeof(mmChunk) + bytes + MM_DEBUG_GUARD_BYTES ) ) ) +#endif + { +#if MM_WINDOWS + fprintf( stderr, "FATAL : Denied memory allocation ( %ld ) at %s:%d !\nExiting\n", (long)bytes, file, line ); +#else + fprintf( stderr, "FATAL : Denied memory allocation ( %lld ) at %s:%d !\nExiting\n", (long long)bytes, file, line ); +#endif + exit( 1 ); + } + mmListAdd( &mmChunkList, chunk, offsetof(mmChunk,list) ); + chunk->file = file; + chunk->line = line; + chunk->size = bytes; +#if MM_DEBUG_GUARD_BYTES + endguard = ADDRESS( chunk, sizeof(mmChunk) + bytes ); + for( guard = 0 ; guard < MM_DEBUG_GUARD_BYTES ; guard++ ) + { + chunk->guard[guard] = 0x55; + endguard[guard] = 0x75; + } +#endif + mmCount += bytes; + if( mmCount > mmMaxCount ) + mmMaxCount = mmCount; + mtMutexUnlock( &mmGlobalMutex ); + return ADDRESS( chunk, sizeof(mmChunk) ); +} + + +void *mmDebugRealloc( void *v, size_t bytes, const char *file, int line ) +{ + size_t size; + mmChunk *chunk; + void *newchunk = 0; + newchunk = mmDebugAlloc( bytes, file, line ); + if( v ) + { + size = bytes; + chunk = ADDRESS( v, -(int)sizeof(mmChunk) ); + if( chunk->size < size ) + size = chunk->size; + if( size ) + memcpy( newchunk, v, size ); + mmDebugFree( v, file, line ); + } + return newchunk; +} + + +void mmDebugFree( void *v, const char *file, int line ) +{ + mmChunk *chunk; +#if MM_DEBUG_GUARD_BYTES + int guard; + char *endguard; +#endif + chunk = ADDRESS( v, -(int)sizeof(mmChunk) ); +#if MM_DEBUG_GUARD_BYTES + endguard = ADDRESS( chunk, sizeof(mmChunk) + chunk->size ); + for( guard = 0 ; guard < MM_DEBUG_GUARD_BYTES ; guard++ ) + { + if( chunk->guard[guard] != 0x55 ) + { + fprintf( stderr, "MALLOC START[%d] GUARD ERROR : Corruption of %s:%d\n", guard, chunk->file, chunk->line ); + exit( 1 ); + } + if( endguard[guard] != 0x75 ) + { + fprintf( stderr, "MALLOC END[%d] GUARD ERROR : Corruption of %s:%d\n", guard, chunk->file, chunk->line ); + exit( 1 ); + } + } +#endif + mtMutexLock( &mmGlobalMutex ); + mmListRemove( chunk, offsetof(mmChunk,list) ); + mmCount -= chunk->size; +#if MM_DEBUG_MMAP + #if MM_DEBUG_MMAP_LINGERING + mprotect( chunk, sizeof(mmChunk) + chunk->size + MM_DEBUG_GUARD_BYTES, PROT_NONE ); + #else + munmap( chunk, sizeof(mmChunk) + chunk->size + MM_DEBUG_GUARD_BYTES ); + #endif +#else + free( chunk ); +#endif + mtMutexUnlock( &mmGlobalMutex ); + return; +} + + +void *mmAlloc( void *unused, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + return mmDebugAlloc( bytes MM_PASSPARAMS ); +#else + void *chunk; + #if MM_WINDOWS + if( !( chunk = malloc( bytes ) ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld )!\nExiting\n", (long)bytes ); + #else + if( !( chunk = malloc( bytes ) ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld )!\nExiting\n", (long long)bytes ); + #endif + return chunk; +#endif +} + + +void *mmRealloc( void *unused, void *v, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + return mmDebugRealloc( v, bytes MM_PASSPARAMS ); +#else + #if MM_WINDOWS + if( !( v = realloc( v, bytes ) ) && ( bytes ) ) + fprintf( stderr, "WARNING : Denied memory reallocation ( %ld )!\nExiting\n", (long)bytes ); + #else + if( !( v = realloc( v, bytes ) ) && ( bytes ) ) + fprintf( stderr, "WARNING : Denied memory reallocation ( %lld )!\nExiting\n", (long long)bytes ); + #endif + return v; +#endif +} + + +void mmFree( void *unused, void *v, size_t bytes MM_PARAMS ) +{ +#if MM_DEBUG + mmDebugFree( v MM_PASSPARAMS ); +#else + free( v ); +#endif + return; +} + + + +/** + * Private function to add the sorted memory item in the linked list. + */ +static void mmListSortAdd( mmChunk *chunk, void **sortlist ) +{ + intptr_t i; + const char *s0, *s1; + mmChunk *sortchunk; + s0 = chunk->file; + for( ; *sortlist ; sortlist = &sortchunk->list.next ) + { + sortchunk = *sortlist; + s1 = sortchunk->file; + for( i = 0 ; ; i++ ) + { + if( s0[i] < s1[i] ) + goto insert; + if( s0[i] > s1[i] ) + break; + if( s1[i] ) + continue; + if( chunk->line > sortchunk->line ) + break; + goto insert; + } + } + insert: + mmListAdd( sortlist, chunk, offsetof(mmChunk,list) ); + return; +} + + +/** + * List all memory allocations at any time and print the results on stdout. + */ +void mmListUses( const char *file, int line ) +{ + mmChunk *chunk; + mmChunk *chunksort; + void *sortlist; + size_t totalsize; + mtMutexLock( &mmGlobalMutex ); + printf( "-- Memory allocation listing at %s:%d --\n", file, line ); + if( !( mmChunkList ) ) + { + printf( " List empty\n\n" ); + mtMutexUnlock( &mmGlobalMutex ); + return; + } + sortlist = 0; + for( chunk = mmChunkList ; chunk ; chunk = chunk->list.next ) + { + for( chunksort = sortlist ; chunksort ; chunksort = chunksort->list.next ) + { + if( chunk->file != chunksort->file ) + continue; + if( chunk->line != chunksort->line ) + continue; + goto found; + } + if( !( chunksort = malloc( sizeof(mmChunk) ) ) ) + { + mtMutexUnlock( &mmGlobalMutex ); + return; + } + memset( chunksort, 0, sizeof(mmChunk) ); + chunksort->file = chunk->file; + chunksort->line = chunk->line; + chunksort->count = 0; + mmListSortAdd( chunksort, &sortlist ); + found: + chunksort->size += chunk->size; + chunksort->count++; + } + totalsize = 0; + for( chunksort = sortlist ; chunksort ; chunksort = chunk ) + { + printf( " %10ld bytes in %6ld chunk(s) allocated at %s:%d\n", (long)chunksort->size, (long)chunksort->count, chunksort->file, chunksort->line ); + totalsize += chunksort->size; + chunk = chunksort->list.next; + free( chunksort ); + } +#if MM_WINDOWS + printf( " %10ld bytes total\n", (long)totalsize ); + printf( " %10ld bytes maximum reached\n\n", (long)mmMaxCount ); +#else + printf( " %10lld bytes total\n", (long long)totalsize ); + printf( " %10lld bytes maximum reached\n\n", (long long)mmMaxCount ); +#endif + mtMutexUnlock( &mmGlobalMutex ); + return; +} + + + +//// + + + +#ifdef MM_WIN32 + +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + +int mmGetTimeOfDay( struct timeval *tv ) +{ + FILETIME ft; + uint64_t curtime; + if( tv ) + { + GetSystemTimeAsFileTime( &ft ); + curtime = ft.dwHighDateTime; + curtime <<= 32; + curtime |= ft.dwLowDateTime; + curtime /= 10; + curtime -= DELTA_EPOCH_IN_MICROSECS; + tv->tv_sec = (long)( curtime / 1000000UL ); + tv->tv_usec = (long)( curtime % 1000000UL ); + } + return 0; +} + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mm.h b/ecere/src/gfx/newFonts/cc/mm.h new file mode 100644 index 0000000..077e9c2 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mm.h @@ -0,0 +1,786 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Global memory management header. + */ + +#ifndef MM_H +#define MM_H + +#include + +#define OFFSET(s, m) ((unsigned int)(uintptr_t) (&((s *) 0)->m)) + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define MM_DEBUG 0 + +#define MM_INLINE_LIST_FUNCTIONS + +#define MM_ALLOC_CHECK + + +#define MM_DEBUG_GUARD_BYTES (32) +#define MM_DEBUG_MMAP (1) +/* Enabling this will lead to ever growing memory usage! Strictly for debugging. */ +#define MM_DEBUG_MMAP_LINGERING (0) + + + +//// + + +#ifdef MM_NUMA + #include +#endif + + +#if defined(__linux__) || defined(__gnu_linux__) || defined(__linux) || defined(__linux) + #define MM_LINUX (1) + #define MM_UNIX (1) +#elif defined(__APPLE__) + #define MM_OSX (1) + #define MM_UNIX (1) +#elif defined(__unix__) || defined(__unix) || defined(unix) + #define MM_UNIX (1) +#elif defined(_WIN64) || defined(__WIN64__) || defined(WIN64) + #define MM_WIN64 (1) + #define MM_WIN32 (1) + #define MM_WINDOWS (1) +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define MM_WIN32 (1) + #define MM_WINDOWS (1) +#endif + +#if __MINGW64__ + #define MM_MINGW32 (1) + #define MM_MINGW64 (1) +#elif __MINGW32__ + #define MM_MINGW32 (1) +#endif + + +#if !MM_UNIX + #undef MM_DEBUG_MMAP + #define MM_DEBUG_MMAP (0) +#endif + + +#define MM_CPU_COUNT_MAXIMUM (1024) +#define MM_NODE_COUNT_MAXIMUM (256) + + +#ifndef CPUCONF_CACHE_LINE_SIZE + #define CPUCONF_CACHE_LINE_SIZE 64 +#endif + + +#if defined(__GNUC__) + #define MM_CACHE_ALIGN __attribute__((aligned(CPUCONF_CACHE_LINE_SIZE))) + #define MM_RESTRICT __restrict + #define MM_NOINLINE __attribute__((noinline)) +#else + #define MM_CACHE_ALIGN + #define MM_RESTRICT + #define MM_NOINLINE +#endif + + +#if defined(__GNUC__) || defined(__INTEL_COMPILER) + #define MM_ALIGN16 __attribute__((aligned(16))) + #define MM_ALIGN16_SAFE (1) +#elif defined(_MSC_VER) + #define MM_ALIGN16 __declspec(align(16)) + #define MM_ALIGN16_SAFE (1) +#else + #define MM_ALIGN16 + #define MM_ALIGN16_SAFE (0) +#endif + + + +#define MM_ERROR() {printf("MM Error at %s:%d\n",file,line);exit(1)} + +#ifndef ADDRESS + #define ADDRESS(p,o) ((void *)(((char *)p)+(o))) +#endif + +#ifndef ADDRESSDIFF + #define ADDRESSDIFF(a,b) (((char *)a)-((char *)b)) +#endif + + +#include + +#ifdef __WIN32__ +int mmGetTimeOfDay( struct timeval *tv ); +#define gettimeofday(a,b) mmGetTimeOfDay(a) +#endif + + +typedef struct +{ + int numaflag; + int pagesize; + int cpucount; + int nodecount; + int cpunode[MM_CPU_COUNT_MAXIMUM]; + int64_t nodesize[MM_NODE_COUNT_MAXIMUM]; + int nodecpucount[MM_NODE_COUNT_MAXIMUM]; + int64_t sysmemory; +} mmContext; + +extern mmContext mmcontext; + + +#include "mmatomic.h" +#include "mmthread.h" + + +#if MM_DEBUG + #define MM_FUNC(n) mm##n##Debug + #define MM_PARAMS , const char *file, int line +#else + #define MM_FUNC(n) mm##n + #define MM_PARAMS +#endif + + +//// + + +void mmInit(); +void mmEnd(); + + +void mmThreadBindToNode( int nodeindex ); +void mmThreadBindToNode( int nodeindex ); +void mmThreadBindToCpu( int cpuindex ); +int mmCpuGetNode( int cpuindex ); + +void *mmNodeAlloc( int nodeindex, size_t size ); +void mmNodeFree( int nodeindex, void *v, size_t size ); +void mmNodeMap( int nodeindex, void *start, size_t bytes ); + +void *mmNodeAlignAlloc( int nodeindex, size_t size, intptr_t align ); +void mmNodeAlignFree( int nodeindex, void *v, size_t size ); + + + +//// + + + +typedef struct +{ + void **prev; + void *next; +} mmListNode; + +typedef struct +{ + void *first; + void **last; +} mmListDualHead; + +typedef struct +{ + void *first; + void *last; +} mmListLoopHead; + +#ifndef MM_INLINE_LIST_FUNCTIONS + +void mmListAdd( void **list, void *item, intptr_t offset ); +void mmListRemove( void *item, intptr_t offset ); +void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ); +void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ); + +void mmListDualInit( mmListDualHead *head ); +void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ); +void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ); +void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ); +void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ); +void *mmListDualLast( mmListDualHead *head, intptr_t offset ); +void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ); + +#else + +static inline void mmListAdd( void **list, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = list; + node->next = *list; + if( *list ) + { + next = ADDRESS( *list, offset ); + next->prev = &(node->next); + } + *list = item; + return; +} + +static inline void mmListRemove( void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + return; +} + +static inline void mmListMergeList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + return; + for( item = *listdst ; item ; item = node->next ) + { + node = ADDRESS( item, offset ); + listdst = &node->next; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + +static inline void mmListMoveList( void **listdst, void **listsrc, intptr_t offset ) +{ + void *item; + mmListNode *node; + if( !( *listsrc ) ) + { + *listdst = 0; + return; + } + item = *listsrc; + node = ADDRESS( item, offset ); + node->prev = listdst; + *listdst = item; + *listsrc = 0; + return; +} + +static inline void mmListDualInit( mmListDualHead *head ) +{ + head->first = 0; + head->last = &head->first; + return; +} + +static inline void mmListDualAddFirst( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = &head->first; + node->next = head->first; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + head->first = item; + return; +} + +static inline void mmListDualAddLast( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + void **prev; + prev = head->last; + *prev = item; + node = ADDRESS( item, offset ); + node->prev = head->last; + head->last = &(node->next); + node->next = 0; + return; +} + +static inline void mmListDualInsertAfter( mmListDualHead *head, void **prevnext, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + node->prev = prevnext; + node->next = *prevnext; + if( *prevnext ) + { + next = ADDRESS( *prevnext, offset ); + next->prev = &(node->next); + } + else + head->last = &(node->next); + *prevnext = item; + return; +} + +static inline void mmListDualRemove( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node, *next; + node = ADDRESS( item, offset ); + *(node->prev) = (void *)node->next; + if( node->next ) + { + next = ADDRESS( node->next, offset ); + next->prev = node->prev; + } + else + head->last = node->prev; + return; +} + +static inline void *mmListDualLast( mmListDualHead *head, intptr_t offset ) +{ + if( !( head->first ) ) + return 0; + return ADDRESS( head->last, -( offset + OFFSET(mmListNode,next) ) ); +} + +static inline void *mmListDualPrevious( mmListDualHead *head, void *item, intptr_t offset ) +{ + mmListNode *node; + if( item == head->first ) + return 0; + node = ADDRESS( item, offset ); + return ADDRESS( node->prev, -( offset + OFFSET(mmListNode,next) ) ); +} + +#endif + +void mmListLoopInit( mmListLoopHead *head ); +void mmListLoopAddFirst( mmListLoopHead *head, void *item, intptr_t offset ); +void mmListLoopAddLast( mmListLoopHead *head, void *item, intptr_t offset ); +void mmListLoopInsert( mmListLoopHead *head, void *previtem, void *item, intptr_t offset ); +void mmListLoopRemove( mmListLoopHead *head, void *item, intptr_t offset ); +void *mmListLoopLast( mmListLoopHead *head, intptr_t offset ); + + + +//// + + + +typedef struct +{ + void *child[2]; + void *parent; + int flags; +} mmBTreeNode; + +#define MM_BTREE_FLAGS_LEFT (0) +#define MM_BTREE_FLAGS_RIGHT (1) +#define MM_BTREE_FLAGS_DIRECTION_MASK (1) +#define MM_BTREE_FLAGS_STEP (2) + +void mmBTreeInsert( void *item, void *parent, int itemflag, intptr_t offset, void **root ); +void mmBTreeRemove( void *item, intptr_t offset, void **root ); + +void *mmBtreeMostLeft( void *root, intptr_t offset ); +void *mmBtreeMostRight( void *root, intptr_t offset ); +void *mmBtreeNeighbourLeft( void *item, intptr_t offset ); +void *mmBtreeNeighbourRight( void *item, intptr_t offset ); +intptr_t mmBtreeItemCount( void *root, intptr_t offset ); +int mmBtreeListOrdered( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ); +int mmBtreeListBalanced( void *root, intptr_t offset, int (*callback)( void *item, void *v ), void *v ); + + + +//// + + + +typedef struct +{ + mmListNode listnode; + mmBTreeNode node; + int freecount; + int blockindex; +} mmBlock; + +typedef struct +{ + void *blocklist; + void *freelist; + size_t chunksize; + int chunkperblock; + int alignment; + size_t allocsize; + int keepfreecount; + int chunkfreecount; + void *treeroot; + void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ); + void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ); + void *relayvalue; + mtSpin spinlock; +} mmBlockHead; + +void MM_FUNC(BlockInit)( mmBlockHead *head, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ); +void MM_FUNC(BlockNodeInit)( mmBlockHead *head, int nodeindex, size_t chunksize, int chunkperblock, int keepfreecount, int alignment MM_PARAMS ); +void *MM_FUNC(BlockAlloc)( mmBlockHead *head MM_PARAMS ); +void MM_FUNC(BlockRelease)( mmBlockHead *head, void *v MM_PARAMS ); +void MM_FUNC(BlockFree)( mmBlockHead *head, void *v MM_PARAMS ); +void MM_FUNC(BlockFreeAll)( mmBlockHead *head MM_PARAMS ); +void MM_FUNC(BlockProcessList)( mmBlockHead *head, void *userpointer, int (*processchunk)( void *chunk, void *userpointer ) MM_PARAMS ); +int MM_FUNC(BlockUseCount)( mmBlockHead *head MM_PARAMS ); +int MM_FUNC(BlockFreeCount)( mmBlockHead *head MM_PARAMS ); + +#if MM_DEBUG + #define mmBlockInit(v,w,x,y,z) MM_FUNC(BlockInit)(v,w,x,y,z,__FILE__,__LINE__) + #define mmBlockNodeInit(u,v,w,x,y,z) MM_FUNC(BlockNodeInit)(u,v,w,x,y,z,__FILE__,__LINE__) + #define mmBlockAlloc(x) MM_FUNC(BlockAlloc)(x,__FILE__,__LINE__) + #define mmBlockRelease(x,y) MM_FUNC(BlockRelease)(x,y,__FILE__,__LINE__) + #define mmBlockFree(x,y) MM_FUNC(BlockFree)(x,y,__FILE__,__LINE__) + #define mmBlockFreeAll(x) MM_FUNC(BlockFreeAll)(x,__FILE__,__LINE__) + #define mmBlockProcessList(x,y,z) MM_FUNC(BlockProcessList)(x,y,z,__FILE__,__LINE__) + #define mmBlockUseCount(x) MM_FUNC(BlockProcessList)(x,__FILE__,__LINE__) + #define mmBlockFreeCount(x) MM_FUNC(BlockProcessList)(x,__FILE__,__LINE__) +#endif + +/* +void mmBlockRelayByVolume( mmBlockHead *head, void *volumehead ); +void mmBlockRelayByZone( mmBlockHead *head, void *zonehead ); +*/ + + + +//// + + + +typedef struct +{ + mmBlockHead indexblock; + void *indextree; + intptr_t indexlimit; + mtSpin spinlock; +} mmIndexHead; + +void mmIndexInit( mmIndexHead *head, int indexesperblock ); +void mmIndexFreeAll( mmIndexHead *head ); +void mmIndexAdd( mmIndexHead *head, intptr_t index ); +intptr_t mmIndexGet( mmIndexHead *head ); +int mmIndexRemove( mmIndexHead *head, intptr_t index ); +size_t mmIndexCount( mmIndexHead *head ); + + + +//// + + + +typedef struct +{ + uintptr_t bitmask; + int bitshift; + uintptr_t countalign; + uintptr_t indexshift; + uintptr_t indexmask; + uintptr_t initmask; + size_t mapsize; + uintptr_t *map; + mtSpin spinlock; +} mmBitTableHead; + +void mmBitTableInit( mmBitTableHead *head, int bitsperentry, int chunksize, int initmask ); +void mmBitTableFreeAll( mmBitTableHead *head ); +void mmBitTableSet( mmBitTableHead *head, uintptr_t index, int flags, int editmask ); +uintptr_t mmBitTableGet( mmBitTableHead *head, uintptr_t index ); + + + +//// + + + +typedef struct +{ + size_t size; + size_t used; + void *next; +} mmGrowNode; + +typedef struct +{ + mmGrowNode *first; + size_t nodesize; + mtSpin spinlock; +} mmGrow; + +int MM_FUNC(GrowInit)( mmGrow *mgrow, size_t nodesize MM_PARAMS ); +void MM_FUNC(GrowFreeAll)( mmGrow *mgrow MM_PARAMS ); +void *MM_FUNC(GrowAlloc)( mmGrow *mgrow, size_t bytes MM_PARAMS ); +void MM_FUNC(GrowRewindLast)( mmGrow *mgrow, size_t rewind MM_PARAMS ); + +#if MM_DEBUG + #define mmGrowInit(x,y) MM_FUNC(GrowInit)(x,y,__FILE__,__LINE__) + #define mmGrowFreeAll(x) MM_FUNC(GrowFreeAll)(x,__FILE__,__LINE__) + #define mmGrowAlloc(x,y) MM_FUNC(GrowAlloc)(x,y,__FILE__,__LINE__) + #define mmGrowRewindLast(x) MM_FUNC(GrowRewindLast)(x,__FILE__,__LINE__) +#endif + + + +//// + + +#if 0 + +typedef struct +{ + void ***table; + intptr_t pagecount; + intptr_t pagesize; + intptr_t pagemask; + intptr_t pageshift; + mtSpin spinlock; +} mmDirectory; + +#define MM_DIR_ENTRY(dir,index) ( (dir)->table[ index >> (dir)->pageshift ][ index & (dir)->pagemask ] ) + +int MM_FUNC(DirInit)( mmDirectory *dir, intptr_t pageshift, intptr_t pagecount MM_PARAMS ); +void MM_FUNC(DirSize)( mmDirectory *dir, intptr_t size MM_PARAMS ); +void MM_FUNC(DirSet)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ); +void *MM_FUNC(DirGet)( mmDirectory *dir, intptr_t index MM_PARAMS ); +void MM_FUNC(DirSetFast)( mmDirectory *dir, intptr_t index, void *entry MM_PARAMS ); +void *MM_FUNC(DirGetFast)( mmDirectory *dir, intptr_t index MM_PARAMS ); +void MM_FUNC(DirFree)( mmDirectory *dir MM_PARAMS ); + +#if MM_DEBUG + #define mmDirInit(x,y,z) MM_FUNC(DirInit)(x,y,z,__FILE__,__LINE__) + #define mmDirSize(x,y) MM_FUNC(DirSize)(x,y,__FILE__,__LINE__) + #define mmDirSet(x,y,z) MM_FUNC(DirSet)(x,y,z,__FILE__,__LINE__) + #define mmDirGet(x,y) MM_FUNC(DirGet)(x,y,__FILE__,__LINE__) + #define mmDirSetFast(x,y,z) MM_FUNC(DirSetFast)(x,y,z,__FILE__,__LINE__) + #define mmDirGetFast(x,y) MM_FUNC(DirGetFast)(x,y,__FILE__,__LINE__) + #define mmDirFree(x) MM_FUNC(DirFree)(x,__FILE__,__LINE__) +#endif + +#endif + + +//// + + + +void *MM_FUNC(AlignAlloc)( size_t bytes, intptr_t align MM_PARAMS ); +void MM_FUNC(AlignFree)( void *v MM_PARAMS ); +void *MM_FUNC(AlignGrow)( void *v, size_t bytes, size_t copybytes, intptr_t align MM_PARAMS ); +void *MM_FUNC(AlignRelayAlloc)( void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ), void *relayvalue, size_t bytes, intptr_t align, size_t displacement MM_PARAMS ); +void MM_FUNC(AlignRelayFree)( void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ), void *relayvalue, void *v, size_t bytes MM_PARAMS ); + +#if MM_DEBUG + #define mmAlignAlloc(x,y) MM_FUNC(AlignAlloc)(x,y,__FILE__,__LINE__) + #define mmAlignFree(x) MM_FUNC(AlignFree)(x,__FILE__,__LINE__) + #define mmAlignGrow(x) MM_FUNC(AlignGrow)(x,__FILE__,__LINE__) + #define mmAlignRelayAlloc(v,w,x,y,z) MM_FUNC(AlignRelayAlloc)(v,w,x,y,z,__FILE__,__LINE__) + #define mmAlignRelayFree(w,x,y,z) MM_FUNC(AlignRelayFree)(w,x,y,z,__FILE__,__LINE__) +#endif + + + +//// + + + +typedef struct +{ + size_t volumesize; + size_t volumeblocksize; + size_t minchunksize; + size_t volumechunksize; + size_t keepfreesize; + size_t totalfreesize; + size_t alignment; + void *freeroot; + void *volumelist; + void *(*relayalloc)( void *head, size_t bytes MM_PARAMS ); + void (*relayfree)( void *head, void *v, size_t bytes MM_PARAMS ); + void *relayvalue; + mtSpin spinlock; +} mmVolumeHead; + +void MM_FUNC(VolumeInit)( mmVolumeHead *head, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ); +void MM_FUNC(VolumeNodeInit)( mmVolumeHead *head, int nodeindex, size_t volumesize, size_t minchunksize, size_t keepfreesize, size_t alignment MM_PARAMS ); +void *MM_FUNC(VolumeAlloc)( mmVolumeHead *head, size_t bytes MM_PARAMS ); +void MM_FUNC(VolumeRelease)( mmVolumeHead *head, void *v MM_PARAMS ); +void MM_FUNC(VolumeFree)( mmVolumeHead *head, void *v MM_PARAMS ); +void MM_FUNC(VolumeShrink)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ); +size_t MM_FUNC(VolumeGetAllocSize)( mmVolumeHead *head, void *v ); +void MM_FUNC(VolumeClean)( mmVolumeHead *head MM_PARAMS ); +void MM_FUNC(VolumeFreeAll)( mmVolumeHead *head MM_PARAMS ); +void *MM_FUNC(VolumeRealloc)( mmVolumeHead *head, void *v, size_t bytes MM_PARAMS ); + +#if MM_DEBUG + #define mmVolumeInit(w,x,y,z,a) MM_FUNC(VolumeInit)(w,x,y,z,a,__FILE__,__LINE__); + #define mmVolumeNodeInit(v,w,x,y,z) MM_FUNC(VolumeNodeInit)(v,w,x,y,z,__FILE__,__LINE__); + #define mmVolumeAlloc(x,y) MM_FUNC(VolumeAlloc)(x,y,__FILE__,__LINE__); + #define mmVolumeRelease(x,y) MM_FUNC(VolumeRelease)(x,y,__FILE__,__LINE__); + #define mmVolumeFree(x,y) MM_FUNC(VolumeFree)(x,y,__FILE__,__LINE__); + #define mmVolumeShrink(x,y,z) MM_FUNC(VolumeShrink)(x,y,z,__FILE__,__LINE__); + #define mmVolumeGetAllocSize(x) MM_FUNC(VolumeGetAllocSize)(x,y,__FILE__,__LINE__); + #define mmVolumeClean(x) MM_FUNC(VolumeClean)(x,__FILE__,__LINE__); + #define mmVolumeFreeAll(x) MM_FUNC(VolumeFreeAll)(x,__FILE__,__LINE__); + #define mmVolumeRealloc(x) MM_FUNC(VolumeRealloc)(x,y,z,__FILE__,__LINE__); + + #define mmVolumeAlloc MM_FUNC(VolumeAlloc) + #define mmVolumeRelease MM_FUNC(VolumeRelease) + #define mmVolumeFree MM_FUNC(VolumeFree) + +#endif + +/* +void mmVolumeRelayByZone( mmVolumeHead *head, void *zonehead ); +*/ + + + +//// + + + +typedef struct +{ + void *address; + size_t pagesize; + size_t pagealignment; + size_t zonesize; + size_t alignment; + size_t chunkheadersize; + void *chunklist; + mtSpin spinlock; +} mmZoneHead; + +int MM_FUNC(ZoneInit)( mmZoneHead *head, size_t zonesize, intptr_t alignment MM_PARAMS ); +void *MM_FUNC(ZoneAlloc)( mmZoneHead *head, size_t bytes MM_PARAMS ); +void MM_FUNC(ZoneFree)( mmZoneHead *head, void *v MM_PARAMS ); +void MM_FUNC(ZoneFreeAll)( mmZoneHead *head MM_PARAMS ); + +#if MM_DEBUG + #define mmZoneInit(x,y,z) MM_FUNC(ZoneInit)(x,y,z,__FILE__,__LINE__); + #define mmZoneAlloc(x,y) MM_FUNC(ZoneAlloc)(x,y,__FILE__,__LINE__); + #define mmZoneFree(x,y) MM_FUNC(ZoneFree)(x,y,__FILE__,__LINE__); + #define mmZoneFreeAll(x) MM_FUNC(ZoneFreeAll)(x,__FILE__,__LINE__); +#endif + + + +//// + + + +void *mmAlloc( void *unused, size_t bytes MM_PARAMS ); +void *mmRealloc( void *unused, void *v, size_t bytes MM_PARAMS ); +void mmFree( void *unused, void *v, size_t bytes MM_PARAMS ); + +void *mmDebugAlloc( size_t bytes, const char *file, int line ); +void *mmDebugRealloc( void *v, size_t bytes, const char *file, int line ); +void mmDebugFree( void *v, const char *file, int line ); +#define mmDebugAlloc(x) mmDebugAlloc(x,__FILE__,__LINE__) +#define mmDebugFree(x) mmDebugFree(x,__FILE__,__LINE__) +#define mmDebugRealloc(x,y) mmDebugRealloc(x,y,__FILE__,__LINE__) + +void mmListUses( const char *file, int line ); +#define mmListUses() mmListUses(__FILE__,__LINE__); + + +//// + + +#if MM_DEBUG + #define malloc(x) mmAlloc(0,(x),__FILE__,__LINE__) + #define realloc(x,y) mmRealloc(0,(x),(y),__FILE__,__LINE__) + #define free(x) mmFree(0,(x),0,__FILE__,__LINE__) +#elif defined(MM_ALLOC_CHECK) + +static inline void *mmAllocCheck( size_t size, const char *file, int line ) +{ + void *p; + p = malloc( size ); +#if MM_WINDOWS + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld bytes ) at %s:%d\n", (long)size, file, line ); +#else + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld bytes ) at %s:%d\n", (long long)size, file, line ); +#endif + return p; +} + +static inline void *mmReallocCheck( void *p, size_t size, const char *file, int line ) +{ + p = realloc( p, size ); +#if MM_WINDOWS + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %ld bytes ) at %s:%d\n", (long)size, file, line ); +#else + if( !( p ) ) + fprintf( stderr, "WARNING : Denied memory allocation ( %lld bytes ) at %s:%d\n", (long long)size, file, line ); +#endif + return p; +} + + #define malloc(x) mmAllocCheck((x),__FILE__,__LINE__) + #define realloc(x,y) mmReallocCheck((x),(y),__FILE__,__LINE__) +#endif + + + + +//// + + + +static inline uint64_t mmGetMillisecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000 ) + ( (uint64_t)lntime.tv_usec / 1000 ); +} + + +static inline uint64_t mmGetMicrosecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000000 ) + (uint64_t)lntime.tv_usec; +} + + +static inline uint64_t mmGetNanosecondsTime() +{ + struct timeval lntime; + gettimeofday( &lntime, 0 ); + return ( (uint64_t)lntime.tv_sec * 1000000000 ) + ( (uint64_t)lntime.tv_usec * 1000 ); +} + + + +//// + + + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/ecere/src/gfx/newFonts/cc/mmatomic.h b/ecere/src/gfx/newFonts/cc/mmatomic.h new file mode 100644 index 0000000..6b5729a --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmatomic.h @@ -0,0 +1,2475 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Atomic memory operations. + */ + + + + +#if ( defined(CPUCONF_ARCH_IA32) || defined(CPUCONF_ARCH_AMD64) ) && defined(__GNUC__) && !defined(MM_ATOMIC_SUPPORT) + + +#define MM_ATOMIC_SUPPORT + + +#ifdef CPUCONF_ARCH_AMD64 + #define MM_ATOMIC_64_BITS_SUPPORT +#endif + + +typedef struct { volatile int8_t value; } mmAtomic8; +typedef struct { volatile int16_t value; } mmAtomic16; +typedef struct { volatile int32_t value; } mmAtomic32; +#ifdef MM_ATOMIC_64_BITS_SUPPORT +typedef struct { volatile int64_t value; } mmAtomic64; +#endif + + +//// + + +/* + +Architecture Memory Ordering + +Memory model for x86 and amd64 +--- Aligned stores can not be partially seen by loads +--- Loads can NOT be reordered after loads +--- Loads can NOT be reordered after stores +--- Stores can NOT be reordered after stores +-X- Stores CAN be reordered after loads +--- Atomic instructions are NOT reordered with loads +--- Atomic instructions are NOT reordered with stores +--- Dependent loads can NOT be reordered + +*/ + + +/* Memory model configuration for x86/amd64 */ +// #define CPUCONF_LOAD_REODERING_AFTER_LOAD +// #define CPUCONF_LOAD_REODERING_AFTER_STORE +#define CPUCONF_STORE_REODERING_AFTER_LOAD +// #define CPUCONF_STORE_REODERING_AFTER_STORE +// #define CPUCONF_ATOMIC_REODERING_WITH_LOAD +// #define CPUCONF_ATOMIC_REODERING_WITH_STORE +// #define CPUCONF_DEPENDENT_REODERING + + +//// + + +/* Do nothing, prevent compiler reordering */ +static inline void mmBarrier() +{ + __asm__ __volatile__( "":::"memory" ); + return; +} + +/* All previous loads must complete before future loads */ +static inline void mmReadBarrier() +{ +#ifdef CPUCONF_CAP_SSE2 + __asm__ __volatile__( "lfence":::"memory" ); +#else + __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" ); +#endif + return; +} + +/* All previous stores must complete before future stores */ +static inline void mmWriteBarrier() +{ + /* x86 and AMD64 never reorder stores : the sfence instruction is useless unless chatting with devices on MMIO */ + __asm__ __volatile__( "":::"memory" ); + return; +} + +/* All previous loads/stores must complete before future loads/stores */ +static inline void mmFullBarrier() +{ +#ifdef CPUCONF_CAP_SSE2 + __asm__ __volatile__( "mfence":::"memory" ); +#else + __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" ); +#endif + return; +} + + +//// + + +/* Direct access to the atomic variables, for use when the caller knows no atomicity is needed */ +#define MM_ATOMIC_ACCESS_8(v) ((v)->value) +#define MM_ATOMIC_ACCESS_16(v) ((v)->value) +#define MM_ATOMIC_ACCESS_32(v) ((v)->value) +#ifdef MM_ATOMIC_64_BITS_SUPPORT + #define MM_ATOMIC_ACCESS_64(v) ((v)->value) +#endif + + +//// + + +/* +mmAtomicRead*() +Atomically read the value +*/ +static inline int8_t mmAtomicRead8( mmAtomic8 *v ) +{ + mmBarrier(); + return v->value; +} + +static inline int16_t mmAtomicRead16( mmAtomic16 *v ) +{ + mmBarrier(); + return v->value; +} + +static inline int32_t mmAtomicRead32( mmAtomic32 *v ) +{ + mmBarrier(); + return v->value; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicRead64( mmAtomic64 *v ) +{ + mmBarrier(); + return v->value; +} +#endif + + +//// + + +/* +mmAtomicWrite*() +Atomically write the value +*/ +static inline void mmAtomicWrite8( mmAtomic8 *v, int8_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +static inline void mmAtomicWrite16( mmAtomic16 *v, int16_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +static inline void mmAtomicWrite32( mmAtomic32 *v, int32_t i ) +{ + mmBarrier(); + v->value = i; + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicWrite64( mmAtomic64 *v, int64_t i ) +{ + mmBarrier(); + v->value = i; + return; +} +#endif + + +//// + + +/* +mmAtomicBarrierWrite*() +Atomically write the value and act as a full memory barrier +*/ +static inline void mmAtomicBarrierWrite8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xchgb %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +static inline void mmAtomicBarrierWrite16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xchgw %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +static inline void mmAtomicBarrierWrite32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xchgl %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicBarrierWrite64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xchgq %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return; +} +#endif + + +//// + + +static inline void mmAtomicAdd8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; addb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAdd16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; addw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAdd32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; addl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicAdd64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; addq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline void mmAtomicSub8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; subb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicSub16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; subw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicSub32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; subl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSub64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; subq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicAddTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAddTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline int mmAtomicSubTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicSubTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicInc8( mmAtomic8 *v ) +{ + __asm__ __volatile__( + "lock ; incb %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicInc16( mmAtomic16 *v ) +{ + __asm__ __volatile__( + "lock ; incw %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicInc32( mmAtomic32 *v ) +{ + __asm__ __volatile__( + "lock ; incl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicInc64( mmAtomic64 *v ) +{ + __asm__ __volatile__( + "lock ; incq %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline void mmAtomicDec8( mmAtomic8 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicDec16( mmAtomic16 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +static inline void mmAtomicDec32( mmAtomic32 *v ) +{ + __asm__ __volatile__( + "lock ; decl %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicDec64( mmAtomic64 *v ) +{ + __asm__ __volatile__( + "lock ; decq %0" + :"=m"(v->value) + :"m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicIncTestZero8( mmAtomic8 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incb %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicIncTestZero16( mmAtomic16 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incw %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicIncTestZero32( mmAtomic32 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incl %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicIncTestZero64( mmAtomic64 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; incq %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} +#endif + + +//// + + +static inline int mmAtomicDecTestZero8( mmAtomic8 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decb %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicDecTestZero16( mmAtomic16 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decw %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +static inline int mmAtomicDecTestZero32( mmAtomic32 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decl %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicDecTestZero64( mmAtomic64 *v ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; decq %0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"m"(v->value) :"memory" ); + return c != 0; +} +#endif + + +//// + + +static inline int mmAtomicAddTestNegative8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addb %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestNegative16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addw %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAddTestNegative32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addl %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAddTestNegative64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; addq %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline int mmAtomicSubTestNegative8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subb %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestNegative16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subw %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicSubTestNegative32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subl %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicSubTestNegative64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; subq %2,%0 ; sets %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + + +//////////////// + + + +static inline void mmAtomicAnd8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; andb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAnd16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; andw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicAnd32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; andl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicAnd64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; andq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicAndTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAndTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicAndTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicAndTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; andq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicOr8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; orb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicOr16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; orw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicOr32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; orl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicOr64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; orq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicOrTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicOrTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicOrTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicOrTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; orq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + +//// + + +static inline void mmAtomicXor8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xorb %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicXor16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xorw %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +static inline void mmAtomicXor32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xorl %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicXor64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xorq %1,%0" + :"=m"(v->value) + :"ir"(i), "m"(v->value) :"memory" ); +} +#endif + + +//// + + +static inline int mmAtomicXorTestZero8( mmAtomic8 *v, int8_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorb %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicXorTestZero16( mmAtomic16 *v, int16_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorw %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +static inline int mmAtomicXorTestZero32( mmAtomic32 *v, int32_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorl %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicXorTestZero64( mmAtomic64 *v, int64_t i ) +{ + unsigned char c; + __asm__ __volatile__( + "lock ; xorq %2,%0 ; setz %1" + :"=m"(v->value), "=qm"(c) + :"ir"(i), "m"(v->value) :"memory" ); + return c; +} +#endif + + + +//////////////// + + + +static inline int8_t mmAtomicXchg8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "lock ; xchgb %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +static inline int16_t mmAtomicXchg16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "lock ; xchgw %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +static inline int32_t mmAtomicXchg32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "lock ; xchgl %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicXchg64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "lock ; xchgq %0,%1" + :"=q"(i) + :"m"(v->value), "0"(i) :"memory" ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicCmpXchg8( mmAtomic8 *v, int8_t old, int8_t n ) +{ + int8_t prev; + __asm__ __volatile__( + "lock ; cmpxchgb %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +static inline int16_t mmAtomicCmpXchg16( mmAtomic16 *v, int16_t old, int16_t n ) +{ + int16_t prev; + __asm__ __volatile__( + "lock ; cmpxchgw %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +static inline int32_t mmAtomicCmpXchg32( mmAtomic32 *v, int32_t old, int32_t n ) +{ + int32_t prev; + __asm__ __volatile__( + "lock ; cmpxchgl %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicCmpXchg64( mmAtomic64 *v, int64_t old, int64_t n ) +{ + int64_t prev; + __asm__ __volatile__( + "lock ; cmpxchgq %1,%2" + :"=a"(prev) + :"r"(n), "m"(v->value), "a"(old) :"memory" ); + return prev; +} +#endif + + + +//////////////// + + + +static inline void mmAtomicPause() +{ + __asm__ __volatile__( + "rep ; nop" + : + ::"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpb %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpw %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitEq32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpl %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpinWaitEq64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpq %1,%0\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} +#endif + + +static inline int32_t mmAtomicSpinWaitEq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpb %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitEq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpw %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitEq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpl %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int32_t mmAtomicSpinWaitEq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpq %2,%1\n" + "jnz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} +#endif + + +static inline void mmAtomicSpinWaitNeq8( mmAtomic8 *v, int8_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpb %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitNeq16( mmAtomic16 *v, int16_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpw %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +static inline void mmAtomicSpinWaitNeq32( mmAtomic32 *v, int32_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpl %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpinWaitNeq64( mmAtomic64 *v, int64_t i ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "rep ; nop\n" + "1:\n" + "cmpq %1,%0\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + : + :"m"(v->value), "r"(i) :"memory" ); + return; +} +#endif + + +static inline int32_t mmAtomicSpinWaitNeq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpb %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitNeq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpw %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +static inline int32_t mmAtomicSpinWaitNeq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpl %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int32_t mmAtomicSpinWaitNeq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount ) +{ + __asm__ __volatile__( + "jmp 1f\n" + ".p2align 6\n" + "2:\n" + "subl $1,%0\n" + "jz 3f\n" + "rep ; nop\n" + "1:\n" + "cmpq %2,%1\n" + "jz 2b\n" + ".p2align 4\n" + "3:\n" + :"=q"(spinmaxcount) + :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" ); + return spinmaxcount; +} +#endif + + +//// + + +static inline void mmAtomicSpin8( mmAtomic8 *v, int8_t old, int8_t n ) +{ + for( ; mmAtomicCmpXchg8( v, old, n ) != old ; ) + { + for( ; mmAtomicRead8( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +static inline void mmAtomicSpin16( mmAtomic16 *v, int16_t old, int16_t n ) +{ + for( ; mmAtomicCmpXchg16( v, old, n ) != old ; ) + { + for( ; mmAtomicRead16( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +static inline void mmAtomicSpin32( mmAtomic32 *v, int32_t old, int32_t n ) +{ + for( ; mmAtomicCmpXchg32( v, old, n ) != old ; ) + { + for( ; mmAtomicRead32( v ) != old ; ) + mmAtomicPause(); + } + return; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline void mmAtomicSpin64( mmAtomic64 *v, int64_t old, int64_t n ) +{ + for( ; mmAtomicCmpXchg64( v, old, n ) != old ; ) + { + for( ; mmAtomicRead64( v ) != old ; ) + mmAtomicPause(); + } + return; +} +#endif + + +static inline int mmAtomicTrySpin8( mmAtomic8 *v, int8_t old, int8_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg8( v, old, n ) != old ; ) + { + for( ; mmAtomicRead8( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +static inline int mmAtomicTrySpin16( mmAtomic16 *v, int16_t old, int16_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg16( v, old, n ) != old ; ) + { + for( ; mmAtomicRead16( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +static inline int mmAtomicTrySpin32( mmAtomic32 *v, int32_t old, int32_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg32( v, old, n ) != old ; ) + { + for( ; mmAtomicRead32( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int mmAtomicTrySpin64( mmAtomic64 *v, int64_t old, int64_t n, int spincount ) +{ + for( ; mmAtomicCmpXchg64( v, old, n ) != old ; ) + { + for( ; mmAtomicRead64( v ) != old ; ) + { + if( !( --spincount ) ) + return 0; + mmAtomicPause(); + } + } + return 1; +} +#endif + + +//////////////// + + +static inline int8_t mmAtomicAddRead8( mmAtomic8 *v, int8_t add ) +{ + int8_t i; + do + { + i = mmAtomicRead8( v ); + } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i ); + return (int8_t)(i + add); +} + +static inline int16_t mmAtomicAddRead16( mmAtomic16 *v, int16_t add ) +{ + int16_t i; + do + { + i = mmAtomicRead16( v ); + } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i ); + return (int16_t)(i + add); +} + +static inline int32_t mmAtomicAddRead32( mmAtomic32 *v, int32_t add ) +{ + int32_t i; + do + { + i = mmAtomicRead32( v ); + } while( mmAtomicCmpXchg32( v, i, i + add ) != i ); + return i + add; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicAddRead64( mmAtomic64 *v, int64_t add ) +{ + int64_t i; + do + { + i = mmAtomicRead64( v ); + } while( mmAtomicCmpXchg64( v, i, i + add ) != i ); + return i + add; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAdd8( mmAtomic8 *v, int8_t add ) +{ + int8_t i; + do + { + i = mmAtomicRead8( v ); + } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAdd16( mmAtomic16 *v, int16_t add ) +{ + int16_t i; + do + { + i = mmAtomicRead16( v ); + } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAdd32( mmAtomic32 *v, int32_t add ) +{ + int32_t i; + do + { + i = mmAtomicRead32( v ); + } while( mmAtomicCmpXchg32( v, i, i + add ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAdd64( mmAtomic64 *v, int64_t add ) +{ + int64_t i; + do + { + i = mmAtomicRead64( v ); + } while( mmAtomicCmpXchg64( v, i, i + add ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAnd8( mmAtomic8 *v, int8_t mask ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = i & mask; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAnd16( mmAtomic16 *v, int16_t mask ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = i & mask; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAnd32( mmAtomic32 *v, int32_t mask ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i & mask; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAnd64( mmAtomic64 *v, int64_t mask ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i & mask; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadOr8( mmAtomic8 *v, int8_t mask ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = i | mask; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadOr16( mmAtomic16 *v, int16_t mask ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = i | mask; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadOr32( mmAtomic32 *v, int32_t mask ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i | mask; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadOr64( mmAtomic64 *v, int64_t mask ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i | mask; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadIncLoop8( mmAtomic8 *v, int8_t max ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = (int8_t)(i + 1); + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadIncLoop16( mmAtomic16 *v, int16_t max ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = (int8_t)(i + 1); + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadIncLoop32( mmAtomic32 *v, int32_t max ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i + 1; + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadIncLoop64( mmAtomic64 *v, int64_t max ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i + 1; + if( j >= max ) + j = 0; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + +//// + + +static inline int8_t mmAtomicReadAddLoop8( mmAtomic8 *v, int8_t add, int8_t base, int8_t max ) +{ + int8_t i, j; + do + { + i = mmAtomicRead8( v ); + j = (int8_t)(i + add); + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg8( v, i, j ) != i ); + return i; +} + +static inline int16_t mmAtomicReadAddLoop16( mmAtomic16 *v, int16_t add, int16_t base, int16_t max ) +{ + int16_t i, j; + do + { + i = mmAtomicRead16( v ); + j = (int16_t)(i + add); + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg16( v, i, j ) != i ); + return i; +} + +static inline int32_t mmAtomicReadAddLoop32( mmAtomic32 *v, int32_t add, int32_t base, int32_t max ) +{ + int32_t i, j; + do + { + i = mmAtomicRead32( v ); + j = i + add; + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg32( v, i, j ) != i ); + return i; +} + +#ifdef MM_ATOMIC_64_BITS_SUPPORT +static inline int64_t mmAtomicReadAddLoop64( mmAtomic64 *v, int64_t add, int64_t base, int64_t max ) +{ + int64_t i, j; + do + { + i = mmAtomicRead64( v ); + j = i + add; + if( j >= max ) + j = base; + } while( mmAtomicCmpXchg64( v, i, j ) != i ); + return i; +} +#endif + + + +//////////////// + + + + +#define mmAtomicCmpReplace8(v,old,new) (mmAtomicCmpXchg8(v,old,new)==(old)) +#define mmAtomicCmpReplace16(v,old,new) (mmAtomicCmpXchg16(v,old,new)==(old)) +#define mmAtomicCmpReplace32(v,old,new) (mmAtomicCmpXchg32(v,old,new)==(old)) +#define mmAtomicCmpReplace64(v,old,new) (mmAtomicCmpXchg64(v,old,new)==(old)) + + +#if CPUCONF_POINTER_BITS == 64 + #define mmAtomicP mmAtomic64 + #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_64(v) + #define mmAtomicReadP(v) (void *)mmAtomicRead64(v) + #define mmAtomicWriteP(v,i) mmAtomicWrite64(v,(int64_t)i) + #define mmAtomicAddP(v,i) mmAtomicAdd64(v,(int64_t)i) + #define mmAtomicSubP(v,i) mmAtomicSub64(v,(int64_t)i) + #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero64(v,(int64_t)i) + #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero64(v,(int64_t)i) + #define mmAtomicIncP(v) mmAtomicInc64(v) + #define mmAtomicDecP(v) mmAtomicDec64(v) + #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero64(v) + #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero64(v) + #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative64(v,(int64_t)i) + #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative64(v,(int64_t)i) + #define mmAtomicAndP(v,i) mmAtomicAnd64(v,(int64_t)i) + #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero64(v,(int64_t)i) + #define mmAtomicOrP(v,i) mmAtomicOr64(v,(int64_t)i) + #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero64(v,(int64_t)i) + #define mmAtomicXorP(v,i) mmAtomicXor64(v,(int64_t)i) + #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero64(v,(int64_t)i) + #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg64(v,(int64_t)i) + #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg64(v,(int64_t)i,(int64_t)j) + #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace64(v,(int64_t)i,(int64_t)j) + #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin64(v,(int64_t)i,(int64_t)j) + #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead64(v,(int64_t)i) +#elif CPUCONF_POINTER_BITS == 32 + #define mmAtomicP mmAtomic32 + #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_32(v) + #define mmAtomicReadP(v) (void *)mmAtomicRead32(v) + #define mmAtomicWriteP(v,i) mmAtomicWrite32(v,(int32_t)i) + #define mmAtomicAddP(v,i) mmAtomicAdd32(v,(int32_t)i) + #define mmAtomicSubP(v,i) mmAtomicSub32(v,(int32_t)i) + #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero32(v,(int32_t)i) + #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero32(v,(int32_t)i) + #define mmAtomicIncP(v) mmAtomicInc32(v) + #define mmAtomicDecP(v) mmAtomicDec32(v) + #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero32(v) + #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero32(v) + #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative32(v,(int32_t)i) + #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative32(v,(int32_t)i) + #define mmAtomicAndP(v,i) mmAtomicAnd32(v,(int32_t)i) + #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero32(v,(int32_t)i) + #define mmAtomicOrP(v,i) mmAtomicOr32(v,(int32_t)i) + #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero32(v,(int32_t)i) + #define mmAtomicXorP(v,i) mmAtomicXor32(v,(int32_t)i) + #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero32(v,(int32_t)i) + #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg32(v,(int32_t)i) + #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg32(v,(int32_t)i,(int32_t)j) + #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace32(v,(int32_t)i,(int32_t)j) + #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin32(v,(int32_t)i,(int32_t)j) + #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead32(v,(int32_t)i) +#else + #error CPUCONF_POINTER_BITS undefined +#endif + +#ifdef MM_ATOMIC_64_BITS_SUPPORT + #define intlarge int64_t + #define uintlarge uint64_t + #define mmAtomicL mmAtomic64 + #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_64(v) + #define mmAtomicReadL(v) mmAtomicRead64(v) + #define mmAtomicWriteL(v,i) mmAtomicWrite64(v,i) + #define mmAtomicAddL(v,i) mmAtomicAdd64(v,i) + #define mmAtomicSubL(v,i) mmAtomicSub64(v,i) + #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero64(v,i) + #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero64(v,i) + #define mmAtomicIncL(v) mmAtomicInc64(v) + #define mmAtomicDecL(v) mmAtomicDec64(v) + #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero64(v) + #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero64(v) + #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative64(v,i) + #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative64(v,i) + #define mmAtomicAndL(v,i) mmAtomicAnd64(v,i) + #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero64(v,i) + #define mmAtomicOrL(v,i) mmAtomicOr64(v,i) + #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero64(v,i) + #define mmAtomicXorL(v,i) mmAtomicXor64(v,i) + #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero64(v,i) + #define mmAtomicXchgL(v,i) mmAtomicXchg64(v,i) + #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg64(v,i,j) + #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace64(v,i,j) + #define mmAtomicSpinL(v,i,j) mmAtomicSpin64(v,i,j) + #define mmAtomicAddReadL(v,i) mmAtomicAddRead64(v,(int64_t)i) +#else + #define intlarge int32_t + #define uintlarge uint32_t + #define mmAtomicL mmAtomic32 + #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_32(v) + #define mmAtomicReadL(v) mmAtomicRead32(v) + #define mmAtomicWriteL(v,i) mmAtomicWrite32(v,i) + #define mmAtomicAddL(v,i) mmAtomicAdd32(v,i) + #define mmAtomicSubL(v,i) mmAtomicSub32(v,i) + #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero32(v,i) + #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero32(v,i) + #define mmAtomicIncL(v) mmAtomicInc32(v) + #define mmAtomicDecL(v) mmAtomicDec32(v) + #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero32(v) + #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero32(v) + #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative32(v,i) + #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative32(v,i) + #define mmAtomicAndL(v,i) mmAtomicAnd32(v,i) + #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero32(v,i) + #define mmAtomicOrL(v,i) mmAtomicOr32(v,i) + #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero32(v,i) + #define mmAtomicXorL(v,i) mmAtomicXor32(v,i) + #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero32(v,i) + #define mmAtomicXchgL(v,i) mmAtomicXchg32(v,i) + #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg32(v,i,j) + #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace32(v,i,j) + #define mmAtomicSpinL(v,i,j) mmAtomicSpin32(v,i,j) + #define mmAtomicAddReadL(v,i) mmAtomicAddRead32(v,(int32_t)i) +#endif + + + +//////////////// + + + +typedef struct { mmAtomic8 v; } mmAtomicLock8; + +#define MM_ATOMIC_LOCK8_WRITE (-((int8_t)0x7f)) + +static inline void mmAtomicLockInit8( mmAtomicLock8 *v ) +{ + mmAtomicWrite8( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead8( mmAtomicLock8 *v ) +{ + if( mmAtomicAddTestNegative8( &v->v, 1 ) ) + { + mmAtomicAdd8( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite8( mmAtomicLock8 *v ) +{ + if( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead8( mmAtomicLock8 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead8( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) ) + break; + mmAtomicAdd8( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite8( mmAtomicLock8 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead8( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead8( mmAtomicLock8 *v, int spincount ) +{ + do + { + if( mmAtomicRead8( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd8( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite8( mmAtomicLock8 *v, int spincount ) +{ + do + { + if( mmAtomicRead8( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead8( mmAtomicLock8 *v ) +{ + mmAtomicAdd8( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite8( mmAtomicLock8 *v ) +{ + mmAtomicAdd8( &v->v, -MM_ATOMIC_LOCK8_WRITE ); + return; +} + + +//// + + +typedef struct { mmAtomic16 v; } mmAtomicLock16; + +#define MM_ATOMIC_LOCK16_WRITE (-((int16_t)0x7fff)) + +static inline void mmAtomicLockInit16( mmAtomicLock16 *v ) +{ + mmAtomicWrite16( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead16( mmAtomicLock16 *v ) +{ + if( mmAtomicAddTestNegative16( &v->v, 1 ) ) + { + mmAtomicAdd16( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite16( mmAtomicLock16 *v ) +{ + if( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead16( mmAtomicLock16 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead16( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) ) + break; + mmAtomicAdd16( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite16( mmAtomicLock16 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead16( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead16( mmAtomicLock16 *v, int spincount ) +{ + do + { + if( mmAtomicRead16( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd16( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite16( mmAtomicLock16 *v, int spincount ) +{ + do + { + if( mmAtomicRead16( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead16( mmAtomicLock16 *v ) +{ + mmAtomicAdd16( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite16( mmAtomicLock16 *v ) +{ + mmAtomicAdd16( &v->v, -MM_ATOMIC_LOCK16_WRITE ); + return; +} + + +//// + + +typedef struct { mmAtomic32 v; } mmAtomicLock32; + +/* +#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x7fffffff)) +*/ +#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x10000000)) + +static inline void mmAtomicLockInit32( mmAtomicLock32 *v ) +{ + mmAtomicWrite32( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead32( mmAtomicLock32 *v ) +{ + if( mmAtomicAddTestNegative32( &v->v, 1 ) ) + { + mmAtomicAdd32( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite32( mmAtomicLock32 *v ) +{ + if( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead32( mmAtomicLock32 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead32( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) ) + break; + mmAtomicAdd32( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite32( mmAtomicLock32 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead32( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead32( mmAtomicLock32 *v, int spincount ) +{ + do + { + if( mmAtomicRead32( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd32( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite32( mmAtomicLock32 *v, int spincount ) +{ + do + { + if( mmAtomicRead32( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead32( mmAtomicLock32 *v ) +{ + mmAtomicAdd32( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite32( mmAtomicLock32 *v ) +{ + mmAtomicAdd32( &v->v, -MM_ATOMIC_LOCK32_WRITE ); + return; +} + + +//// + + +#ifdef MM_ATOMIC_64_BITS_SUPPORT + +typedef struct { mmAtomic64 v; } mmAtomicLock64; + +#define MM_ATOMIC_LOCK64_WRITE (-((int64_t)0x7fffffffffffffff)) + +static inline void mmAtomicLockInit64( mmAtomicLock64 *v ) +{ + mmAtomicWrite64( &v->v, 0 ); + return; +} + +static inline int mmAtomicLockAttemptRead64( mmAtomicLock64 *v ) +{ + if( mmAtomicAddTestNegative64( &v->v, 1 ) ) + { + mmAtomicAdd64( &v->v, -1 ); + return 0; + } + return 1; +} + +static inline int mmAtomicLockAttemptWrite64( mmAtomicLock64 *v ) +{ + if( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) + return 0; + return 1; +} + +static inline void mmAtomicLockSpinRead64( mmAtomicLock64 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead64( &v->v ) < 0 ; ) + mmAtomicPause(); + if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) ) + break; + mmAtomicAdd64( &v->v, -1 ); + } + return; +} + +static inline void mmAtomicLockSpinWrite64( mmAtomicLock64 *v ) +{ + for( ; ; ) + { + for( ; mmAtomicRead64( &v->v ) ; ) + mmAtomicPause(); + if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) ) + break; + } + return; +} + +static inline int mmAtomicLockTryRead64( mmAtomicLock64 *v, int spincount ) +{ + do + { + if( mmAtomicRead64( &v->v ) < 0 ) + mmAtomicPause(); + else + { + if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) ) + return 1; + mmAtomicAdd64( &v->v, -1 ); + } + } while( --spincount ); + return 0; +} + +static inline int mmAtomicLockTryWrite64( mmAtomicLock64 *v, int spincount ) +{ + do + { + if( mmAtomicRead64( &v->v ) ) + mmAtomicPause(); + else + { + if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) ) + return 1; + } + } while( --spincount ); + return 0; +} + +static inline void mmAtomicLockDoneRead64( mmAtomicLock64 *v ) +{ + mmAtomicAdd64( &v->v, -1 ); + return; +} + +static inline void mmAtomicLockDoneWrite64( mmAtomicLock64 *v ) +{ + mmAtomicAdd64( &v->v, -MM_ATOMIC_LOCK64_WRITE ); + return; +} + +#endif + + + +//////////////// + + + +#define MM_ATOMIC_LIST_BUSY ((void *)0x1) + +typedef struct +{ + mmAtomicP prev; /* mmAtomicP* to &(prev->next) */ + mmAtomicP next; /* void* to next */ + mmAtomic32 status; +} mmAtomicListNode; + +#define MM_ATOMIC_LIST_VALID (0x0) +#define MM_ATOMIC_LIST_DELETED (0x1) + +typedef struct +{ + mmAtomicP first; /* void* to item */ + mmAtomicP last; /* mmAtomicP* to &(lastitem->next) */ +} mmAtomicListDualHead; + + +void mmAtomicListAdd( mmAtomicP *list, void *item, intptr_t offset ); +void mmAtomicListRemove( void *item, intptr_t offset ); + +static inline void *mmAtomicListFirst( mmAtomicP *head ) +{ + void *item; + for( ; ( item = mmAtomicReadP( head ) ) == MM_ATOMIC_LIST_BUSY ; ) + mmAtomicPause(); + return item; +} + +static inline void *mmAtomicListNext( mmAtomicListNode *node ) +{ + void *item; + for( ; ; ) + { + item = mmAtomicReadP( &node->next ); + if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED ) + return 0; + /* At the time we have read node->next, the node has not been deleted yet */ + if( item != MM_ATOMIC_LIST_BUSY ) + break; + mmAtomicPause(); + } + return item; +} + + +void mmAtomicListDualInit( mmAtomicListDualHead *head ); +void mmAtomicListDualAddFirst( mmAtomicListDualHead *head, void *item, intptr_t offset ); +void mmAtomicListDualAddLast( mmAtomicListDualHead *head, void *item, intptr_t offset ); +void mmAtomicListDualRemove( mmAtomicListDualHead *head, void *item, intptr_t offset ); + +static inline void *mmAtomicListDualFirst( mmAtomicListDualHead *head ) +{ + void *item; + for( ; ( item = mmAtomicReadP( &head->first ) ) == MM_ATOMIC_LIST_BUSY ; ) + mmAtomicPause(); + return item; +} + +static inline void *mmAtomicListDualNext( mmAtomicListNode *node ) +{ + void *item; + for( ; ; ) + { + item = mmAtomicReadP( &node->next ); + if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED ) + return 0; + /* At the time we have read node->next, the node has not been deleted yet */ + if( item != MM_ATOMIC_LIST_BUSY ) + break; + mmAtomicPause(); + } + return item; +} + + + +//////////////// + + + +/* +#define MM_ATOMIC_BARRIER_DEBUG +*/ + + +#define MM_ATOMIC_BARRIER_DELAYED_RESET + + +typedef struct +{ + int32_t clearcounter; + int32_t yieldcounter; +} mmAtomicBarrierStat; + +typedef struct +{ + mmAtomic32 flag MM_CACHE_ALIGN; + mmAtomic32 counter MM_CACHE_ALIGN; + volatile int32_t flagref MM_CACHE_ALIGN; + void *parent MM_CACHE_ALIGN; + int32_t resetvalue; +} mmAtomicBarrier; + +void mmAtomicBarrierBuild( mmAtomicBarrier *barrier, int childcount, mmAtomicBarrier *parent ); +int mmAtomicBarrierWait( mmAtomicBarrier *barrier, int32_t spinwaitcounter, mmAtomicBarrierStat *barrierstat ); + + + + +//// + + + +typedef struct +{ + mmAtomic32 counter MM_CACHE_ALIGN; + /* Data below remains constant */ + void *parent MM_CACHE_ALIGN; + int32_t resetvalue; +} mmAtomicCounterNode; + +typedef struct +{ + int32_t lockcount; + int32_t nodecount; + mmAtomicCounterNode *nodearray; + mmAtomicCounterNode **locknode; +} mmAtomicCounter; + +void mmAtomicCounterInit( mmAtomicCounter *counter, int lockcount, int stagesize ); +void mmAtomicCounterDestroy( mmAtomicCounter *counter ); +int mmAtomicCounterHit( mmAtomicCounter *counter, int lockindex ); + + + +//// + + +/* +Stockpile callbacks for freeing items + +*/ + +typedef struct +{ + mmAtomicP p; + + +} mmAtomicRcu; + +static inline void mmAtomicRcuEnter( mmAtomicRcu *rcu ) +{ + /* On archs with messed up memory models like Alpha, we would need some memory barrier here */ + return; +} + +static inline void mmAtomicRcuLeave( mmAtomicRcu *rcu ) +{ + /* On archs with messed up memory models like Alpha, we would need some memory barrier here */ + return; +} + + +/* +To reiterate, synchronize_rcu() waits only for ongoing RCU read-side critical sections to complete, +not necessarily for any that begin after synchronize_rcu() is invoked. +*/ +static inline void mmAtomicRcuSync() +{ + +} + +static inline void mmAtomicRcuRead() +{ + + +} + + +static inline void mmAtomicRcuWrite() +{ + + +} + + + +#endif + + diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.c b/ecere/src/gfx/newFonts/cc/mmbitmap.c new file mode 100644 index 0000000..dcd2bf0 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmbitmap.c @@ -0,0 +1,334 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "cpuconfig.h" +#include "cc.h" +#include "mm.h" +#include "mmbitmap.h" + + +/* Friendlier to cache on SMP systems */ +#define BP_BITMAP_PREWRITE_CHECK + + +/* +TODO +If we don't have atomic instruction support somehow, we need a much better mutex locking mechanism!! +*/ + + +int mmBitMapInit( mmBitMap *bitmap, size_t entrycount, int initvalue ) +{ + size_t mapsize, index; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + mapsize = ( entrycount + CPUCONF_LONG_BITS - 1 ) >> CPUCONF_LONG_BITSHIFT; + bitmap->map = 0; + if( mapsize ) + { + if( !( bitmap->map = malloc( mapsize * sizeof(mmAtomicL) ) ) ) + return 0; + } + bitmap->mapsize = mapsize; + bitmap->entrycount = entrycount; + + map = bitmap->map; + value = ( initvalue & 0x1 ? ~0x0 : 0x0 ); +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < mapsize ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < mapsize ; index++ ) + map[index] = value; + mtMutexInit( &bitmap->mutex ); +#endif + + return 1; +} + +void mmBitMapReset( mmBitMap *bitmap, int resetvalue ) +{ + size_t index; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + map = bitmap->map; + value = ( resetvalue & 0x1 ? ~(long)0x0 : (long)0x0 ); +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < bitmap->mapsize ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < bitmap->mapsize ; index++ ) + map[index] = value; +#endif + + return; +} + +void mmBitMapResetRange( mmBitMap *bitmap, int minimumentrycount, int resetvalue ) +{ + size_t index, entrycount; + long value; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; +#endif + + map = bitmap->map; + value = ( resetvalue & 0x1 ? ~(long)0x0 : (long)0x0 ); + entrycount = ( minimumentrycount + CPUCONF_LONG_BITS - 1 ) >> CPUCONF_LONG_BITSHIFT; +#ifdef MM_ATOMIC_SUPPORT + for( index = 0 ; index < entrycount ; index++ ) + mmAtomicWriteL( &map[index], value ); +#else + for( index = 0 ; index < entrycount ; index++ ) + map[index] = value; +#endif + + return; +} + +void mmBitMapFree( mmBitMap *bitmap ) +{ + free( bitmap->map ); + bitmap->map = 0; + bitmap->mapsize = 0; +#ifndef MM_ATOMIC_SUPPORT + mtMutexDestroy( &bitmap->mutex ); +#endif + return; +} + +/* TODO: Yeah... That code was written in one go, maybe I should test if it's working fine, just in case? */ +int mmBitMapFindSet( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ) +{ + unsigned long value; + size_t index, shift, shiftbase, shiftmax, indexlast, shiftlast; + + if( !( bitmap->entrycount ) ) + return 0; + + indexlast = entryindexlast >> CPUCONF_LONG_BITSHIFT; + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); + + /* Leading bits search */ + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shiftbase = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + mtMutexLock( &bitmap->mutex ); + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + shiftmax = CPUCONF_LONG_BITS-1; + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + shiftmax = shiftlast; + value >>= shiftbase; + for( shift = shiftbase ; shift <= shiftmax ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + { +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + return 0; + } + + /* Main search */ + for( ; ; ) + { + index = ( index + 1 ) % bitmap->mapsize; + if( index == indexlast ) + break; +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + for( shift = 0 ; ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + } + + /* Trailing bits search */ + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != (unsigned long)0x0 ) + { + for( shift = 0 ; shift < shiftlast ; shift++, value >>= 1 ) + { + if( !( value & 0x1 ) ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + + return 0; +} + +int mmBitMapFindClear( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ) +{ + unsigned long value; + size_t index, shift, shiftbase, shiftmax, indexlast, shiftlast; + + if( !( bitmap->entrycount ) ) + return 0; + + indexlast = entryindexlast >> CPUCONF_LONG_BITSHIFT; + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); + + /* Leading bits search */ + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shiftbase = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + mtMutexLock( &bitmap->mutex ); + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + shiftmax = CPUCONF_LONG_BITS-1; + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + shiftmax = shiftlast; + value >>= shiftbase; + for( shift = shiftbase ; shift <= shiftmax ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + if( ( index == indexlast ) && ( shiftlast > shiftbase ) ) + { +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + return 0; + } + + /* Main search */ + for( ; ; ) + { + index = ( index + 1 ) % bitmap->mapsize; + if( index == indexlast ) + break; +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + for( shift = 0 ; ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } + } + + /* Trailing bits search */ + shiftlast = entryindexlast & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = mmAtomicReadL( &bitmap->map[index] ); +#else + value = bitmap->map[index]; +#endif + if( value != ~(unsigned long)0x0 ) + { + for( shift = 0 ; shift <= shiftlast ; shift++, value >>= 1 ) + { + if( value & 0x1 ) + continue; + entryindex = ( index << CPUCONF_LONG_BITSHIFT ) | shift; + if( entryindex >= bitmap->entrycount ) + break; +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + *retentryindex = entryindex; + return 1; + } + } +#ifndef MM_ATOMIC_SUPPORT + mtMutexUnlock( &bitmap->mutex ); +#endif + + return 0; +} diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.h b/ecere/src/gfx/newFonts/cc/mmbitmap.h new file mode 100644 index 0000000..75ba883 --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmbitmap.h @@ -0,0 +1,211 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +#include "cpuconfig.h" +#include "mm.h" + +#if !defined(CPUCONF_LONG_BITSHIFT) || !defined(CPUCONF_LONG_BITS) + #error Preprocessor symbols CPUCONF_LONG_BITSHIFT and CPUCONF_LONG_BITS are undefined! + #error This header requires cpuconfig.h +#endif + +#if !defined(MM_ATOMIC_SUPPORT) + #warning Compiling mmbitmap without atomic support, it is going to be SLOW. + #warning This header requires mm.h +#endif + + +typedef struct +{ + size_t entrycount; + size_t mapsize; +#ifdef MM_ATOMIC_SUPPORT + mmAtomicL *map; +#else + long *map; + mtMutex mutex; +#endif +} mmBitMap; + +int mmBitMapInit( mmBitMap *bitmap, size_t entrycount, int initvalue ); +void mmBitMapReset( mmBitMap *bitmap, int resetvalue ); +void mmBitMapResetRange( mmBitMap *bitmap, int minimumentrycount, int resetvalue ); +void mmBitMapFree( mmBitMap *bitmap ); + +int mmBitMapFindSet( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ); +int mmBitMapFindClear( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex ); + + +//// + + +/* No atomic locking, single-threaded access */ + +static inline int mmBitMapDirectGet( mmBitMap *bitmap, size_t entryindex ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & 0x1); +#else + value = ( bitmap->map[index] >> shift ) & 0x1; +#endif + return value; +} + +static inline void mmBitMapDirectSet( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) |= (long)1 << shift; +#else + bitmap->map[index] |= (long)1 << shift; +#endif + return; +} + +static inline void mmBitMapDirectClear( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) &= ~( (long)1 << shift ); +#else + bitmap->map[index] &= ~( (long)1 << shift ); +#endif + return; +} + +static inline int mmBitMapDirectMaskGet( mmBitMap *bitmap, size_t entryindex, long mask ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & mask); +#else + value = ( bitmap->map[index] >> shift ) & mask; +#endif + return value; +} + +static inline void mmBitMapDirectMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) = ( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) & ~( mask << shift ) ) | ( value << shift ); +#else + bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift ); +#endif + return; +} + + +//// + + +/* Atomic locking, multi-threaded access */ + +static inline int mmBitMapGet( mmBitMap *bitmap, size_t entryindex ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & 0x1); +#else + mtMutexLock( &bitmap->mutex ); + value = ( bitmap->map[index] >> shift ) & 0x1; + mtMutexUnlock( &bitmap->mutex ); +#endif + return value; +} + +static inline void mmBitMapSet( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + #ifdef BP_BITMAP_PREWRITE_CHECK + if( !( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) ) ) + mmAtomicOrL( &bitmap->map[index], (long)1 << shift ); + #else + mmAtomicOrL( &bitmap->map[index], (long)1 << shift ); + #endif +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] |= (long)1 << shift; + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} + +static inline void mmBitMapClear( mmBitMap *bitmap, size_t entryindex ) +{ + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + #ifdef BP_BITMAP_PREWRITE_CHECK + if( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) ) + mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) ); + #else + mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) ); + #endif +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] &= ~( (long)1 << shift ); + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} + +static inline int mmBitMapMaskGet( mmBitMap *bitmap, size_t entryindex, long mask ) +{ + int value; + size_t index, shift; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & mask); +#else + mtMutexLock( &bitmap->mutex ); + value = ( bitmap->map[index] >> shift ) & mask; + mtMutexUnlock( &bitmap->mutex ); +#endif + return value; +} + +static inline void mmBitMapMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask ) +{ + size_t index, shift; + long oldvalue, newvalue; + index = entryindex >> CPUCONF_LONG_BITSHIFT; + shift = entryindex & ( CPUCONF_LONG_BITS - 1 ); +#ifdef MM_ATOMIC_SUPPORT + for( ; ; ) + { + oldvalue = (int)mmAtomicReadL( &bitmap->map[index] ); + newvalue = ( oldvalue & ~( mask << shift ) ) | ( value << shift ); + if( mmAtomicCmpReplaceL( &bitmap->map[index], oldvalue, newvalue ) ) + break; + } +#else + mtMutexLock( &bitmap->mutex ); + bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift ); + mtMutexUnlock( &bitmap->mutex ); +#endif + return; +} diff --git a/ecere/src/gfx/newFonts/cc/mmthread.h b/ecere/src/gfx/newFonts/cc/mmthread.h new file mode 100644 index 0000000..71f07ab --- /dev/null +++ b/ecere/src/gfx/newFonts/cc/mmthread.h @@ -0,0 +1,530 @@ +/* ***************************************************************************** + * Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +/** + * @file + * + * Threading interface, POSIX implementation. + */ + + + +#if defined(COMPILE_FOR_VSL) + #define MT_QT +#endif + + + +#if defined(MT_QT) + + #include "mmthreadqt.h" + +#elif defined(MT_DISABLED) + +struct mtNull +{ +}; + +typedef struct mtNull mtMutex; +typedef struct mtNull mtSpin; +typedef struct mtNull mtSignal; + + #define mtMutexInit(a) + #define mtMutexDestroy(a) + #define mtMutexLock(a) + #define mtMutexUnlock(a) + #define mtMutexTryLock(a) + + #define mtSpinInit(a) + #define mtSpinDestroy(a) + #define mtSpinLock(a) + #define mtSpinUnlock(a) + #define mtSpinTryLock(a) + + #define mtSignalInit(a) + #define mtSignalDestroy(a) + #define mtSignalDispatch(a) + #define mtSignalBroadcast(a) + #define mtSignalMutexWait(a,b) + +#else + + #include + #include + #include + #include + + #if _POSIX_SPIN_LOCKS > 0 + #define MT_SPIN_LOCK_SUPPORT + #endif +/* + #define MT_BARRIER_SUPPORT + #define MT_RWLOCK_SUPPORT +*/ + + +static inline void mtYield() +{ + sched_yield(); + return; +} + + +typedef struct mtThread mtThread; + +struct mtThread +{ + pthread_t pthread; +}; + + +#define MT_THREAD_FLAGS_JOINABLE (0x1) +#define MT_THREAD_FLAGS_SETSTACK (0x2) + + +static inline void mtThreadCreate( mtThread *thread, void *(*threadmain)( void *value ), void *value, int flags, void *stack, size_t stacksize ) +{ + pthread_attr_t attr; + + pthread_attr_init( &attr ); +#if !MM_WIN32 && !MM_WIN64 + if( flags & MT_THREAD_FLAGS_SETSTACK ) + pthread_attr_setstack( &attr, stack, stacksize ); +#endif + if( flags & MT_THREAD_FLAGS_JOINABLE ) + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_JOINABLE ); + else + pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED ); + pthread_create( &thread->pthread, &attr, threadmain, value ); + pthread_attr_destroy( &attr ); + + return; +} + +static inline void mtThreadExit() +{ + pthread_exit( 0 ); + return; +} + +static inline void mtThreadJoin( mtThread *thread ) +{ + void *ret; + pthread_join( thread->pthread, &ret ); + return; +} + + +static inline mtThread mtThreadSelf() +{ + mtThread thread; + thread.pthread = pthread_self(); + return thread; +} + +static inline int mtThreadCmpEqual( mtThread *thread0, mtThread *thread1 ) +{ + return ( pthread_equal( thread0->pthread, thread1->pthread ) != 0 ? 1 : 0 ); +} + + +static inline size_t mtGetMinimumStackSize() +{ + #ifdef PTHREAD_STACK_MIN + return PTHREAD_STACK_MIN; + #else + return ( mmcontext.pagesize ? 4*mmcontext.pagesize : 16384 ); + #endif +} + + + + #ifdef MT_DEBUG + #ifndef DEBUG_WARNING() + #define DEBUG_WARNING() ({printf( "\nRF WARNING : %s %s %d\n", __FILE__, __FUNCTION__, __LINE__ ); fflush( stdout );}) + #endif + #endif + + + +typedef struct mtMutex mtMutex; + +struct mtMutex +{ +#ifdef MT_DEBUG + unsigned char *function; + unsigned char *file; + int line; +#endif + pthread_mutex_t pmutex; +}; + +static inline void mtMutexInit( mtMutex *mutex ) +{ + pthread_mutex_init( &mutex->pmutex, 0 ); + return; +} + +static inline void mtMutexDestroy( mtMutex *mutex ) +{ + pthread_mutex_destroy( &mutex->pmutex ); + return; +} + + #ifdef MT_DEBUG + + #define mtMutexLock(a) mtMutexLockDebug(a,__FUNCTION__,__FILE__,__LINE__) + #define mtMutexUnlock(a) mtMutexUnlockDebug(a,__FUNCTION__,__FILE__,__LINE__) + #define mtMutexTryLock(a) mtMutexTryLockDebug(a,__FUNCTION__,__FILE__,__LINE__) + +static inline void mtMutexLockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + printf( "Mutex lock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( pthread_mutex_trylock( &mutex->pmutex ) ) + { + printf( " Mutex %p locked by %s() in %s:%d\n", mutex, mutex->function, mutex->file, mutex->line ); + fflush( stdout ); + if( pthread_mutex_lock( &mutex->pmutex ) ) + DEBUG_WARNING(); + } + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + return; +} + +static inline void mtMutexUnlockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + printf( "Mutex Unlock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( pthread_mutex_unlock( &mutex->pmutex ) ) + DEBUG_WARNING(); + return; +} + +static inline int mtMutexTryLockDebug( mtMutex *mutex, const unsigned char *function, const unsigned char *file, const int line ) +{ + printf( "Mutex Trylock : Thread %d on %p from %s() in %s:%d\n", (int)pthread_self(), mutex, function, file, line ); + fflush( stdout ); + if( !( pthread_mutex_trylock( &mutex->pmutex ) ) ) + { + mutex->function = (unsigned char *)function; + mutex->file = (unsigned char *)file; + mutex->line = line; + return 1; + } + else + return 0; +} + + #else + +static inline void mtMutexLock( mtMutex *mutex ) +{ + pthread_mutex_lock( &mutex->pmutex ); + return; +} + +static inline void mtMutexUnlock( mtMutex *mutex ) +{ + pthread_mutex_unlock( &mutex->pmutex ); + return; +} + +static inline int mtMutexTryLock( mtMutex *mutex ) +{ + return !( pthread_mutex_trylock( &mutex->pmutex ) ); +} + + #endif + + + +//// + + + +typedef struct mtSignal mtSignal; + +struct mtSignal +{ + pthread_cond_t pcond; +}; + +static inline void mtSignalInit( mtSignal *signal ) +{ + pthread_cond_init( &signal->pcond, 0 ); + return; +} + +static inline void mtSignalDestroy( mtSignal *signal ) +{ + pthread_cond_destroy( &signal->pcond ); + return; +} + +static inline void mtSignalWake( mtSignal *signal ) +{ + #ifdef MT_DEBUG + if( pthread_cond_signal( &signal->pcond ) ) + DEBUG_WARNING(); + #else + pthread_cond_signal( &signal->pcond ); + #endif + return; +} + +static inline void mtSignalBroadcast( mtSignal *signal ) +{ + #ifdef MT_DEBUG + if( pthread_cond_broadcast( &signal->pcond ) ) + DEBUG_WARNING(); + #else + pthread_cond_broadcast( &signal->pcond ); + #endif + return; +} + +static inline void mtSignalWait( mtSignal *signal, mtMutex *mutex ) +{ + #ifdef MT_DEBUG + if( pthread_cond_wait( &signal->pcond, &mutex->pmutex ) ) + DEBUG_WARNING(); + #else + pthread_cond_wait( &signal->pcond, &mutex->pmutex ); + #endif + return; +} + +static inline void mtSignalWaitTimeout( mtSignal *signal, mtMutex *mutex, long milliseconds ) +{ + uint64_t microsecs; + struct timespec ts; + struct timeval tp; + gettimeofday( &tp, NULL ); + ts.tv_sec = tp.tv_sec + ( milliseconds / 1000 ); + microsecs = tp.tv_usec + ( ( milliseconds % 1000 ) * 1000 ); + if( microsecs >= 1000000 ) + { + ts.tv_sec++; + microsecs -= 1000000; + } + ts.tv_nsec = (int)(microsecs * 1000); + pthread_cond_timedwait( &signal->pcond, &mutex->pmutex, &ts ); + return; +} + + #ifdef MT_DEBUG + #define MT_MUTEX_INITIALIZER { 0, 0, 0, PTHREAD_MUTEX_INITIALIZER } + #else + #define MT_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + #endif + + + +//// + + + + #ifdef MM_ATOMIC_SUPPORT + +typedef struct mtSpin mtSpin; + +struct mtSpin +{ + mmAtomic32 atomicspin; +}; + +static inline void mtSpinInit( mtSpin *spin ) +{ + mmAtomicWrite32( &spin->atomicspin, 0x0 ); + return; +} + +static inline void mtSpinDestroy( mtSpin *spin ) +{ + return; +} + +static inline void mtSpinLock( mtSpin *spin ) +{ + mmAtomicSpin32( &spin->atomicspin, 0x0, 0x1 ); + return; +} + +static inline void mtSpinUnlock( mtSpin *spin ) +{ + mmAtomicWrite32( &spin->atomicspin, 0x0 ); + return; +} + +static inline int mtSpinTryLock( mtSpin *spin ) +{ + return mmAtomicCmpReplace32( &spin->atomicspin, 0x0, 0x1 ); +} + + #elif _POSIX_SPIN_LOCKS > 0 + +typedef struct mtSpin mtSpin; + +struct mtSpin +{ + #ifdef MT_DEBUG + unsigned char *function; + unsigned char *file; + int line; + #endif + pthread_spinlock_t pspinlock; +}; + +static inline void mtSpinInit( mtSpin *spin ) +{ + pthread_spin_init( &spin->pspinlock, PTHREAD_PROCESS_PRIVATE ); + return; +} + +static inline void mtSpinDestroy( mtSpin *spin ) +{ + pthread_spin_destroy( &spin->pspinlock ); + return; +} + +static inline void mtSpinLock( mtSpin *spin ) +{ + #ifdef MT_DEBUG + if( pthread_spin_lock( &spin->pspinlock ) ) + DEBUG_WARNING(); + #else + pthread_spin_lock( &spin->pspinlock ); + #endif + return; +} + +static inline void mtSpinUnlock( mtSpin *spin ) +{ + #ifdef MT_DEBUG + if( pthread_spin_unlock( &spin->pspinlock ) ) + DEBUG_WARNING(); + #else + pthread_spin_unlock( &spin->pspinlock ); + #endif + return; +} + +static inline int mtSpinTryLock( mtSpin *spin ) +{ + return !( pthread_spin_trylock( &spin->pspinlock ) ); +} + + #else + +typedef struct mtMutex mtSpin; + + #define mtSpinInit(a) mtMutexInit(a) + #define mtSpinDestroy(a) mtMutexDestroy(a) + #define mtSpinLock(a) mtMutexLock(a) + #define mtSpinUnlock(a) mtMutexUnlock(a) + #define mtSpinTryLock(a) mtMutexTryLock(a) + + #endif + + + +//// + + + + #ifdef MT_BARRIER_SUPPORT + +typedef struct +{ + pthread_barrier_t pbarrier; +} mtBarrier; + +static inline void mtBarrierInit( mtBarrier *barrier, int count ) +{ + pthread_barrier_init( &barrier->pbarrier, 0, count ); + return; +} + +static inline void mtBarrierDestroy( mtBarrier *barrier ) +{ + pthread_barrier_destroy( &barrier->pbarrier ); + return; +} + +static inline int mtBarrierWait( mtBarrier *barrier ) +{ + return pthread_barrier_wait( &barrier->pbarrier ); +} + + #endif + + + +//// + + + + #ifdef MT_RWLOCK_SUPPORT + +typedef struct mtRWlock mtRWlock; + +struct mtRWlock +{ + pthread_rwlock_t prwlock; +}; + + +static inline void mtRWlockInit( mtRWlock *rwlock ) +{ + pthread_rwlock_init( &rwlock->prwlock, 0 ); + return; +} + +static inline void mtRWlockDestroy( mtRWlock *rwlock ) +{ + pthread_rwlock_destroy( &rwlock->prwlock ); + return; +} + +static inline void mtRWlockRead( mtRWlock *rwlock ) +{ + pthread_rwlock_rdlock( &rwlock->prwlock ); + return; +} + +static inline void mtRWlockWrite( mtRWlock *rwlock ) +{ + pthread_rwlock_wrlock( &rwlock->prwlock ); + return; +} + +static inline void mtRWlockUnlock( mtRWlock *rwlock ) +{ + pthread_rwlock_unlock( &rwlock->prwlock ); + return; +} + +static inline int mtRWlockTryRead( mtRWlock *rwlock ) +{ + return pthread_rwlock_rdlock( &rwlock->prwlock ); +} + +static inline int mtRWlockTryWrite( mtRWlock *rwlock ) +{ + return pthread_rwlock_wrlock( &rwlock->prwlock ); +} + + #endif + + +#endif + + diff --git a/ecere/src/gfx/newFonts/drawManager.ec b/ecere/src/gfx/newFonts/drawManager.ec new file mode 100644 index 0000000..09ddc0c --- /dev/null +++ b/ecere/src/gfx/newFonts/drawManager.ec @@ -0,0 +1,862 @@ +import "instance" + +#include +#include + +#if defined(_GLES) + #define ES1_1 +#else + //#define SHADERS +#endif + +#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) +# if defined(SHADERS) +# include "gl_core_3_3.h" +# else +# include "gl_compat_4_4.h" +# endif +#endif + +#include "cc.h" + +#define OFFSET(s, m) ((uint)(uintptr) (&((s *) 0)->m)) + +import "fontManager" +import "textureManager" + + +#define DM_ENABLE_IMAGE_ROTATION (1) + +//// + +static struct DMDrawVertex +{ + // TODO FIXME: switch to short + float vertex[2]; + float texcoord0[2]; + uint32 color; +} __attribute__((aligned(16))); + +struct DMDrawBuffer +{ + GLuint vbo; + int glType; + int vertexCount; + int vertexAlloc; + void *vertexBuffer; +}; + +class DMProgramFlags : uint { bool valid:1; } + +struct DMProgram +{ + GLuint glProgram; + GLuint vertexShader; + GLuint fragmentShader; + GLint matrixloc; + GLint vertexloc; + GLint texcoord0loc; + GLint texcoord1loc; + GLint colorloc; + GLint texbaseloc; + DMProgramFlags flags; + int64 lastUpdateCount; +}; + +class DMImageFlags : uint16 +{ + bool empty:1; // Image is empty, do not draw + bool blending:1; // Must draw image with blending +} + +public struct DMImage +{ +private: + Texture texture; + DMImageFlags flags; + short programIndex; + short srcx, srcy; + short sizex, sizey; + + // Computed order for sorted rendering, in defineImage() + uint32 orderMask; + +public: + void clear() + { + this = { flags = { empty = true } }; + } + + void defineImage( Texture texture, int offsetx, int offsety, int sizex, int sizey, bool blending, int programIndex, int layerindex ) + { + int ordx = offsetx >> 6; + int ordy = offsety >> 6; + uint32 orderimage = ccMortonNumber32( ordx, ordy ) & ( ( 1 << DM_IMAGE_ORDER_BITS ) - 1 ); + this = + { + texture = texture; + srcx = (short)offsetx; + srcy = (short)offsety; + sizex = (short)sizex; + sizey = (short)sizey; + programIndex = (short)programIndex; + flags = { blending = blending }; + orderMask = (orderimage << DM_IMAGE_ORDER_SHIFT) | + (( blending == true ) << DM_BLEND_ORDER_SHIFT) | + (programIndex << DM_PROGRAM_ORDER_SHIFT) | + texture.orderMask | + (layerindex << DM_LAYER_ORDER_SHIFT); + }; + } +}; + +struct DMImageBuffer +{ + DMImage *image; + short offsetx; + short offsety; + short sizex; + short sizey; +#if DM_ENABLE_IMAGE_ROTATION + short angcos; + short angsin; +#endif + uint32 color; + uint32 orderindex; +}; + + +//// + + +define DM_IMAGE_ORDER_BITS = 8; +define DM_BLEND_ORDER_BITS = 1; +define DM_PROGRAM_ORDER_BITS = 4; +define DM_TEXTURE_ORDER_BITS = 10; +define DM_LAYER_ORDER_BITS = 4; +define DM_BARRIER_ORDER_BITS = 5; + +define DM_IMAGE_ORDER_SHIFT = 0; +define DM_BLEND_ORDER_SHIFT = DM_IMAGE_ORDER_BITS; +define DM_PROGRAM_ORDER_SHIFT = DM_IMAGE_ORDER_BITS+DM_BLEND_ORDER_BITS; +define DM_TEXTURE_ORDER_SHIFT = DM_IMAGE_ORDER_BITS+DM_BLEND_ORDER_BITS+DM_PROGRAM_ORDER_BITS; +define DM_LAYER_ORDER_SHIFT = DM_IMAGE_ORDER_BITS+DM_BLEND_ORDER_BITS+DM_PROGRAM_ORDER_BITS+DM_TEXTURE_ORDER_BITS; +define DM_BARRIER_ORDER_SHIFT = DM_IMAGE_ORDER_BITS+DM_BLEND_ORDER_BITS+DM_PROGRAM_ORDER_BITS+DM_TEXTURE_ORDER_BITS+DM_LAYER_ORDER_BITS; + +define DM_LAYER_COUNT = 1<glProgram = 0; + program->vertexShader = 0; + program->fragmentShader = 0; + + program->vertexShader = dmCreateShader( GL_VERTEX_SHADER, vertexsource, optionstring ); + if( !( program->vertexShader ) ) + { + fprintf(stderr, "ERROR: Unable to load vertex shader\n"); + goto error; + } + program->fragmentShader = dmCreateShader( GL_FRAGMENT_SHADER, fragmentsource, optionstring ); + if( !( program->fragmentShader ) ) + { + fprintf(stderr, "ERROR: Unable to load fragment shader\n"); + goto error; + } + program->glProgram = glCreateProgram(); + if( !( program->glProgram ) ) + { + fprintf(stderr, "ERROR: Unable to create program\n"); + goto error; + } + + glAttachShader( program->glProgram, program->vertexShader ); + glAttachShader( program->glProgram, program->fragmentShader ); + glLinkProgram( program->glProgram ); + glGetProgramiv( program->glProgram, GL_LINK_STATUS, &status ); + if( status != GL_TRUE ) + { + fprintf( stderr, "ERROR, failed to link shader program\n" ); + glGetProgramInfoLog( program->glProgram, 8192, &loglength, infolog ); + fprintf( stderr, "ERROR: \n%s\n\n", infolog ); + goto error; + } + + glUseProgram( program->glProgram ); + + program->matrixloc = glGetUniformLocation( program->glProgram, "uniMatrix" ); + program->vertexloc = glGetAttribLocation( program->glProgram, "inVertex" ); + program->texcoord0loc = glGetAttribLocation( program->glProgram, "inTexcoord0" ); + program->texcoord1loc = glGetAttribLocation( program->glProgram, "inTexcoord1" ); + program->colorloc = glGetAttribLocation( program->glProgram, "inColor" ); + program->texbaseloc = glGetUniformLocation( program->glProgram, "texBase" ); + program->flags.valid = true; + + return true; + + error: + if( program->fragmentShader ) + glDeleteShader( program->fragmentShader ); + if( program->vertexShader ) + glDeleteShader( program->vertexShader ); + if( program->glProgram ) + glDeleteProgram( program->glProgram ); + return false; +} + + +//// + + +const char *dmVertexShaderNormal = +"#version 130\n" +"uniform mat4 uniMatrix;\n" +"in vec2 inVertex;\n" +"in vec2 inTexcoord0;\n" +"in vec4 inColor;\n" +"out vec2 varTexcoord0;\n" +"out vec4 varColor;\n" +"void main()\n" +"{\n" +" \n" +" varTexcoord0 = inTexcoord0;\n" +" varColor = inColor;\n" +" gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n" +" return;\n" +"}\n" +; + + +const char *dmFragmentShaderNormal = +"#version 130\n" +"uniform sampler2D texBase;\n" +"in vec2 varTexcoord0;\n" +"in vec4 varColor;\n" +"out vec4 gl_FragColor;\n" +"void main()\n" +"{\n" +" gl_FragColor = varColor * texture2D( texBase, varTexcoord0 );\n" +" return;\n" +"}\n" +; + + +const char *dmVertexShaderAlpha = +"#version 130\n" +"uniform mat4 uniMatrix;\n" +"in vec2 inVertex;\n" +"in vec2 inTexcoord0;\n" +"in vec4 inColor;\n" +"out vec2 varTexcoord0;\n" +"out vec4 varColor;\n" +"void main()\n" +"{\n" +" \n" +" varTexcoord0 = inTexcoord0;\n" +" varColor = inColor;\n" +" gl_Position = uniMatrix * vec4( inVertex, 0.0, 1.0 );\n" +" return;\n" +"}\n" +; + + +const char *dmFragmentShaderAlpha = +"#version 130\n" +"uniform sampler2D texBase;\n" +"in vec2 varTexcoord0;\n" +"in vec4 varColor;\n" +"out vec4 gl_FragColor;\n" +"void main()\n" +"{\n" +" gl_FragColor = vec4( varColor.rgb, varColor.a * texture2D( texBase, varTexcoord0 ).r );\n" +" return;\n" +"}\n" +; + +static void matrixOrtho( float *m, float left, float right, float bottom, float top, float nearval, float farval ) +{ + float x = 2.0f / ( right - left ); + float y = 2.0f / ( top - bottom ); + float z = -2.0f / ( farval - nearval ); + float tx = -( right + left ) / ( right - left ); + float ty = -( top + bottom ) / ( top - bottom ); + float tz = -( farval + nearval ) / ( farval - nearval ); + +#define M(row,col) m[col*4+row] + M(0,0) = x; M(0,1) = 0.0; M(0,2) = 0.0; M(0,3) = tx; + M(1,0) = 0.0; M(1,1) = y; M(1,2) = 0.0; M(1,3) = ty; + M(2,0) = 0.0; M(2,1) = 0.0; M(2,2) = z; M(2,3) = tz; + M(3,0) = 0.0; M(3,1) = 0.0; M(3,2) = 0.0; M(3,3) = 1.0; +#undef M +} + +//// + + +/* FIXME: Radix sort, not hybrid sort! */ + +static inline int dmSortImagesCmp( DMImageBuffer *draw0, DMImageBuffer *draw1 ) +{ + return ( ( draw0->orderindex < draw1->orderindex ) ? 0 : 1 ); +} + +#define HSORT_MAIN dmSortImages +#define HSORT_CMP dmSortImagesCmp +#define HSORT_TYPE DMImageBuffer +#include "cchybridsort.h" +#undef HSORT_MAIN +#undef HSORT_CMP +#undef HSORT_TYPE + + +//// + +// TOFIX: Make this private, have a property +public class DrawManagerFlags : uint32 { bool prehistoricOpenGL:1; } + +public class DrawManager +{ + DrawManagerFlags flags; + DMProgram shaderprograms[DM_PROGRAM_COUNT]; + + // Matrix + float matrix[16]; + + int imageBufferCount; + int imageBufferSize; + DMImageBuffer *imageBuffer; + DMImageBuffer *imageBufferTmp; + + // Buffers for drawimages() batching + DMDrawBuffer drawBuffer[DM_CONTEXT_DRAW_BUFFER_COUNT]; + int drawBufferIndex; + int drawBarrierIndex; + uint32 orderBarrierMask; + + // Font manager + FontManager fm; + + // Counter to track program uniforms and such + int64 updateCount; + + GLuint prevProgram; + + static void flushRenderDrawBuffer( DMDrawBuffer *drawBuffer, DMProgram *program, int vertexCount ) + { + if( !program || flags.prehistoricOpenGL ) + { + glEnable( GL_TEXTURE_2D ); + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo ); + glColor3f( 1.0, 1.0, 1.0 ); + + glEnableClientState( GL_VERTEX_ARRAY ); + glEnableClientState( GL_TEXTURE_COORD_ARRAY ); + glEnableClientState( GL_COLOR_ARRAY ); + + glVertexPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) ); + glTexCoordPointer( 2, GL_FLOAT, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) ); + glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) ); + + glDrawArrays( GL_TRIANGLES, 0, vertexCount ); + + glDisableClientState( GL_VERTEX_ARRAY ); + glDisableClientState( GL_TEXTURE_COORD_ARRAY ); + glDisableClientState( GL_COLOR_ARRAY ); + glDisable( GL_TEXTURE_2D ); + } + else + { + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo ); + if( program->vertexloc != -1 ) + { + glEnableVertexAttribArray( program->vertexloc ); + glVertexAttribPointer( program->vertexloc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,vertex) ); + } + if( program->texcoord0loc != -1 ) + { + glEnableVertexAttribArray( program->texcoord0loc ); + glVertexAttribPointer( program->texcoord0loc, 2, GL_FLOAT, GL_FALSE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,texcoord0) ); + } + if( program->colorloc != -1 ) + { + glEnableVertexAttribArray( program->colorloc ); + glVertexAttribPointer( program->colorloc, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(DMDrawVertex), (void *)OFFSET(DMDrawVertex,color) ); + } + + glDrawArrays( GL_TRIANGLES, 0, vertexCount ); + + if( program->vertexloc != -1 ) + glDisableVertexAttribArray( program->vertexloc ); + if( program->texcoord0loc != -1 ) + glDisableVertexAttribArray( program->texcoord0loc ); + if( program->colorloc != -1 ) + glDisableVertexAttribArray( program->colorloc ); + } + + #if DM_FLUSH_EACH_RENDER_DRAW_BUFFER + glFlush(); + #endif + } + + static DMProgram *dmFlushUseProgram( int programIndex ) + { + DMProgram *program; + + program = &this.shaderprograms[ programIndex ]; + if( !program->flags.valid) + { + glUseProgram( 0 ); + return 0; + } + + glUseProgram( program->glProgram ); + if( program->lastUpdateCount != this.updateCount ) + { + glUniformMatrix4fv( program->matrixloc, 1, GL_FALSE, this.matrix ); + glUniform1i( program->texbaseloc, 0 ); + program->lastUpdateCount = this.updateCount; + } + + return program; + } + +public: + bool init( FontManager fontManager, DrawManagerFlags flags ) + { + int drawBufferIndex, programIndex; + DMDrawBuffer *drawBuffer; + + imageBufferCount = 0; + imageBufferSize = 4096; + imageBuffer = new DMImageBuffer[imageBufferSize]; + imageBufferTmp = new DMImageBuffer[imageBufferSize]; + + for( drawBufferIndex = 0 ; drawBufferIndex < DM_CONTEXT_DRAW_BUFFER_COUNT ; drawBufferIndex++ ) + { + drawBuffer = &this.drawBuffer[drawBufferIndex]; + drawBuffer->glType = GL_FLOAT; + drawBuffer->vertexCount = 0; + drawBuffer->vertexAlloc = DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC; + glGenBuffers( 1, &drawBuffer->vbo ); + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo ); + glBufferData( GL_ARRAY_BUFFER, drawBuffer->vertexAlloc * sizeof(DMDrawVertex), 0, GL_DYNAMIC_DRAW ); + drawBuffer->vertexBuffer = new DMDrawVertex[drawBuffer->vertexAlloc]; + } + + fm = fontManager; + this.flags = flags; + + if( !flags.prehistoricOpenGL ) + { + DMProgram *program; + for( programIndex = 0 ; programIndex < DM_PROGRAM_COUNT ; programIndex++ ) + { + program = &shaderprograms[ programIndex ]; + program->flags = 0; + program->lastUpdateCount = -1; + } + program = &shaderprograms[ DM_PROGRAM_NORMAL ]; + if( !( dmCreateProgram( program, dmVertexShaderNormal, dmFragmentShaderNormal, 0 ) ) ) + return false; + program = &shaderprograms[ DM_PROGRAM_ALPHABLEND ]; + if( !( dmCreateProgram( program, dmVertexShaderAlpha, dmFragmentShaderAlpha, 0 ) ) ) + return false; + glUseProgram( 0 ); + } + + updateCount = 0; + + return true; + } + + + void end( ) + { + int i; + + for( i = 0 ; i < DM_CONTEXT_DRAW_BUFFER_COUNT ; i++ ) + { + DMDrawBuffer *db = &drawBuffer[i]; + glDeleteBuffers( 1, &db->vbo ); + delete db->vertexBuffer; + } + + // TODO: Destroy the shaders! + delete imageBuffer; + delete imageBufferTmp; + } + + void ready( int viewportwidth, int viewportheight ) + { + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint *)&prevProgram); + while(glGetError()); + + // Save OpenGL state + glPushClientAttrib( GL_CLIENT_ALL_ATTRIB_BITS ); + glPushAttrib( GL_ALL_ATTRIB_BITS ); + + // Prepare rendering pass + matrixOrtho( matrix, 0.0, (float)viewportwidth, (float)viewportheight, 0.0, -1.0f, 1.0 ); + drawBarrierIndex = 0; + orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT; + orderBarrierMask = 0; + + updateCount++; + } + + void drawImage( DMImage *image, int offsetx, int offsety, int sizex, int sizey, uint32 color ) + { + DMImageBuffer *imageBuffer; + + if( image->flags.empty || ( sizex <= 0 ) || ( sizey <= 0 ) ) + return; + + if( imageBufferCount >= imageBufferSize ) + { + imageBufferSize <<= 1; + this.imageBuffer = realloc( this.imageBuffer, imageBufferSize * sizeof(DMImageBuffer) ); + imageBufferTmp = realloc( imageBufferTmp, imageBufferSize * sizeof(DMImageBuffer) ); + } + + imageBuffer = &this.imageBuffer[ imageBufferCount ]; + imageBuffer->image = image; + imageBuffer->offsetx = (short)offsetx; + imageBuffer->offsety = (short)offsety; + imageBuffer->sizex = (short)sizex; + imageBuffer->sizey = (short)sizey; + #if DM_ENABLE_IMAGE_ROTATION + imageBuffer->angsin = 0; + imageBuffer->angcos = (short)rotationNormFactor; + #endif + imageBuffer->color = color; + imageBuffer->orderindex = image->orderMask | this.orderBarrierMask; + + #if DM_RENDER_IMAGE_DEBUG + printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex ); + #endif + + imageBufferCount++; + } + + void drawImageFloat( DMImage *image, float offsetx, float offsety, float sizex, float sizey, float angsin, float angcos, uint32 color ) + { + DMImageBuffer *imageBuffer; + + if( image->flags.empty || sizex <= 0 || sizey <= 0 ) + return; + + if( imageBufferCount >= imageBufferSize ) + { + imageBufferSize <<= 1; + this.imageBuffer = renew this.imageBuffer DMImageBuffer[imageBufferSize]; + imageBufferTmp = renew imageBufferTmp DMImageBuffer[imageBufferSize]; + } + + imageBuffer = &this.imageBuffer[ imageBufferCount ]; + imageBuffer->image = image; + imageBuffer->offsetx = (short)offsetx; + imageBuffer->offsety = (short)offsety; + imageBuffer->sizex = (short)sizex; + imageBuffer->sizey = (short)sizey; + #if DM_ENABLE_IMAGE_ROTATION + imageBuffer->angsin = (short)roundf( angsin * rotationNormFactor ); + imageBuffer->angcos = (short)roundf( angcos * rotationNormFactor ); + #endif + imageBuffer->color = color; + imageBuffer->orderindex = image->orderMask | this.orderBarrierMask; + + #if DM_RENDER_IMAGE_DEBUG + printf( " Queue image at %d %d, order 0x%x\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex ); + #endif + + imageBufferCount++; + } + + + void flushImages( ) + { + bool flushflag, stateBlend; + int index, vertexCount, programIndex; + float vx0, vx1, vx2, vx3, vy0, vy1, vy2, vy3; + #if DM_ENABLE_IMAGE_ROTATION + float angsin, angcos, sizex, sizey; + #endif + float tx0, tx1, ty0, ty1; + DMImageBuffer *imageBuffer; + DMImage *image, *bindimage; + Texture texture, bindTexture; + DMDrawBuffer *drawBuffer; + DMDrawVertex *vboVertex; + DMProgram *program; + + ERRORCHECK(); + + drawBarrierIndex = 0; + this.orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT; + if( !( imageBufferCount ) ) + return; + + /* Sort by image type and texture, minimize state changes */ + dmSortImages( this.imageBuffer, imageBufferTmp, imageBufferCount, (uint32)( (intptr_t)this.imageBuffer >> 4 ) ); + + /* Fill a drawBuffer, write vertex and texcoords */ + drawBuffer = &this.drawBuffer[drawBufferIndex]; + drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT; + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo ); + vboVertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); + vertexCount = 0; + + glActiveTexture( GL_TEXTURE0 ); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + glDisable( GL_BLEND ); + + #if DM_RENDER_IMAGE_DEBUG + printf( " Flush %d images\n", (int)imageBufferCount ); + #endif + + bindimage = 0; + bindTexture = 0; + stateBlend = 0; + programIndex = -1; + program = 0; + imageBuffer = this.imageBuffer; + for( index = 0 ; index < imageBufferCount ; index++, imageBuffer++ ) + { + image = imageBuffer->image; + texture = image->texture; + + flushflag = 0; + if( image != bindimage ) + { + if( stateBlend != image->flags.blending ) + flushflag = 1; + if( texture != bindTexture ) + flushflag = 1; + } + if( vertexCount >= ( drawBuffer->vertexAlloc - 6 ) ) + flushflag = 1; + + if( flushflag ) + { + if( vertexCount ) + { + glUnmapBuffer( GL_ARRAY_BUFFER ); + // Flush font manager texture updates + fm.flushUpdate( ); + // Render buffered images + flushRenderDrawBuffer( drawBuffer, program, vertexCount ); + drawBuffer = &this.drawBuffer[drawBufferIndex]; + drawBufferIndex = ( drawBufferIndex + 1 ) % DM_CONTEXT_DRAW_BUFFER_COUNT; + glBindBuffer( GL_ARRAY_BUFFER, drawBuffer->vbo ); + vboVertex = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY ); + vertexCount = 0; + } + + if( stateBlend != ( image->flags.blending ) ) + { + stateBlend = image->flags.blending; + ( stateBlend ? glEnable : glDisable )( GL_BLEND ); + #if DM_RENDER_IMAGE_DEBUG + printf( " Switch blending %d\n", stateBlend != false ); + #endif + } + if( programIndex != image->programIndex ) + { + programIndex = image->programIndex; + program = dmFlushUseProgram( programIndex ); + } + if( texture != bindTexture ) + { + bindTexture = texture; + glBindTexture( GL_TEXTURE_2D, bindTexture.glTex ); + #if DM_RENDER_IMAGE_DEBUG + printf( " Switch to texture 0x%x\n", (int)texture.orderMask ); + #endif + } + bindimage = image; + } + + #if DM_RENDER_IMAGE_DEBUG + printf( " Render image at %d %d, order 0x%x, texture %p\n", (int)imageBuffer->offsetx, (int)imageBuffer->offsety, (int)imageBuffer->orderindex, texture ); + #endif + + #if DM_ENABLE_IMAGE_ROTATION + angsin = (float)imageBuffer->angsin * (1.0f/rotationNormFactor); + angcos = (float)imageBuffer->angcos * (1.0f/rotationNormFactor); + sizex = (float)imageBuffer->sizex; + sizey = (float)imageBuffer->sizey; + vx0 = (float)( imageBuffer->offsetx ); + vy0 = (float)( imageBuffer->offsety ); + vx1 = vx0 + ( angcos * sizex ); + vy1 = vy0 + ( angsin * sizex ); + vx2 = vx0 - ( angsin * sizey ); + vy2 = vy0 + ( angcos * sizey ); + vx3 = vx0 + ( angcos * sizex ) - ( angsin * sizey ); + vy3 = vy0 + ( angsin * sizex ) + ( angcos * sizey ); + #else + vx0 = (float)( imageBuffer->offsetx ); + vy0 = (float)( imageBuffer->offsety ); + vx3 = vx0 + (float)( imageBuffer->sizex ); + vy3 = vy0 + (float)( imageBuffer->sizey ); + vx1 = vx3; + vy1 = vy0; + vx2 = vx0; + vy2 = vy3; + #endif + + tx0 = (float)( image->srcx ) * texture.widthinv; + ty0 = (float)( image->srcy ) * texture.heightinv; + tx1 = (float)( image->srcx + image->sizex ) * texture.widthinv; + ty1 = (float)( image->srcy + image->sizey ) * texture.heightinv; + + // Write data to VBO + vboVertex[0].vertex[0] = vx3; + vboVertex[0].vertex[1] = vy3; + vboVertex[0].texcoord0[0] = tx1; + vboVertex[0].texcoord0[1] = ty1; + vboVertex[0].color = imageBuffer->color; + vboVertex[1].vertex[0] = vx1; + vboVertex[1].vertex[1] = vy1; + vboVertex[1].texcoord0[0] = tx1; + vboVertex[1].texcoord0[1] = ty0; + vboVertex[1].color = imageBuffer->color; + vboVertex[2].vertex[0] = vx2; + vboVertex[2].vertex[1] = vy2; + vboVertex[2].texcoord0[0] = tx0; + vboVertex[2].texcoord0[1] = ty1; + vboVertex[2].color = imageBuffer->color; + vboVertex[3].vertex[0] = vx0; + vboVertex[3].vertex[1] = vy0; + vboVertex[3].texcoord0[0] = tx0; + vboVertex[3].texcoord0[1] = ty0; + vboVertex[3].color = imageBuffer->color; + vboVertex[4].vertex[0] = vx2; + vboVertex[4].vertex[1] = vy2; + vboVertex[4].texcoord0[0] = tx0; + vboVertex[4].texcoord0[1] = ty1; + vboVertex[4].color = imageBuffer->color; + vboVertex[5].vertex[0] = vx1; + vboVertex[5].vertex[1] = vy1; + vboVertex[5].texcoord0[0] = tx1; + vboVertex[5].texcoord0[1] = ty0; + vboVertex[5].color = imageBuffer->color; + + vboVertex += 6; + vertexCount += 6; + } + + glUnmapBuffer( GL_ARRAY_BUFFER ); + // Flush font manager texture updates + fm.flushUpdate( ); + // Render buffered images + flushRenderDrawBuffer( drawBuffer, program, vertexCount ); + imageBufferCount = 0; + + ERRORCHECK(); + + #if 1 + glBindBuffer( GL_ARRAY_BUFFER, 0 ); + glabCurArrayBuffer = 0; + + glUseProgram( prevProgram ); + + #endif + // Restore OpenGL state + glPopAttrib(); + glPopClientAttrib(); + } + + void drawBarrier( ) + { + drawBarrierIndex++; + if( drawBarrierIndex >= ( 1 << DM_BARRIER_ORDER_BITS ) ) + flushImages( ); + orderBarrierMask = drawBarrierIndex << DM_BARRIER_ORDER_SHIFT; + } +} diff --git a/ecere/src/gfx/newFonts/fontManager.ec b/ecere/src/gfx/newFonts/fontManager.ec new file mode 100644 index 0000000..d783bbd --- /dev/null +++ b/ecere/src/gfx/newFonts/fontManager.ec @@ -0,0 +1,1666 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +import "LinkList" +import "File" + +import "atlasBuilder" + +#include +#include + +#include "cc.h" + +static inline uint32 decodeUTF8( uint32 b, uint32 *state, unichar *retCodePoint ) { return ccUtf8ToUnicode(b, state, (uint *)retCodePoint); } + +//// + +// Based on Exponential blur, Jani Huhtanen, 2006 + +#define APREC 16 +#define ZPREC 7 + +static void blurCols( byte *dst, int w, int h, int stride, int alpha ) +{ + int y; + for( y = 0 ; y < h ; y++ ) + { + int x, z = 0; + for( x = 1 ; x < w ; x++ ) + { + z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC; + dst[x] = (byte)( z >> ZPREC ); + } + for( x = w-2 ; x >= 0 ; x-- ) + { + z += ( alpha * ( ( (int)( dst[x] ) << ZPREC ) - z ) ) >> APREC; + dst[x] = (byte)( z >> ZPREC ); + } + dst += stride; + } +} + +static void blurRows( byte *dst, int w, int h, int stride, int alpha ) +{ + int x; + for( x = 0 ; x < w ; x++ ) + { + int y, z = 0; + for( y = stride ; y < h*stride ; y += stride ) + { + z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC; + dst[y] = (byte)( z >> ZPREC ); + } + for( y = (h-2)*stride ; y >= 0 ; y -= stride ) + { + z += ( alpha * ( ( (int)( dst[y] ) << ZPREC ) - z ) ) >> APREC; + dst[y] = (byte)( z >> ZPREC ); + } + dst++; + } +} + +static void blur( byte *dst, int width, int height, int stride, int radius ) +{ + if( radius >= 1 ) + { + int boxcount = 2; + // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) + float radiusrange = (float)radius * ( 1.0f / sqrtf( 3 ) ); + uint32 alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) ); + for( ; boxcount ; boxcount-- ) + { + blurRows( dst, width, height, stride, alpha ); + blurCols( dst, width, height, stride, alpha ); + } + } +} + +#undef APREC +#undef ZPREC + + +#define APREC 12 +#define ZPREC 7 +#define SPREC (31-(APREC+ZPREC+8)) + +static void blurCols32( int *dst, int w, int h, int stride, int alpha ) +{ + int y; + for( y = 0 ; y < h ; y++ ) + { + int x, z = 0; + for( x = 1 ; x < w ; x++ ) + { + z += ( alpha * ( dst[x] - z ) ) >> APREC; + dst[x] = z; + } + for( x = w - 2 ; x >= 0 ; x-- ) + { + z += ( alpha * ( dst[x] - z ) ) >> APREC; + dst[x] = z; + } + dst += stride; + } +} + +static void blurRows32( int *dst, int w, int h, int stride, int alpha ) +{ + int x; + for( x = 0 ; x < w ; x++ ) + { + int y, z = 0; + for( y = 1 * stride ; y < h * stride ; y += stride ) + { + z += ( alpha * ( dst[y] - z ) ) >> APREC; + dst[y] = z; + } + for( y = ( h - 2 ) * stride ; y >= 0 ; y -= stride ) + { + z += ( alpha * ( dst[y] - z ) ) >> APREC; + dst[y] = z; + } + dst++; + } +} + +static void blurScale( byte *dst, int width, int height, int stride, int radius, int scale ) +{ + if( radius >= 1 ) + { + int x, y, boxcount; + float radiusrange; + int alpha; + int *buffer, *bufferrow; + byte *dstrow; + + if( scale > ( 1 << SPREC ) ) + scale = 1 << SPREC; + + buffer = malloc( width * height * sizeof(int) ); + for( y = 0 ; y < height ; y++ ) + { + dstrow = &dst[y*stride]; + bufferrow = &buffer[y*width]; + for( x = 0 ; x < width ; x++ ) + bufferrow[x] = ( (int)dstrow[x] << ZPREC ) * scale; + } + + boxcount = 3; + + /* Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) */ + radiusrange = (float)radius * 0.3f; + alpha = (uint32)roundf( ( 1 << APREC ) * ( 1.0f - expf( -2.3f / ( radiusrange + 1.0f ) ) ) ); + for( ; boxcount ; boxcount-- ) + { + blurRows32( buffer, width, height, width, alpha ); + blurCols32( buffer, width, height, width, alpha ); + } + + for( y = 0 ; y < height ; y++ ) + { + dstrow = &dst[y*stride]; + bufferrow = &buffer[y*width]; + for( x = 0 ; x < width ; x++ ) + dstrow[x] = (byte)Min(255, bufferrow[x] >> ZPREC); + } + free( buffer ); + } +} + +#undef APREC +#undef ZPREC +#undef SPREC + + +//// + +static void buildCursorGlyph( byte *dst, int glyphwidth, int glyphheight, int dststride ) +{ + int x, y, hbarheight, hgap, hline, vline; + byte *dstrow; + + if( glyphwidth >= 3 ) + { + hbarheight = 1 + ( glyphheight >> 6 ); + hgap = ( glyphwidth - ( 1 + ( glyphheight >> 6 ) ) ) >> 1; + hline = hgap + glyphwidth - ( hgap << 1 ); + vline = glyphheight - ( hbarheight << 1 ); + for( y = 0 ; y < hbarheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + for( y = 0 ; y < vline ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < hgap ; x++ ) + dstrow[x] = 0; + for( ; x < hline ; x++ ) + dstrow[x] = 255; + for( ; x < glyphwidth ; x++ ) + dstrow[x] = 0; + dst += dststride; + } + for( y = 0 ; y < hbarheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + } + else + { + for( y = 0 ; y < glyphheight ; y++ ) + { + dstrow = dst; + for( x = 0 ; x < glyphwidth ; x++ ) + dstrow[x] = 255; + dst += dststride; + } + } + + return; +} + +static inline void addGlyphAdvance( int *x, int *subpixel, FMGlyph *glyph ) +{ + *subpixel += glyph->advance; +#if FM_SUBPIXEL_ROUNDING_RANGE + *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1); +#endif + *x += *subpixel >> 6; + *subpixel &= 0x3f; +} + +public class FontManagerRenderer +{ +public: + FontManager fm; + + virtual bool init(); + + // Create, resize or update texture data ; rect is [minx,maxx,miny,maxy] + virtual int createTexture( int width, int height ); + virtual int resizeTexture( int width, int height ); + virtual void updateTexture( int *rect, const byte *data ); + + // If the renderer does any buffering of drawImage(), flush it all right now (texture may change immediately after) + virtual void flush( ); + + // If the renderer records per-image data, return an imageIndex passed to drawImage() + virtual int registerImage( int offsetx, int offsety, int width, int height ); + // Draw an image, imageIndex passed as the value previously returned by registerImage() + virtual void drawImage( int targetx, int targety, int imageIndex ); + // Draw an image, called instead of drawImage() for text cursors, can point to exactly the same function + virtual void drawImageCursor( int targetx, int targety, int imageIndex ); + // If drawImage is zero, then this alternate function is called, passing everything required to render the glyph + virtual void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height ); + // Draw a non-aligned image, imageIndex passed as the value previously returned by registerImage() + virtual void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageIndex ); + + // The renderer must flush all recorded images, registerImage() will be called for new images + virtual void resetImages( ); +}; + + +//// + + +#include + +#include FT_FREETYPE_H +#include FT_ADVANCES_H + + +//// + + +#define FM_SUPPORT_GLYPH_ROTATION (1) + + +//// + + +#define FM_ENABLE_HINTING (1) +#define FM_SUBPIXEL_ROUNDING_RANGE (16) +#if FM_SUPPORT_GLYPH_ROTATION + #define FM_TEXTURE_PADDING (1) +#else + #define FM_TEXTURE_PADDING (0) +#endif +#define FM_DEBUG_WHITERECT (0) + + +#define FM_HASH_TABLE_SIZE (4096) +#define FM_INIT_GLYPHS (1024) +#define FM_INIT_ATLAS_NODES (512) + +#define FM_MAX_STATES (16) + + +//// + +struct FMFreeTypeFont +{ + FT_Face face; + + static inline int ::init() + { + FT_Error ftError; + ftError = FT_Init_FreeType( &ftLibrary2 ); + return ftError == 0; + } + + static inline int loadFont( byte *data, int dataSize ) + { + FT_Error ftError = FT_New_Memory_Face( ftLibrary2, (const FT_Byte*)data, dataSize, 0, &face ); + return ftError == 0; + } + + void free() + { + FT_Done_Face( face ); + } + + static inline void getFontVMetrics( float *ascent, float *descent, float *lineHeight, float *limitminy, float *limitmaxy ) + { + *ascent = (float)face->ascender; + *descent = (float)face->descender; + *lineHeight = (float)face->height / (float)face->units_per_EM; + *limitminy = (float)face->bbox.yMin / (float)face->units_per_EM; + *limitmaxy = (float)face->bbox.yMax / (float)face->units_per_EM; + } + + static inline int getGlyphIndex( unichar codepoint ) + { + return FT_Get_Char_Index( this.face, codepoint ); + } + + static int buildGlyphBitmap( int glyph, int size, int subpixel, int *advance, int *x0, int *y0, int *x1, int *y1 ) + { + FT_Error ftError; + FT_GlyphSlot glyphslot; + FT_Vector subvector; + + ftError = FT_Set_Pixel_Sizes( this.face, 0, (FT_UInt)size ); + if( ftError ) + return 0; + + subvector.x = subpixel; + subvector.y = 0; + FT_Set_Transform( face, 0, &subvector ); + + #if FM_ENABLE_HINTING + ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER ); + #else + ftError = FT_Load_Glyph( face, glyph, FT_LOAD_RENDER | FT_LOAD_NO_HINTING ); + #endif + if( ftError ) + return 0; + + glyphslot = face->glyph; + *advance = glyphslot->metrics.horiAdvance; + *x0 = glyphslot->bitmap_left; + *x1 = glyphslot->bitmap_left + glyphslot->bitmap.width; + *y0 = -glyphslot->bitmap_top; + *y1 = -glyphslot->bitmap_top + glyphslot->bitmap.rows; + + return 1; + } + + static void copyGlyphBitmap( byte *dst, int glyphwidth, int glyphheight, int dststride, int glyphindex ) + { + int x, y; + byte *dstrow, *src; + FT_GlyphSlot glyphslot; + + glyphslot = face->glyph; + src = glyphslot->bitmap.buffer; + for( y = 0 ; y < glyphslot->bitmap.rows ; y++ ) + { + dstrow = &dst[ y * dststride ]; + for( x = 0 ; x < glyphslot->bitmap.width ; x++ ) + dstrow[ x ] = src[ x ]; + src += glyphslot->bitmap.width; + } + return; + } + + static inline int getGlyphKernAdvance( int glyph1, int glyph2 ) + { + FT_Vector ftKerning; + FT_Get_Kerning( face, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning ); + return ftKerning.x; + } +}; + +#define FM_DEF_CODEPOINT_BITS (32) +#define FM_DEF_SIZE_BITS (12) +#define FM_DEF_SUBPIXEL_BITS (6) +#define FM_DEF_BLURRADIUS_BITS (8) +#define FM_DEF_BLURSCALE_BITS (6) + +#define FM_DEF_CODEPOINT_SHIFT (0) +#define FM_DEF_SIZE_SHIFT (FM_DEF_CODEPOINT_BITS) +#define FM_DEF_SUBPIXEL_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS) +#define FM_DEF_BLURRADIUS_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS) +#define FM_DEF_BLURSCALE_SHIFT (FM_DEF_CODEPOINT_BITS+FM_DEF_SIZE_BITS+FM_DEF_SUBPIXEL_BITS+FM_DEF_BLURRADIUS_BITS) + +#define FM_GLYPH_COMPUTE_DEF(codepoint,size,subpixel,blurradius,blurscale) ( (((uint64)(codepoint))<= glyphalloc ) + { + glyphalloc <<= 1; + if( !(glyphs = renew glyphs FMGlyph[glyphalloc]) ) + return null; + } + return &glyphs[ glyphcount++ ]; + } + + //// + + static float getVertAlign( int align, int size ) + { + if( align & FM_ALIGN_TOP ) + return ascender * size; + else if( align & FM_ALIGN_MIDDLE ) + return middleAlign * size; + else if( align & FM_ALIGN_BOTTOM ) + return descender * (float)size; + return 0.0f; + } + + static inline void addKerning( int prevGlyphIndex, FMGlyph *glyph, int *x, int *subpixel ) + { + if( prevGlyphIndex != -1 ) + { + *subpixel += ftFont.getGlyphKernAdvance( prevGlyphIndex, glyph->glyphindex ); + #if FM_SUBPIXEL_ROUNDING_RANGE + *subpixel = ( *subpixel + (FM_SUBPIXEL_ROUNDING_RANGE>>1) ) & ~(FM_SUBPIXEL_ROUNDING_RANGE-1); + #endif + *x += *subpixel >> 6; + *subpixel &= 0x3f; + } + } + +}; + +struct FMState +{ + FMFont font; + uint16 size; + uint16 align; + uint16 blurradius; + uint16 blurscale; +}; + +//// + + +static FT_Library ftLibrary2; + +public class FontManager +{ + FontManagerRenderer renderer; + int width, height; + float widthinv, heightinv; + + AtlasBuilder atlas { }; + byte *texdata; + int dirtyrect[4]; + + LinkList fontList { }; + + FMState states[FM_MAX_STATES]; + int nstates; + + #if FM_DEBUG_WHITERECT + static void addWhiteRect( int w, int h ) + { + int gx, gy; + if( atlas.addRect( w, h, &gx, &gy ) ) + { + // Rasterize + byte * dst = &texdata[ gx + ( gy * width ) ]; + int x, y; + for( y = 0 ; y < h ; y++) + { + for( x = 0 ; x < w ; x++ ) + dst[x] = 0xff; + dst += width; + } + + dirtyrect[0] = Min( dirtyrect[0], gx ); + dirtyrect[1] = Min( dirtyrect[1], gy ); + dirtyrect[2] = Max( dirtyrect[2], gx + w ); + dirtyrect[3] = Max( dirtyrect[3], gy + h ); + } + } + #endif + + static FMGlyph *getGlyph( FMFont font, unichar codepoint, int size, int subpixel, int blurradius, int blurscale ) + { + int i, glyphindex, advance, x0, y0, x1, y1, glyphwidth, glyphheight, gx, gy; + #if FM_TEXTURE_PADDING >= 1 + int x, y; + #endif + uint64 glyphdef; + FMGlyph *glyph; + uint32 hashindex; + int padding, added; + byte *bdst; + byte *dst; + + glyph = 0; + if( size < 0.2 ) + return 0; + padding = blurradius + FM_TEXTURE_PADDING; + + /* Find code point and size. */ + glyphdef = FM_GLYPH_COMPUTE_DEF( codepoint, size, subpixel, blurradius, blurscale ); + hashindex = ccHash32Int64Inline( glyphdef ) & ( FM_HASH_TABLE_SIZE - 1 ); + i = font.hashtable[hashindex]; + while( i != -1 ) + { + if( glyphdef == font.glyphs[i].glyphdef ) + return &font.glyphs[i]; + i = font.glyphs[i].listnext; + } + + /* Could not find glyph, create it. */ + if( codepoint == FM_GLYPH_CODEPOINT_CURSOR ) + { + glyphindex = -1; + advance = 0; + #if 0 + x0 = 0; + x1 = 1; + #else + x0 = -2; + x1 = 3; + #endif + y0 = -(int)ceilf( font.limitmaxy * (float)size ); + y1 = -(int)floorf( font.limitminy * (float)size ); + glyphheight = y1 - y0; + i = ( glyphheight - size ) / 3; + y0 += i; + y1 -= i; + } + else + { + glyphindex = font.ftFont.getGlyphIndex( codepoint ); + if( !( font.ftFont.buildGlyphBitmap( glyphindex, size, subpixel, &advance, &x0, &y0, &x1, &y1 ) ) ) + { + if( codepoint != FM_GLYPH_CODEPOINT_REPLACEMENT ) + return getGlyph(font, FM_GLYPH_CODEPOINT_REPLACEMENT, size, subpixel, blurradius, blurscale ); + return 0; + } + } + glyphwidth = ( x1 - x0 ) + ( padding << 1 ); + glyphheight = ( y1 - y0 ) + ( padding << 1 ); + + // Find free spot for the rect in the atlas + added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy ); + if( !( added ) && ( onAtlasFull ) ) + { + /* Atlas is full, let the user to resize the atlas (or not), and try again. */ + onAtlasFull(); + added = atlas.addRect( glyphwidth, glyphheight, &gx, &gy ); + } + if( !( added ) ) + return 0; + + /* Build the glyph */ + glyph = font.allocGlyph(); + glyph->glyphdef = glyphdef; + glyph->glyphindex = glyphindex; + glyph->x0 = (short)gx; + glyph->y0 = (short)gy; + glyph->x1 = (short)( glyph->x0 + glyphwidth ); + glyph->y1 = (short)( glyph->y0 + glyphheight ); + glyph->advance = (short)advance; + glyph->offsetx = (short)( x0 - padding ); + glyph->offsety = (short)( y0 - padding ); + glyph->listnext = 0; + glyph->imageIndex = -1; + if( renderer.registerImage ) + glyph->imageIndex = renderer.registerImage( gx, gy, glyphwidth, glyphheight ); + + /* Add char to hash table */ + glyph->listnext = font.hashtable[hashindex]; + font.hashtable[hashindex] = font.glyphcount - 1; + + /* Rasterize */ + dst = &texdata[ ( glyph->x0 + padding ) + ( ( glyph->y0 + padding ) * this.width ) ]; + if( codepoint == FM_GLYPH_CODEPOINT_CURSOR ) + buildCursorGlyph( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width ); + else + font.ftFont.copyGlyphBitmap( dst, glyphwidth - ( padding << 1 ), glyphheight - ( padding << 1 ), this.width, glyphindex ); + + /* Blur */ + if( blurradius > 0 ) + { + bdst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ]; + if( blurscale == 1 ) + blur(bdst, glyphwidth, glyphheight, this.width, blurradius ); + else + blurScale(bdst, glyphwidth, glyphheight, this.width, blurradius, blurscale ); + } + + #if FM_TEXTURE_PADDING >= 1 + dst = &texdata[ glyph->x0 + ( glyph->y0 * this.width ) ]; + for( y = 0 ; y < glyphheight ; y++ ) + { + dst[ y * this.width] = 0; + dst[ ( glyphwidth - 1 ) + ( y * this.width ) ] = 0; + } + for( x = 0 ; x < glyphwidth ; x++ ) + { + dst[ x ] = 0; + dst[ x + ( ( glyphheight - 1 ) * this.width ) ] = 0; + } + #endif + + dirtyrect[0] = Min( dirtyrect[0], glyph->x0 ); + dirtyrect[1] = Min( dirtyrect[1], glyph->y0 ); + dirtyrect[2] = Max( dirtyrect[2], glyph->x1 ); + dirtyrect[3] = Max( dirtyrect[3], glyph->y1 ); + + return glyph; + } + + static inline void drawTextGlyph( FMFont font, FMGlyph *glyph, int x, int y ) + { + int ptx = x + glyph->offsetx; + int pty = y + glyph->offsety; + if( renderer.drawImage ) + renderer.drawImage( ptx, pty, glyph->imageIndex ); + else if( renderer.drawImageAlt ) + renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 ); + } + + static inline void drawTextCursorGlyph( FMFont font, FMGlyph *glyph, int x, int y ) + { + int ptx, pty; + ptx = x + glyph->offsetx; + pty = y + glyph->offsety; + if( renderer.drawImageCursor ) + renderer.drawImageCursor( ptx, pty, glyph->imageIndex ); + else if( renderer.drawImageAlt ) + renderer.drawImageAlt( texdata, ptx, pty, glyph->x0, glyph->y0, glyph->x1 - glyph->x0, glyph->y1 - glyph->y0 ); + } + + static inline void drawTextGlyphFloat( FMFont font, FMGlyph *glyph, float x, float y, float vectorx, float vectory, float offsetx, float offsety ) + { + float vectx = (float)glyph->offsetx + offsetx; + float vecty = (float)glyph->offsety + offsety; + float ptx = x + ( vectorx * vectx ) - ( vectory * vecty ); + float pty = y + ( vectorx * vecty ) + ( vectory * vectx ); + renderer.drawImageFloat( ptx, pty, vectory, vectorx, glyph->imageIndex ); + } + +public: + + property FontManagerRenderer renderer { set { renderer = value; } get { return renderer; } }\ + + // When notified of a full atlas, you should call fmExpandAtlas() or fmResetAtlas() within the callback + virtual void (*onAtlasFull)(); + + // Create and destroy font manager + bool create( int width, int height, FontManagerRenderer renderer) + { + bool result = false; + + this.renderer = renderer; + incref renderer; + renderer.init(); + + // Initialize implementation library + if(FMFreeTypeFont::init() ) + { + this.width = width; + this.height = height; + if(renderer.createTexture( width, height )) + { + if((atlas.create( this.width, this.height, FM_INIT_ATLAS_NODES ))) + { + // Create texture for the cache. + widthinv = 1.0f / width; + heightinv = 1.0f / height; + texdata = new byte[width * height]; + if(texdata) + { + memset( texdata, 0, width * height ); + + dirtyrect[0] = this.width; + dirtyrect[1] = this.height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + + // Add white rect at 0,0 for debug drawing. + #if FM_DEBUG_WHITERECT + fmAddWhiteRect(2, 2 ); + #endif + + pushState(); + clearState(); + + result = true; + } + } + } + } + return result; + } + + ~FontManager() + { + delete renderer; + + fontList.Free(); + + delete atlas; + delete texdata; + } + + + //// + + // State setting + void setState( FMFont font, int size, int align, int blurradius, int blurscale ) + { + FMState *state; + if( size >= FM_SIZE_MAX ) + size = FM_SIZE_MAX; + if( blurradius >= FM_BLUR_RADIUS_MAX ) + blurradius = FM_BLUR_RADIUS_MAX; + else if( blurradius < 0 ) + blurradius = 0; + if( blurscale >= FM_BLUR_SCALE_MAX ) + blurscale = FM_BLUR_SCALE_MAX; + else if( blurscale < 1 ) + blurscale = 1; + state = &states[ nstates - 1 ]; + state->font = font; + state->size = (uint16)size; + state->align = (uint16)align; + state->blurradius = (uint16)blurradius; + state->blurscale = (uint16)blurscale; + return; + } + + void setFont( FMFont font ) + { + states[ nstates - 1 ].font = font; + } + + void setSize( int size ) + { + if( size >= FM_SIZE_MAX ) + size = FM_SIZE_MAX; + + states[ nstates - 1 ].size = (uint16)size; + } + + void setAlign( int align ) + { + states[ nstates - 1 ].align = (uint16)align; + } + + void setBlur( int radius, int scale ) + { + if( radius >= FM_BLUR_RADIUS_MAX ) + radius = FM_BLUR_RADIUS_MAX; + else if( radius < 0 ) + radius = 0; + if( scale >= FM_BLUR_SCALE_MAX ) + scale = FM_BLUR_SCALE_MAX; + else if( scale < 1 ) + scale = 1; + states[ nstates - 1 ].blurradius = (uint16)radius; + states[ nstates - 1 ].blurscale = (uint16)scale; + } + + + // State handling + void pushState( ) + { + if(nstates < FM_MAX_STATES) + { + if( nstates > 0 ) + memcpy( &states[nstates], &states[nstates-1], sizeof(FMState) ); + nstates++; + } + } + + void popState() + { + if(nstates > 1) + nstates--; + } + + void clearState() + { + FMState *state = &states[ nstates - 1 ]; + state->size = 12; + state->font = 0; + state->blurradius = 0; + state->blurscale = 1; + state->align = FM_ALIGN_LEFT | FM_ALIGN_BASELINE; + } + + + //// + + static void freeFont(FMFont font) + { + if(font) + { + fontList.Remove((IteratorPointer)font); + delete font; + } + } + + // Add font from file + FMFont addFont(const String path ) + { + FMFont font = null; + File f = FileOpen(path, read); + if(f) + { + // Read in the font data + int dataSize = f.GetSize(); + byte *data = new byte[dataSize]; + if(data) + { + f.Read(data, 1, dataSize); + font = addFontData(data, dataSize ); + if(!font) + delete data; + } + delete f; + } + return font; + } + + // Add font from data ; do not free( data ), the font manager will do that when removing the font + FMFont addFontData( byte *data, int dataSize ) + { + FMFont font + { + glyphs = new FMGlyph[FM_INIT_GLYPHS]; + glyphalloc = FM_INIT_GLYPHS; + }; + if(font) + { + int i; + float ascent, descent, fontHeight; + + fontList.Add(font); + + // Init hash lookup + for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ ) + font.hashtable[i] = -1; + + // Init font + if(!font.ftFont.loadFont(data, dataSize)) + { + freeFont(font ); + return 0; + } + + // Store normalized line height. The real line height is got by multiplying by font size. + font.ftFont.getFontVMetrics( &ascent, &descent, &font.lineHeight, &font.limitminy, &font.limitmaxy ); + fontHeight = ascent - descent; + font.ascender = ascent / fontHeight; + font.descender = descent / fontHeight; + font.middleAlign = 0.5f * ( font.ascender + font.descender ); + font.fontdata = data; + } + return font; + } + + // Free font + void removeFont( FMFont font ) + { + freeFont(font ); + } + + // Draw text + int drawText( int x, int y, const char *string, int stringlength ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align horizontally + if( state->align & FM_ALIGN_RIGHT ) + x -= getTextWidth(string, stringlength ); + else if( state->align & FM_ALIGN_CENTER ) + x -= getTextWidth(string, stringlength ) >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + drawTextGlyph(font, glyph, x, y ); + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return x + ( subpixel >= 32 ); + } + + int drawTextWithCursor( int x, int y, const char *string, int stringlength, int cursoroffset ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align horizontally + if( state->align & FM_ALIGN_RIGHT ) + x -= getTextWidth(string, stringlength ); + else if( state->align & FM_ALIGN_CENTER ) + x -= getTextWidth(string, stringlength ) >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; ; index++ ) + { + if( index == cursoroffset ) + { + glyph = getGlyph(font, FM_GLYPH_CODEPOINT_CURSOR, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + drawTextCursorGlyph(font, glyph, x, y ); + } + if( index >= stringlength ) + break; + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + drawTextGlyph(font, glyph, x, y ); + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return x + ( subpixel >= 32 ); + } + + int drawTextTruncate( int x, int y, int truncatewidth, const char *string, int stringlength, char *extstring, int extwidth ) + { + int subpixel, index, textwidth, truncatepoint; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + textwidth = getTextWidthTruncate(string, stringlength, truncatewidth ); + + truncatepoint = x + truncatewidth; + if( textwidth <= truncatewidth ) + extstring = 0; + else + { + if( extwidth >= truncatewidth ) + return x; + truncatepoint -= extwidth; + } + + // Align horizontally + if( state->align & FM_ALIGN_RIGHT ) + x -= textwidth; + else if( state->align & FM_ALIGN_CENTER ) + x -= textwidth >> 1; + + // Align vertically + y += roundf( font.getVertAlign(state->align, state->size ) ); + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + drawTextGlyph(font, glyph, x, y ); + addGlyphAdvance( &x, &subpixel, glyph ); + if( x > truncatepoint ) + break; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + x += ( subpixel >= 32 ); + if( extstring ) + drawText(x, y, extstring, 0 ); + + return x; + } + + + //// + + + // Measure text + int getTextWidth( const char *string, int stringlength ) + { + return getTextWidthTruncate(string, stringlength, ( (unsigned)1 << ( ( sizeof(int) * CHAR_BIT ) - 1 ) ) - 1 ); + } + + + int getTextWidthTruncate( const char *string, int stringlength, int truncatewidth ) + { + int subpixel, index, advance; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return 0; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + advance = 0; + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + if( advance > truncatewidth ) + break; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return advance + ( subpixel >= 32 ); + } + + int getTextBounds( int x, int y, const char *string, int stringlength, int *bounds ) + { + int subpixel, index; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMQuad q; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + int startx, advance; + int minx, miny, maxx, maxy; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return x; + font = state->font; + if( !( stringlength ) ) + stringlength = strlen( string ); + + // Align vertically + y += font.getVertAlign(state->align, state->size ); + + minx = maxx = x; + miny = maxy = y; + startx = x; + + subpixel = 0; + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &x, &subpixel ); + glyph->getQuad(x, y, q); + if( q.x0 < minx ) + minx = q.x0; + if( q.x1 > maxx ) + maxx = q.x1; + if( q.y0 < miny ) + miny = q.y0; + if( q.y1 > maxy ) + maxy = q.y1; + addGlyphAdvance( &x, &subpixel, glyph ); + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + advance = x - startx; + + /* Align horizontally */ + if( state->align & FM_ALIGN_RIGHT ) + { + minx -= advance; + maxx -= advance; + } + else if( state->align & FM_ALIGN_CENTER ) + { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + if( bounds ) + { + bounds[0] = minx; + bounds[1] = maxx; + bounds[2] = miny; + bounds[3] = maxy; + } + + return advance + ( subpixel >= 32 ); + } + + // Find text offset up to truncatewidth + int getTextTruncateOffset( int truncatewidth, const char *string, int stringlength, int extwidth, int *retextflag, int *retfullwidth ) + { + int subpixel, index, advance, truncatewidthext, truncateindex, extflag, fullwidth = 0; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + if( retextflag ) + *retextflag = 0; + if( retfullwidth ) + *retfullwidth = 0; + if( extwidth >= truncatewidth ) + return 0; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return 0; + font = state->font; + if( stringlength <= 0 ) + stringlength = strlen( string ); + + truncatewidthext = truncatewidth - extwidth; + + advance = 0; + subpixel = 0; + truncateindex = 0; + extflag = 0; + for( index = 0 ; ; index++ ) + { + if( index >= stringlength ) + { + truncateindex = index; + fullwidth = advance + ( subpixel >= 32 ); + break; + } + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + if( advance > truncatewidth ) + { + extflag = 1; + break; + } + if( advance <= truncatewidthext ) + { + truncateindex = index + 1; + fullwidth = advance + ( subpixel >= 32 ); + } + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + if( retfullwidth ) + { + if( extflag ) + *retfullwidth = fullwidth; + else + *retfullwidth = fullwidth; + } + if( retextflag ) + *retextflag = extflag; + + return truncateindex; + } + + // Find text offset nearest to the given width + int getTextNearestOffset( int targetwidth, const char *string, int stringlength ) + { + int subpixel, index, advance, truncateindex, distance, bestdistance; + FMState *state; + unichar codepoint; + uint32 utf8state; + FMGlyph *glyph; + int prevGlyphIndex; + int blurradius, blurscale; + FMFont font; + + state = &states[ nstates - 1 ]; + glyph = 0; + utf8state = 0; + prevGlyphIndex = -1; + blurradius = state->blurradius; + blurscale = state->blurscale; + if( !( state->font ) ) + return 0; + font = state->font; + if( stringlength <= 0 ) + stringlength = strlen( string ); + + advance = 0; + subpixel = 0; + truncateindex = 0; + bestdistance = abs( targetwidth ); + for( index = 0 ; index < stringlength ; index++ ) + { + if( decodeUTF8( (byte)string[index], &utf8state, &codepoint ) ) + continue; + glyph = getGlyph(font, codepoint, state->size, subpixel, blurradius, blurscale ); + if( glyph ) + { + font.addKerning(prevGlyphIndex, glyph, &advance, &subpixel ); + addGlyphAdvance( &advance, &subpixel, glyph ); + distance = abs( targetwidth - ( advance + ( subpixel >= 32 ) ) ); + if( distance > bestdistance ) + break; + bestdistance = distance; + truncateindex = index + 1; + } + prevGlyphIndex = ( glyph ? glyph->glyphindex : -1 ); + } + + return truncateindex; + } + + //// + + static void flush( bool rendererFlush ) + { + // Flush texture updates + if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) ) + { + renderer.updateTexture( dirtyrect, texdata ); + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + } + // Flush buffered glyphs + if( rendererFlush ) renderer.flush( ); + } + + // Flush buffered texture updates, renderer->updateTexture() + void flushUpdate( ) + { + flush(false ); + } + + //// + + // Text metrics + + void getFontExtent( int *retascent, int *retdescent ) + { + FMFont font; + FMState *state = &states[ nstates - 1 ]; + if(state->font) + { + font = state->font; + + if( retascent ) + *retascent = -(int)ceilf( font.ascender * (float)state->size ); + if( retdescent ) + *retdescent = -(int)floorf( font.descender * (float)state->size ); + } + } + + void fmGetFontLimits( int *retlimitminy, int *retlimitmaxy ) + { + FMFont font; + FMState *state; + + state = &states[ nstates - 1 ]; + if( !( state->font ) ) + return; + font = state->font; + + if( retlimitminy ) + *retlimitminy = -(int)ceilf( font.limitmaxy * state->size ); + if( retlimitmaxy ) + *retlimitmaxy = -(int)floorf( font.limitminy * state->size ); + + return; + } + + int getFontLineHeight( ) + { + FMFont font; + FMState *state; + + state = &states[ nstates - 1 ]; + if( !( state->font ) ) + return 0 ; + font = state->font; + return (int)ceilf( font.lineHeight * state->size ); + } + + + //// + + // Pull texture changes + const byte *getTextureData( int *width, int *height ) + { + if( width ) + *width = this.width; + if( height ) + *height = this.height; + return texdata; + } + + // Retrieve the dirty rectangle, telling fontmanager you will manually perform the update, returns 0 if no part of the texture requires an update + bool validateTexture( int *retdirtyrect ) + { + if( ( dirtyrect[0] < dirtyrect[2] ) && ( dirtyrect[1] < dirtyrect[3] ) ) + { + retdirtyrect[0] = dirtyrect[0]; + retdirtyrect[1] = dirtyrect[1]; + retdirtyrect[2] = dirtyrect[2]; + retdirtyrect[3] = dirtyrect[3]; + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + return true; + } + return false; + } + + // Returns current atlas size + void getAtlasSize( int *retwidth, int *retheight ) + { + *retwidth = this.width; + *retheight = this.height; + return; + } + + // Expands the atlas size + bool expandAtlas( int width, int height ) + { + width = Max( width, this.width ); + height = Max( height, this.height ); + + if( ( width == this.width ) && ( height == this.height ) ) + return true; + + // Flush all pending glyphs + flush(true); + + // Create new texture + if( renderer.resizeTexture( width, height ) ) + { + byte *data; + int i; + + // Copy old texture data over. + if( !( data = (byte *)malloc( width * height ) ) ) + return false; + for( i = 0 ; i < this.height ; i++ ) + { + byte * dst = &data[ i * width ]; + byte * src = &this.texdata[ i * this.width ]; + memcpy( dst, src, this.width ); + if( width > this.width ) + memset( dst+this.width, 0, width - this.width ); + } + if( height > this.height ) + memset( &data[ this.height * width ], 0, ( height - this.height ) * width ); + + delete this.texdata; + texdata = data; + + // Increase atlas size + atlas.expand( width, height ); + + // Add existing data as dirty. + dirtyrect[0] = 0; + dirtyrect[1] = 0; + dirtyrect[2] = this.width; + dirtyrect[3] = atlas.getAtlasMaxHeight(); + + this.width = width; + this.height = height; + widthinv = 1.0f / this.width; + heightinv = 1.0f / this.height; + + return true; + } + return false; + } + + // Reset the whole fm + bool resetAtlas( int width, int height ) + { + // Flush all pending glyphs + flush(true); + + // Create new texture + if(renderer.resizeTexture( width, height ) ) + { + // Reset atlas + atlas.reset( width, height ); + + // Clear texture data. + texdata = renew texdata byte[width * height]; + if(!texdata) return 0; + memset( this.texdata, 0, width * height ); + + // Reset dirty rect + dirtyrect[0] = width; + dirtyrect[1] = height; + dirtyrect[2] = 0; + dirtyrect[3] = 0; + + // Reset cached glyphs + for(font : fontList) + { + int i; + font.glyphcount = 0; + for( i = 0 ; i < FM_HASH_TABLE_SIZE ; i++ ) + font.hashtable[i] = -1; + } + renderer.resetImages( ); + + this.width = width; + this.height = height; + this.widthinv = 1.0f / this.width; + this.heightinv = 1.0f / this.height; + + // Add white rect at 0,0 for debug drawing. + #if FM_DEBUG_WHITERECT + fmAddWhiteRect(2, 2 ); + #endif + + return true; + } + return false; + } + + //// + bool initPathDraw( FMPathDraw pathdraw ) + { + FMState *state = &states[ nstates - 1 ]; + FMFont font = state->font; + if(font) + { + pathdraw.prevGlyphIndex = -1; + pathdraw.middleAlign = font.middleAlign * (float)state->size; + return true; + } + return false; + } + + float pathDrawCharacter( FMPathDraw pathdraw, float x, float y, float vectorx, float vectory, int unicode ) + { + int subpixel; + FMState *state = &states[ nstates - 1 ]; + int blurradius = state->blurradius; + int blurscale = state->blurscale; + FMFont font = state->font; + FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale ); + if( glyph ) + { + subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex ); + drawTextGlyphFloat(font, glyph, x, y, vectorx, vectory, (float)subpixel * (1.0f/64.0f), pathdraw.middleAlign ); + subpixel += glyph->advance; + pathdraw.prevGlyphIndex = glyph->glyphindex; + } + else + { + subpixel = 0; + pathdraw.prevGlyphIndex = -1; + } + + return (float)subpixel * (1.0f/64.0f); + } + + float pathDrawPredictAdvance( FMPathDraw pathdraw, unichar unicode ) + { + int subpixel = 0; + FMState *state = &states[ nstates - 1 ]; + FMFont font = state->font; + int blurradius = state->blurradius; + int blurscale = state->blurscale; + FMGlyph *glyph = getGlyph(font, unicode, state->size, 0, blurradius, blurscale ); + if( glyph ) + { + subpixel = font.ftFont.getGlyphKernAdvance( pathdraw.prevGlyphIndex, glyph->glyphindex ); + subpixel += glyph->advance; + } + return (float)subpixel * (1.0f/64.0f); + } +} diff --git a/ecere/src/gfx/newFonts/fontRenderer.ec b/ecere/src/gfx/newFonts/fontRenderer.ec new file mode 100644 index 0000000..00f361b --- /dev/null +++ b/ecere/src/gfx/newFonts/fontRenderer.ec @@ -0,0 +1,196 @@ +/* ***************************************************************************** + * Original Version Copyright (c) 2007-2014 Alexis Naveros. + * + * Ecere Corporation has unlimited/unrestricted rights. + * *****************************************************************************/ +import "Color" + +#include + +#if defined(_GLES) + #define ES1_1 +#else + //#define SHADERS +#endif + +#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) +# if defined(SHADERS) +# include "gl_core_3_3.h" +# else +# include "gl_compat_4_4.h" +# endif +#endif + +#include "cc.h" +#include "mm.h" + +import "fontManager" +import "textureManager" +import "drawManager" + +public class FontRenderer : FontManagerRenderer +{ + DrawManager dm; + Texture texture; + int textureWidth, textureHeight; + + int imagecount; + int imageAlloc; + DMImage *imageList; + + ColorAlpha stateColor; + ColorAlpha stateCursorColor; + uint32 stateLayer; + + imageAlloc = 512; + imageList = new DMImage[imageAlloc]; + + stateColor = white; + stateCursorColor = white; + stateLayer = DM_LAYER_ABOVE; + +public: + + property DrawManager drawManager { set { dm = value; } } + + ~FontRenderer() + { + delete texture; + delete imageList; + } + + bool createTexture( int width, int height ) + { + IMGImage image + { + format = { width = width, height = height, type = grayScale, bytesPerPixel = 1, bytesPerLine = width }; + }; + + delete texture; + + texture = { 0 << DM_TEXTURE_ORDER_SHIFT }; + texture.build(image, 0, 0.0, 0 ); + + textureWidth = width; + textureHeight = height; + return true; + } + + int resizeTexture( int width, int height ) + { + int retval; + + // Reuse create to resize too. + delete imageList; + imagecount = 0; + imageAlloc = 512; + imageList = new DMImage[imageAlloc]; + + retval = createTexture( width, height ); + return retval; + } + + void updateTexture( int *rect, const byte* data ) + { + if(texture) + { + int w = rect[2] - rect[0]; + int h = rect[3] - rect[1]; + + glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT ); + glPushAttrib( GL_TEXTURE_BIT ); + glBindTexture( GL_TEXTURE_2D, texture.glTex ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, textureWidth ); + glPixelStorei( GL_UNPACK_SKIP_PIXELS, rect[0] ); + glPixelStorei( GL_UNPACK_SKIP_ROWS, rect[1] ); + glTexSubImage2D( GL_TEXTURE_2D, 0, rect[0], rect[1], w, h, GL_RED, GL_UNSIGNED_BYTE, data ); + glPopAttrib(); + glPopClientAttrib(); + + #if 0 + IMGImage image; + image.format.width = textureWidth; + image.format.height = textureHeight; + image.format.type = IMG_FORMAT_TYPE_GRAYSCALE; + image.format.bytesPerPixel = 1; + image.format.bytesPerLine = image.format.width * image.format.bytesPerPixel; + image.data = data; + imgWritePngFile( "zzz2.png", &image, 1.0 ); + #endif + } + } + + void flush( ) + { + dm.flushImages( ); + } + + int registerImage( int offsetx, int offsety, int width, int height ) + { + int imageindex = imagecount; + DMImage *image; + + if( imagecount >= imageAlloc ) + { + imageAlloc <<= 1; + imageList = renew imageList DMImage[imageAlloc]; + } + imagecount++; + + image = &imageList[ imageindex ]; + #if 1 + image->defineImage( texture, offsetx, offsety, width, height, 1, DM_PROGRAM_ALPHABLEND, stateLayer ); + #else + image->defineImage( texture, offsetx, offsety, width, height, 1, DM_PROGRAM_NORMAL, stateLayer ); + #endif + + return imageindex; + } + + void drawImage( int targetx, int targety, int imageindex ) + { + DMImage *image = &imageList[ imageindex ]; + dm.drawImage( image, targetx, targety, image->sizex, image->sizey, stateColor ); + } + + void drawImageCursor( int targetx, int targety, int imageindex ) + { + DMImage *image = &imageList[ imageindex ]; + dm.drawImage( image, targetx, targety, image->sizex, image->sizey, stateCursorColor ); + } + + void drawImageAlt( byte *texdata, int targetx, int targety, int offsetx, int offsety, int width, int height ) + { + + } + + void drawImageFloat( float targetx, float targety, float angsin, float angcos, int imageindex ) + { + DMImage *image = &imageList[ imageindex ]; + + /* 0.2588190451, 0.965925826289 */ + + dm.drawImageFloat( image, targetx, targety, (float)image->sizex, (float)image->sizey, angsin, angcos, stateColor ); + } + + void resetImages( ) + { + imagecount = 0; + } + + void setColor( ColorAlpha color ) + { + stateColor = { color.a, { color.color.b, color.color.g, color.color.r } }; + } + + void setCursorColor( ColorAlpha color ) + { + stateCursorColor = { color.a, { color.color.b, color.color.g, color.color.r } }; + } + + void setLayer( uint32 layerIndex ) + { + stateLayer = layerIndex; + } +} diff --git a/ecere/src/gfx/newFonts/textureManager.ec b/ecere/src/gfx/newFonts/textureManager.ec new file mode 100644 index 0000000..cdca66f --- /dev/null +++ b/ecere/src/gfx/newFonts/textureManager.ec @@ -0,0 +1,154 @@ +import "instance" + +#include +#include + +#if defined(_GLES) + #define ES1_1 +#else + #define SHADERS +#endif + +#if !defined(__ANDROID__) && !defined(__EMSCRIPTEN__) && !defined(__ODROID__) +# if defined(SHADERS) +# include "gl_core_3_3.h" +# else +# include "gl_compat_4_4.h" +# endif +#endif + +// TOFIX: +int GL_ARB_texture_non_power_of_two = 1; +int GL_EXT_texture_filter_anisotropic = 1; + +#include "cc.h" + +struct IMGFormat +{ + int width; + int height; + IMGFormatType type; + int bytesPerPixel; + int bytesPerLine; +}; + +enum IMGFormatType { any, rgb24, bfr24, rgba32, bgra32, grayScale }; + +struct IMGImage +{ + IMGFormat format; + void *data; +}; + +class TextureFlags : uint32 { bool invalid:1; } + +class Texture : struct +{ + GLuint glTex; + int width; + int height; + float widthinv; + float heightinv; + TextureFlags flags; + uint32 orderMask; + + flags = { invalid = true }; + +public: + + property uint32 orderMask { set { orderMask = value; } } + + static bool setData( IMGImage image, int internalformat, int mipmapmode, float anisotropy, int maxresolution ) + { + int width, height; + int glformat; + + if( image.format.bytesPerPixel == 1 ) + glformat = GL_RED; + else if( image.format.bytesPerPixel == 2 ) + glformat = GL_RG; + else if( image.format.bytesPerPixel == 3 ) + glformat = GL_RGB; + else if( image.format.bytesPerPixel == 4 ) + glformat = GL_RGBA; + else + { + fprintf( stderr, "ERROR: Bad image format.\n" ); + return false; + } + if( internalformat == -1 ) + internalformat = glformat; + + width = image.format.width; + height = image.format.height; + if( !( GL_ARB_texture_non_power_of_two ) ) + { + if( !( ccIsPow2Int32( width ) ) || !( ccIsPow2Int32( height ) ) ) + { + fprintf( stderr, "ERROR: Non-power of two texture used and GL_ARB_texture_non_power_of_two not supported.\n" ); + return false; + } + } + + glGenTextures( 1, &this.glTex ); + glBindTexture( GL_TEXTURE_2D, this.glTex ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, internalformat, image.format.width, image.format.height, 0, glformat, GL_UNSIGNED_BYTE, image.data ); + + if( ( GL_EXT_texture_filter_anisotropic ) && ( anisotropy > 1.0 ) ) + ; //glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, anisotropy ); + + this.width = width; + this.height = height; + widthinv = 1.0f / (float)width; + heightinv = 1.0f / (float)height; + + return true; + } + + bool build( IMGImage image, int mipmapmode, float anisotropy, int maxresolution ) + { + if(!flags.invalid) + glDeleteTextures( 1, &glTex ); + if( !setData( image, -1, mipmapmode, anisotropy, maxresolution ) ) + { + fprintf( stderr, "ERROR: Failed to create texture.\n" ); + return false; + } + flags.invalid = false; + + return true; + } + + bool load( const String path, int mipmapmode, float anisotropy, int maxresolution ) + { + bool result = false; + IMGImage image; + + #if TEXMG_ENABLE_PNG_SUPPORT + if( !( imgReadPngFile( &image, path, 0 ) ) ) + { + fprintf( stderr, "ERROR: Loading texture %s failed.\n", path ); + return false; + } + #else + fprintf( stderr, "ERROR: File support disabled, %s:%d\n", __FILE__, __LINE__ ); + return false; + #endif + + result = build( image, mipmapmode, anisotropy, maxresolution ); + if( !result ) + fprintf( stderr, "ERROR: Bad format for texture %s.\n", path ); + delete image.data; + + return result; + } + + ~Texture() + { + if( !flags.invalid ) + glDeleteTextures( 1, &glTex ); + } +} -- 1.8.3.1