- 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
}
]
},
+ {
+ "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",
--- /dev/null
+/* *****************************************************************************
+ * 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;
+ }
+}
--- /dev/null
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <time.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "cc.h"
+
+#if CC_UNIX
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/time.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/utsname.h> /* For uname() */
+ #include <dirent.h> /* For readdir() */
+ #include <sys/statvfs.h> /* For statvfs( ) */
+#elif CC_WINDOWS
+ #include <windows.h>
+ #include <direct.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+#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 <bjoern@hoehrmann.de> */
+/* 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;
+}
--- /dev/null
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+#include <sys/time.h>
+#include <stdarg.h>
+
+
+#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)-_s))>>shift;})
+#define CC_SHIFTDIV_INT16(value,shift) ({uint16_t _s=((uint16_t)value)>>15;((int16_t)((value)+(_s<<shift)-_s))>>shift;})
+#define CC_SHIFTDIV_INT32(value,shift) ({uint32_t _s=((uint32_t)value)>>31;((int32_t)((value)+(_s<<shift)-_s))>>shift;})
+#define CC_SHIFTDIV_INT64(value,shift) ({uint64_t _s=((uint64_t)value)>>63;((int64_t)((value)+(_s<<shift)-_s))>>shift;})
+
+#define CC_SHIFTDIVROUND(value,shift) ((value>>shift)+(((value&((1<<shift)-1))<<1)>=(1<<shift)))
+
+#define CC_SHIFTDIVROUND_INT8(value,shift) ((value>>shift)+((((value&((1<<shift)-1))-((uint8_t)value>>7))<<1)>=(1<<shift)))
+#define CC_SHIFTDIVROUND_INT16(value,shift) ((value>>shift)+((((value&((1<<shift)-1))-((uint16_t)value>>15))<<1)>=(1<<shift)))
+#define CC_SHIFTDIVROUND_INT32(value,shift) ((value>>shift)+((((value&((1<<shift)-1))-((uint32_t)value>>31))<<1)>=(1<<shift)))
+#define CC_SHIFTDIVROUND_INT64(value,shift) ((value>>shift)+((((value&((1<<shift)-1))-((uint64_t)value>>63))<<1)>=(1<<shift)))
+
+
+#define CC_NUMBITS2(n) ((n&2)?1:0)
+#define CC_NUMBITS4(n) ((n&(0xC))?(2+CC_NUMBITS2(n>>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();
--- /dev/null
+/* *****************************************************************************
+ * 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
+
--- /dev/null
+/* *****************************************************************************
+ * 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
+
--- /dev/null
+#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
--- /dev/null
+/* *****************************************************************************
+ * 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 <pthread.h>
+ #include <sched.h>
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <time.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <assert.h>
+
+
+#include "cpuconfig.h"
+#include "cc.h"
+#include "mm.h"
+
+
+#if defined(MM_UNIX)
+ #include <fcntl.h>
+ #include <dirent.h>
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
+/*
+ #include <sys/sysinfo.h>
+*/
+#elif defined(MM_WINDOWS)
+ #include <windows.h>
+#endif
+
+#if _POSIX_MAPPED_FILES > 0
+ #include <sys/mman.h>
+ #define MM_ZONE_SUPPORT 1
+#endif
+
+#if defined(MM_LINUX)
+ #include <sys/sysinfo.h>
+ #include <utmpx.h>
+#endif
+
+#if defined(MM_WIN32)
+ #include <time.h>
+ #include <windows.h>
+#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)<<head->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
--- /dev/null
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/**
+ * @file
+ *
+ * Global memory management header.
+ */
+
+#ifndef MM_H
+#define MM_H
+
+#include <stdio.h>
+
+#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 <numa.h>
+#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 <sys/time.h>
+
+#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
--- /dev/null
+/* *****************************************************************************
+ * 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
+
+
--- /dev/null
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <math.h>
+
+#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;
+}
--- /dev/null
+/* *****************************************************************************
+ * 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;
+}
--- /dev/null
+/* *****************************************************************************
+ * 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 <pthread.h>
+ #include <unistd.h>
+ #include <sched.h>
+ #include <limits.h>
+
+ #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
+
+
--- /dev/null
+import "instance"
+
+#include <stdio.h>
+#include <math.h>
+
+#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<<DM_LAYER_ORDER_BITS;
+define DM_PROGRAM_COUNT = 1<<DM_PROGRAM_ORDER_BITS;
+
+define DM_CONTEXT_DRAW_BUFFER_COUNT = 64;
+define DM_CONTEXT_DRAW_BUFFER_VERTEX_ALLOC = 1024;
+
+
+/* Range is from zero to DM_LAYER_COUNT */
+enum DMLayer
+{
+ DM_LAYER_0_BOTTOM,
+ DM_LAYER_1_BOTTOM,
+ DM_LAYER_2_BELOW,
+ DM_LAYER_3_BELOW,
+ DM_LAYER_4_BELOW,
+ DM_LAYER_5_NORMAL,
+ DM_LAYER_6_NORMAL,
+ DM_LAYER_7_NORMAL,
+ DM_LAYER_8_ABOVE,
+ DM_LAYER_9_ABOVE,
+ DM_LAYER_10_ABOVE,
+ DM_LAYER_11_OVERLAY,
+ DM_LAYER_12_OVERLAY,
+ DM_LAYER_13_OVERLAY,
+ DM_LAYER_14_TOP,
+ DM_LAYER_15_TOP,
+};
+
+define DM_LAYER_BOTTOM = DMLayer::DM_LAYER_0_BOTTOM;
+define DM_LAYER_BELOW = DMLayer::DM_LAYER_3_BELOW;
+define DM_LAYER_NORMAL = DMLayer::DM_LAYER_6_NORMAL;
+define DM_LAYER_ABOVE = DMLayer::DM_LAYER_9_ABOVE;
+define DM_LAYER_TOP = DMLayer::DM_LAYER_15_TOP;
+
+define DM_PROGRAM_NORMAL = 0;
+define DM_PROGRAM_ALPHABLEND = 1;
+
+#ifdef _DEBUG
+static inline void OpenGLErrorCheck( const char *file, int line )
+{
+ int error = glGetError();
+ if( error != GL_NO_ERROR )
+ printf( "ERROR %d at %s:%d\n", error, file, line );
+}
+
+#define ERRORCHECK() OpenGLErrorCheck(__FILE__,__LINE__)
+#else
+#define ERRORCHECK()
+#endif
+
+define rotationNormFactor = 32767.0f;
+
+
+static GLuint dmCreateShader( GLenum type, const char *shadersource, const char *optionstring )
+{
+ GLuint shader;
+ GLint status;
+ GLsizei loglength;
+ char infolog[8192];
+ const GLchar *sourcearray[2];
+
+ shader = glCreateShader( type );
+ if( !( shader ) )
+ return 0;
+
+ if( !( optionstring ) )
+ optionstring = "";
+ sourcearray[0] = optionstring;
+ sourcearray[1] = shadersource;
+ glShaderSource( shader, 2, sourcearray, NULL );
+ glCompileShader( shader );
+ glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
+ if( status != GL_TRUE )
+ {
+ fprintf( stderr, "ERROR: Failed to compile shader\n" );
+ glGetShaderInfoLog( shader, 8192, &loglength,infolog );
+ fprintf( stderr, "ERROR: \n%s\n\n", infolog );
+ glDeleteShader( shader );
+ return 0;
+ }
+ return shader;
+}
+
+
+static bool dmCreateProgram( DMProgram *program, const char *vertexsource, const char *fragmentsource, char *optionstring )
+{
+ GLint status;
+ GLsizei loglength;
+ char infolog[8192];
+
+ program->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;
+ }
+}
--- /dev/null
+/* *****************************************************************************
+ * Original Version Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+import "LinkList"
+import "File"
+
+import "atlasBuilder"
+
+#include <stdlib.h>
+#include <math.h>
+
+#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 <ft2build.h>
+
+#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))<<FM_DEF_CODEPOINT_SHIFT) | (((uint64)(size))<<FM_DEF_SIZE_SHIFT) | (((uint64)(subpixel))<<FM_DEF_SUBPIXEL_SHIFT) | (((uint64)(blurradius))<<FM_DEF_BLURRADIUS_SHIFT) | (((uint64)(blurscale))<<FM_DEF_BLURSCALE_SHIFT) )
+
+#define FM_SIZE_MAX ((1<<FM_DEF_SIZE_BITS)-1)
+#define FM_BLUR_RADIUS_MAX ((1<<FM_DEF_BLURRADIUS_BITS)-1)
+#define FM_BLUR_SCALE_MAX ((1<<FM_DEF_BLURSCALE_BITS)-1)
+
+#define FM_GLYPH_CODEPOINT_CURSOR (0x1)
+#define FM_GLYPH_CODEPOINT_REPLACEMENT (0xfffd)
+
+#define FM_ALIGN_LEFT (0x0)
+#define FM_ALIGN_CENTER (0x1)
+#define FM_ALIGN_RIGHT (0x2)
+
+#define FM_ALIGN_BASELINE (0x0)
+#define FM_ALIGN_TOP (0x4)
+#define FM_ALIGN_MIDDLE (0x8)
+#define FM_ALIGN_BOTTOM (0x10)
+
+
+public struct FMPathDraw
+{
+ int prevGlyphIndex;
+ float middleAlign;
+};
+
+struct FMQuad { int x0, y0, x1, y1; };
+
+struct FMGlyph
+{
+ int glyphindex;
+ uint64 glyphdef;
+ short x0, y0, x1, y1;
+ short advance, offsetx, offsety;
+ int imageIndex;
+ int listnext;
+
+ static void getQuad( float x, float y, FMQuad q )
+ {
+ int rx = (int)(x + offsetx);
+ int ry = (int)(y + offsety);
+ q = { rx, ry, rx + x1 - x0, ry + y1 - y0 };
+ }
+};
+
+public class FMFont : struct
+{
+ public LinkElement link;
+ FMFreeTypeFont ftFont;
+ void *fontdata;
+ float ascender;
+ float descender;
+ float middleAlign;
+ float lineHeight;
+ float limitminy, limitmaxy;
+ FMGlyph *glyphs;
+ int glyphalloc;
+ int glyphcount;
+ int hashtable[FM_HASH_TABLE_SIZE];
+
+ ~FMFont()
+ {
+ ftFont.free();
+ delete fontdata;
+ delete glyphs;
+ }
+
+ static FMGlyph *allocGlyph( )
+ {
+ if( glyphcount >= 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<FMFont, link = link> 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);
+ }
+}
--- /dev/null
+/* *****************************************************************************
+ * Original Version Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+import "Color"
+
+#include <math.h>
+
+#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;
+ }
+}
--- /dev/null
+import "instance"
+
+#include <stdio.h>
+#include <math.h>
+
+#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 );
+ }
+}