ecere/gfx/newFonts: Initial eC port of font engine by Alexis Naveros
authorAlexis Naveros <alexis@ecere.com>
Sat, 21 Nov 2015 09:07:11 +0000 (04:07 -0500)
committerJerome St-Louis <jerome@ecere.com>
Mon, 21 Nov 2016 14:18:38 +0000 (09:18 -0500)
- 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

17 files changed:
ecere/ecere.epj
ecere/src/gfx/newFonts/atlasBuilder.ec [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/cc.c [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/cc.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/cchybridsort.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/ccmergesort.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/cpuconfig.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mm.c [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mm.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mmatomic.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mmbitmap.c [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mmbitmap.h [new file with mode: 0644]
ecere/src/gfx/newFonts/cc/mmthread.h [new file with mode: 0644]
ecere/src/gfx/newFonts/drawManager.ec [new file with mode: 0644]
ecere/src/gfx/newFonts/fontManager.ec [new file with mode: 0644]
ecere/src/gfx/newFonts/fontRenderer.ec [new file with mode: 0644]
ecere/src/gfx/newFonts/textureManager.ec [new file with mode: 0644]

index c978092..2ca0c6c 100644 (file)
@@ -2150,6 +2150,35 @@ if distributed with the Ecere SDK Windows installer.
                         }
                      ]
                   },
+                  {
+                     "Folder" : "newFonts",
+                     "Files" : [
+                        {
+                           "Folder" : "cc",
+                           "Files" : [
+                              "cc.c",
+                              "cc.h",
+                              "cchybridsort.h",
+                              "ccmergesort.h",
+                              "cpuconfig.h",
+                              "mm.c",
+                              "mm.h",
+                              "mmatomic.h",
+                              "mmthread.h"
+                           ]
+                        },
+                        "atlasBuilder.ec",
+                        "drawManager.ec",
+                        "fontManager.ec",
+                        "fontRenderer.ec",
+                        "textureManager.ec"
+                     ],
+                     "Options" : {
+                        "IncludeDirs" : [
+                           "src/gfx/newFonts/cc"
+                        ]
+                     }
+                  },
                   "Bitmap.ec",
                   "BitmapResource.ec",
                   "Color.ec",
diff --git a/ecere/src/gfx/newFonts/atlasBuilder.ec b/ecere/src/gfx/newFonts/atlasBuilder.ec
new file mode 100644 (file)
index 0000000..2ee8353
--- /dev/null
@@ -0,0 +1,225 @@
+/* *****************************************************************************
+ * Original Version Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+import "instance"
+
+struct AtlasNode { short x, y, width; };
+
+class AtlasBuilder
+{
+  int width, height;
+  AtlasNode *nodes;
+  int nodecount;
+  int nodealloc;
+
+   // Create atlas of given width and height, internal node count will grow as necessary
+   bool create( int w, int h, int nodealloc )
+   {
+      bool result = false;
+
+      width = w;
+      height = h;
+
+      // Allocate space for skyline nodes
+      if( nodealloc < 32 )
+         nodealloc = 32;
+      if( ( nodes = new0 AtlasNode[nodealloc] ) )
+      {
+         nodecount = 0;
+         this.nodealloc = nodealloc;
+
+         // Init root node.
+         nodes[0].width = (short)w;
+         nodecount++;
+
+         result = true;
+     }
+     return result;
+   }
+
+   ~AtlasBuilder( )
+   {
+      delete nodes;
+   }
+
+   static bool insertNode( int nodeindex, int x, int y, int width )
+   {
+      bool result = false;
+
+      // Insert node
+      if( nodecount >= nodealloc )
+      {
+         nodealloc <<= 1;
+         nodes = renew nodes AtlasNode[nodealloc];
+      }
+      if(nodes)
+      {
+         int i;
+         for( i = nodecount; i > nodeindex ; i-- )
+            nodes[i] = nodes[i-1];
+         nodes[nodeindex].x = (short)x;
+         nodes[nodeindex].y = (short)y;
+         nodes[nodeindex].width = (short)width;
+         nodecount++;
+
+         result = true;
+      }
+      return result;
+   }
+
+   static void removeNode( int nodeindex )
+   {
+      if( nodecount)
+      {
+        int i;
+        for( i = nodeindex ; i < nodecount-1 ; i++ )
+            nodes[i] = nodes[i+1];
+         nodecount--;
+      }
+   }
+
+   static bool addSkylineLevel( int nodeindex, int x, int y, int width, int height )
+   {
+      int i, shrink;
+
+      // Insert new node
+      if( insertNode( nodeindex, x, y+height, width ) == 0 )
+         return false;
+
+      // Delete skyline segments that fall under the shadow of the new segment.
+      for( i = nodeindex+1 ; i < nodecount ; i++ )
+      {
+         if( nodes[i].x < ( nodes[i-1].x + nodes[i-1].width ) )
+         {
+            shrink = nodes[i-1].x + nodes[i-1].width - nodes[i].x;
+            nodes[i].x += (short)shrink;
+            nodes[i].width -= (short)shrink;
+            if( nodes[i].width <= 0 )
+            {
+               removeNode( i );
+               i--;
+            }
+            else
+               break;
+         }
+         else
+            break;
+      }
+
+      // Merge same height skyline segments that are next to each other.
+      for( i = 0 ; i < nodecount - 1 ; )
+      {
+         if( nodes[i].y != nodes[i+1].y )
+            i++;
+         else
+         {
+            nodes[i].width += nodes[i+1].width;
+            removeNode( i+1 );
+         }
+      }
+      return 1;
+   }
+
+   static int rectFits( int nodeindex, int width, int height )
+   {
+     int x, y, rem;
+     /* Checks if there is enough space at the location of skyline span 'i', */
+     /* and return the max height of all skyline spans under that at that location, */
+     /* (think tetris block being dropped at that position). Or -1 if no space found. */
+     x = nodes[nodeindex].x;
+     y = nodes[nodeindex].y;
+     if( ( x + width ) > this.width )
+       return -1;
+     for( rem = width ; rem > 0 ; nodeindex++ )
+     {
+       if( nodeindex == nodecount )
+         return -1;
+       if( y < this.nodes[nodeindex].y )
+         y = nodes[nodeindex].y;
+       if( ( y + height ) > this.height )
+         return -1;
+       rem -= nodes[nodeindex].width;
+     }
+     return y;
+   }
+
+   // Place a rectangle of specified dimensions, return 1 on success, retx&rety store offsets
+   bool addRect( int width, int height, int *retx, int *rety )
+   {
+     int besth, bestw, besti, bestx, besty, y, nodeindex;
+
+     besth = this.height;
+     bestw = this.width;
+     besti = -1;
+     bestx = -1;
+     besty = -1;
+
+     // Bottom left fit heuristic.
+     for( nodeindex = 0 ; nodeindex < this.nodecount ; nodeindex++ )
+     {
+       y = rectFits( nodeindex, width, height );
+       if( y != -1 )
+       {
+         if( ( ( y + height ) < besth ) || ( ( ( y + height ) == besth ) && ( this.nodes[nodeindex].width < bestw ) ) )
+         {
+           besti = nodeindex;
+           bestw = this.nodes[nodeindex].width;
+           besth = y + height;
+           bestx = this.nodes[nodeindex].x;
+           besty = y;
+         }
+       }
+     }
+
+     if( besti == -1 )
+       return 0;
+
+     // Perform the actual packing.
+     if( addSkylineLevel( besti, bestx, besty, width, height ) == 0 )
+       return 0;
+
+     *retx = bestx;
+     *rety = besty;
+
+     return 1;
+   }
+
+   // Expand atlas to given dimensions
+   void expand( int width, int height )
+   {
+     // Insert node for empty space
+     if( width > this.width )
+       insertNode( nodecount, this.width, 0, width - this.width );
+     this.width = width;
+     this.height = height;
+   }
+
+   // Clean up the atlas
+   void reset( int width, int height )
+   {
+     this.width = width;
+     this.height = height;
+     nodecount = 0;
+
+     // Init root node.
+     nodes[0].x = 0;
+     nodes[0].y = 0;
+     nodes[0].width = (short)width;
+     nodecount++;
+   }
+
+   // Return the maximum y value of all allocated rectangles
+   int getAtlasMaxHeight()
+   {
+      int i, maxy = 0;
+
+      for( i = 0 ; i < nodecount ; i++ )
+      {
+         if( maxy < nodes[i].y )
+            maxy = nodes[i].y;
+      }
+      return maxy;
+   }
+}
diff --git a/ecere/src/gfx/newFonts/cc/cc.c b/ecere/src/gfx/newFonts/cc/cc.c
new file mode 100644 (file)
index 0000000..3b38b60
--- /dev/null
@@ -0,0 +1,2999 @@
+/* *****************************************************************************
+ * 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, &paramlen, &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, &paramlen, &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;
+}
diff --git a/ecere/src/gfx/newFonts/cc/cc.h b/ecere/src/gfx/newFonts/cc/cc.h
new file mode 100644 (file)
index 0000000..17d5c9c
--- /dev/null
@@ -0,0 +1,1516 @@
+/* *****************************************************************************
+ * 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();
diff --git a/ecere/src/gfx/newFonts/cc/cchybridsort.h b/ecere/src/gfx/newFonts/cc/cchybridsort.h
new file mode 100644 (file)
index 0000000..9be1696
--- /dev/null
@@ -0,0 +1,302 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/*
+
+Templates C style!
+
+#include this whole file with the following definitions set:
+
+#define HSORT_MAIN MyInlinedSortFunction
+#define HSORT_CMP MyComparisonFunction
+#define HSORT_TYPE int
+
+*/
+
+
+#ifndef CC_HSORT_INLINE
+
+ #define CC_HSORT_SWAP(a,b) (__extension__({temp=table[b];table[b]=table[a];table[a]=temp;}))
+ #define CC_HSORT_STACK_DEPTH (512)
+ #define CC_HSORT_MIN_QSORT_COUNT (5)
+
+typedef struct
+{
+  void *table;
+  int count;
+  int depth;
+} ccHybridSortStack;
+
+ #define CC_HSORT_INLINE
+ #define CC_HSORT_STACK_DEPTH (512)
+
+#endif
+
+
+#ifndef HSORT_COPY
+ #define HSORT_COPY(d,s) (*(d)=*(s))
+ #define CC_HSORT_COPY
+#endif
+
+
+#ifdef HSORT_CONTEXT
+ #define HSORT_CONTEXT_PARAM , HSORT_CONTEXT context
+ #define HSORT_CONTEXT_PASS context,
+ #define HSORT_CONTEXT_PASSLAST , context
+#else
+ #define HSORT_CONTEXT_PARAM
+ #define HSORT_CONTEXT_PASS
+ #define HSORT_CONTEXT_PASSLAST
+#endif
+
+
+/* Build merge sort pipeline */
+#define HSORT_MERGE_FUNCX(n) n##Merge
+#define HSORT_MERGE_FUNC(n) HSORT_MERGE_FUNCX(n)
+#define MSORT_MAIN HSORT_MERGE_FUNC(HSORT_MAIN)
+#define MSORT_CMP HSORT_CMP
+#define MSORT_TYPE HSORT_TYPE
+#ifdef HSORT_CONTEXT
+ #define MSORT_CONTEXT HSORT_CONTEXT
+#endif
+#include "ccmergesort.h"
+#undef MSORT_MAIN
+#undef MSORT_CMP
+#undef MSORT_TYPE
+#ifdef HSORT_CONTEXT
+ #undef MSORT_CONTEXT
+#endif
+
+
+#define HSORT_PART_FUNCX(n) n##Part
+#define HSORT_PART_FUNC(n) HSORT_PART_FUNCX(n)
+
+static void HSORT_PART_FUNC(HSORT_MAIN)( HSORT_TYPE *table, int count HSORT_CONTEXT_PARAM )
+{
+  HSORT_TYPE temp;
+  if( count == 4 )
+  {
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) )
+      CC_HSORT_SWAP( 1, 0 );
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) )
+      CC_HSORT_SWAP( 3, 2 );
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) )
+    {
+      temp = table[2];
+      table[2] = table[1];
+      table[1] = table[0];
+      table[0] = temp;
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) )
+      {
+        CC_HSORT_SWAP( 3, 2 );
+        if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) )
+          CC_HSORT_SWAP( 2, 1 );
+      }
+    }
+    else
+    {
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) )
+      {
+        CC_HSORT_SWAP( 2, 1 );
+        if( HSORT_CMP( HSORT_CONTEXT_PASS &table[2], &table[3] ) )
+          CC_HSORT_SWAP( 3, 2 );
+      }
+    }
+  }
+  else if( count == 3 )
+  {
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) )
+    {
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) )
+      {
+        /* [1]>[0], [2]>[1] = [2]>[1]>[0] */
+        CC_HSORT_SWAP( 2, 0 );
+      }
+      else
+      {
+        if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) )
+        {
+          /* [1]>[0], [2]<[1], [2]>[0] = [1]>[2]>[0] */
+          temp = table[0];
+          table[0] = table[1];
+          table[1] = table[2];
+          table[2] = temp;
+        }
+        else
+        {
+          /* [1]>[0], [2]<[1], [2]<[0] = [1]>[0]>[2] */
+          CC_HSORT_SWAP( 1, 0 );
+        }
+      }
+    }
+    else
+    {
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &table[1], &table[2] ) )
+      {
+        if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[2] ) )
+        {
+          /* [1]<[0], [2]>[1], [2]>[0] = [2]>[0]>[1] */
+          temp = table[2];
+          table[2] = table[1];
+          table[1] = table[0];
+          table[0] = temp;
+        }
+        else
+        {
+          /* [1]<[0], [2]>[1], [2]<[0] = [0]>[2]>[1] */
+          CC_HSORT_SWAP( 1, 2 );
+        }
+      }
+      else
+      {
+        /* [1]<[0], [2]<[1] = [0]>[1]>[2] */
+      }
+    }
+  }
+  else if( count == 2 )
+  {
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[1] ) )
+      CC_HSORT_SWAP( 1, 0 );
+  }
+  return;
+}
+
+
+static void HSORT_MAIN( HSORT_TYPE *table, HSORT_TYPE *tmp, int count HSORT_CONTEXT_PARAM, uint32_t randmask )
+{
+  int depth, depthmax, tmpcountalloc;
+  ssize_t i, pivotindex, leftcount, rightcount, highindex, pivotoffset;
+  HSORT_TYPE temp;
+  HSORT_TYPE *mergesrc;
+  HSORT_TYPE pivot;
+  ccHybridSortStack stack[CC_HSORT_STACK_DEPTH];
+  ccHybridSortStack *sp;
+#ifdef HSORT_PIVOT_STORE
+  HSORT_PIVOT_STORE pivotstore;
+#endif
+
+  if( count <= 1 )
+    return;
+
+  depth = 0;
+  depthmax = 1 + ccLog2Int32( count );
+  tmpcountalloc = count;
+
+  sp = stack;
+  for( ; ; )
+  {
+    if( count < CC_HSORT_MIN_QSORT_COUNT )
+    {
+      HSORT_PART_FUNC(HSORT_MAIN)( table, count HSORT_CONTEXT_PASSLAST );
+      if( sp == stack )
+        break;
+      sp--;
+      table = sp->table;
+      count = sp->count;
+      depth = sp->depth;
+      continue;
+    }
+
+    /* Pathological case, switch to merge sort */
+    if( depth >= depthmax )
+    {
+      if( !( tmp ) )
+      {
+        tmp = malloc( tmpcountalloc * sizeof(HSORT_TYPE) );
+        tmpcountalloc = 0;
+      }
+      mergesrc = HSORT_MERGE_FUNC(HSORT_MAIN)( table, tmp, count HSORT_CONTEXT_PASSLAST );
+      if( mergesrc != table )
+        memcpy( table, mergesrc, count * sizeof(HSORT_TYPE) );
+      if( sp == stack )
+        break;
+      sp--;
+      table = sp->table;
+      count = sp->count;
+      depth = sp->depth;
+      continue;
+    }
+
+    /* Select pivot */
+    randmask += count;
+    pivotindex = 1 + ( randmask % ( count-2 ) );
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) )
+      CC_HSORT_SWAP( pivotindex, 0 );
+    if( HSORT_CMP( HSORT_CONTEXT_PASS &table[pivotindex], &table[count-1] ) )
+    {
+      CC_HSORT_SWAP( count-1, pivotindex );
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &table[0], &table[pivotindex] ) )
+        CC_HSORT_SWAP( pivotindex, 0 );
+    }
+
+    /* Quick sort on both sides of the pivot */
+    pivot = table[pivotindex];
+    highindex = count - 2;
+    pivotoffset = highindex;
+    CC_HSORT_SWAP( pivotoffset, pivotindex );
+    pivotindex = 1;
+#ifdef HSORT_PIVOT_STORE
+    HSORT_PIVOT_INIT( HSORT_CONTEXT_PASS &pivotstore, &pivot );
+#endif
+    for( i = highindex ; --i ; )
+    {
+      /* Optional optimization for constant pivot */
+#ifdef HSORT_PIVOT_STORE
+      if( HSORT_PIVOT_CMP( HSORT_CONTEXT_PASS &pivotstore, &pivot, &table[pivotindex] ) )
+#else
+      if( HSORT_CMP( HSORT_CONTEXT_PASS &pivot, &table[pivotindex] ) )
+#endif
+        pivotindex++;
+      else
+      {
+        highindex--;
+        CC_HSORT_SWAP( highindex, pivotindex );
+      }
+    }
+    CC_HSORT_SWAP( pivotindex, pivotoffset );
+
+    /* Count of entries on both sides of the pivot */
+    leftcount = pivotindex;
+    pivotindex++;
+    rightcount = count - pivotindex;
+
+    /* Fast sort small chunks, iterate */
+    if( leftcount < rightcount )
+    {
+      depth++;
+      sp->table = &table[pivotindex];
+      sp->count = (int)rightcount;
+      sp->depth = depth;
+      sp++;
+      count = (int)leftcount;
+    }
+    else
+    {
+      depth++;
+      sp->table = table;
+      sp->count = (int)leftcount;
+      sp->depth = depth;
+      sp++;
+      table += pivotindex;
+      count = (int)rightcount;
+    }
+  }
+
+  if( !( tmpcountalloc ) )
+    free( tmp );
+
+  return;
+}
+
+
+#ifdef CC_HSORT_COPY
+ #undef HSORT_COPY
+ #undef CC_HSORT_COPY
+#endif
+
+#undef HSORT_CONTEXT_PARAM
+#undef HSORT_CONTEXT_PASS
+#undef HSORT_CONTEXT_PASSLAST
+
diff --git a/ecere/src/gfx/newFonts/cc/ccmergesort.h b/ecere/src/gfx/newFonts/cc/ccmergesort.h
new file mode 100644 (file)
index 0000000..4c95358
--- /dev/null
@@ -0,0 +1,215 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/*
+
+Templates C style!
+
+#include this whole file with the following definitions set:
+
+#define MSORT_MAIN MyInlinedSortFunction
+#define MSORT_CMP MyComparisonFunction
+#define MSORT_TYPE int
+
+*/
+
+
+#ifndef CC_MSORT_INLINE
+
+typedef struct
+{
+  void *src;
+  void *dst;
+  int count;
+  int mergeflag;
+  int depthbit;
+} ccMergeSortStack;
+
+ #define CC_MSORT_INLINE
+ #define CC_MSORT_STACK_DEPTH (512)
+
+#endif
+
+
+#ifndef MSORT_COPY
+ #define MSORT_COPY(d,s) (*(d)=*(s))
+ #define CC_MSORT_COPY
+#endif
+
+
+#ifdef MSORT_CONTEXT
+ #define MSORT_CONTEXT_PARAM , MSORT_CONTEXT context
+ #define MSORT_CONTEXT_PASS context,
+ #define MSORT_CONTEXT_PASSLAST , context
+#else
+ #define MSORT_CONTEXT_PARAM
+ #define MSORT_CONTEXT_PASS
+ #define MSORT_CONTEXT_PASSLAST
+#endif
+
+
+static MSORT_TYPE *MSORT_MAIN( MSORT_TYPE *src, MSORT_TYPE *tmp, int count MSORT_CONTEXT_PARAM )
+{
+  int swapflag, depthbit, maxdepthbit;
+  ssize_t leftcount, rightcount;
+  MSORT_TYPE *dst;
+  MSORT_TYPE *sl;
+  MSORT_TYPE *sr;
+  MSORT_TYPE *dstend;
+  MSORT_TYPE *dstbase;
+  MSORT_TYPE *swap;
+  MSORT_TYPE temp;
+  ccMergeSortStack stack[CC_MSORT_STACK_DEPTH];
+  ccMergeSortStack *sp;
+
+  if( count <= 1 )
+    return src;
+
+  dst = tmp;
+  sp = stack;
+  swapflag = 0;
+  depthbit = 0;
+
+  leftcount = count;
+  for( maxdepthbit = 1 ; ; maxdepthbit ^= 1 )
+  {
+    leftcount = leftcount - ( leftcount >> 1 );
+    if( leftcount <= 4 )
+      break;
+  }
+
+  for( ; ; )
+  {
+    if( count <= 4 )
+    {
+      if( !( depthbit ^ maxdepthbit ) )
+      {
+        if( ( count == 4 ) && MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) )
+        {
+          MSORT_COPY( &temp, &src[2] );
+          MSORT_COPY( &src[2], &src[3] );
+          MSORT_COPY( &src[3], &temp );
+        }
+        if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) )
+        {
+          MSORT_COPY( &temp, &src[0] );
+          MSORT_COPY( &src[0], &src[1] );
+          MSORT_COPY( &src[1], &temp );
+        }
+        swapflag = 0;
+      }
+      else
+      {
+        if( count == 4 )
+        {
+          if( MSORT_CMP( MSORT_CONTEXT_PASS &src[2], &src[3] ) )
+          {
+            MSORT_COPY( &dst[2], &src[3] );
+            MSORT_COPY( &dst[3], &src[2] );
+          }
+          else
+          {
+            MSORT_COPY( &dst[2], &src[2] );
+            MSORT_COPY( &dst[3], &src[3] );
+          }
+        }
+        else if( count == 3 )
+          MSORT_COPY( &dst[2], &src[2] );
+        if( MSORT_CMP( MSORT_CONTEXT_PASS &src[0], &src[1] ) )
+        {
+          MSORT_COPY( &dst[0], &src[1] );
+          MSORT_COPY( &dst[1], &src[0] );
+        }
+        else
+        {
+          MSORT_COPY( &dst[0], &src[0] );
+          MSORT_COPY( &dst[1], &src[1] );
+        }
+        swap = src;
+        src = dst;
+        dst = swap;
+        swapflag = 1;
+      }
+    }
+    else
+    {
+      rightcount = count >> 1;
+      leftcount = count - rightcount;
+      sp->src = src;
+      sp->dst = dst;
+      sp->count = count;
+      sp->mergeflag = 1;
+      sp->depthbit = depthbit;
+      depthbit ^= 1;
+      sp++;
+      sp->src = src + leftcount;
+      sp->dst = dst + leftcount;
+      sp->count = (int)rightcount;
+      sp->mergeflag = 0;
+      sp->depthbit = depthbit;
+      sp++;
+      count = (int)leftcount;
+      continue;
+    }
+
+    for( ; ; )
+    {
+      rightcount = count >> 1;
+      leftcount = count - rightcount;
+      sl = src;
+      sr = src + leftcount;
+      dstbase = dst;
+      for( ; ; )
+      {
+        if( MSORT_CMP( MSORT_CONTEXT_PASS sl, sr ) )
+        {
+          MSORT_COPY( dst++, sr++ );
+          if( --rightcount )
+            continue;
+          for( dstend = &dst[leftcount] ; dst < dstend ; )
+            MSORT_COPY( dst++, sl++ );
+          break;
+        }
+        else
+        {
+          MSORT_COPY( dst++, sl++ );
+          if( --leftcount )
+            continue;
+          for( dstend = &dst[rightcount] ; dst < dstend ; )
+            MSORT_COPY( dst++, sr++ );
+          break;
+        }
+      }
+      if( sp == stack )
+        return dstbase;
+      sp--;
+      src = sp->src;
+      dst = sp->dst;
+      count = sp->count;
+      depthbit = sp->depthbit;
+      if( !( sp->mergeflag ) )
+        break;
+      swapflag ^= 1;
+      if( swapflag )
+      {
+        src = sp->dst;
+        dst = sp->src;
+      }
+    }
+  }
+
+  return 0;
+}
+
+
+#ifdef CC_MSORT_COPY
+ #undef MSORT_COPY
+ #undef CC_MSORT_COPY
+#endif
+
+#undef MSORT_CONTEXT_PARAM
+#undef MSORT_CONTEXT_PASS
+#undef MSORT_CONTEXT_PASSLAST
+
diff --git a/ecere/src/gfx/newFonts/cc/cpuconfig.h b/ecere/src/gfx/newFonts/cc/cpuconfig.h
new file mode 100644 (file)
index 0000000..fd817be
--- /dev/null
@@ -0,0 +1,104 @@
+#ifndef __CPUCONFIG_H__
+#define __CPUCONFIG_H__
+
+/* Automatically generated CPU information header */
+
+#define CPUCONF_CHAR_SIZE (1)
+#define CPUCONF_SHORT_SIZE (2)
+#define CPUCONF_INT_SIZE (4)
+#define CPUCONF_LONG_SIZE (4)
+#define CPUCONF_LONG_LONG_SIZE (8)
+#define CPUCONF_INTPTR_SIZE (8)
+#define CPUCONF_POINTER_SIZE (8)
+#define CPUCONF_FLOAT_SIZE (4)
+#define CPUCONF_DOUBLE_SIZE (8)
+#define CPUCONF_LONG_DOUBLE_SIZE (16)
+
+#define CPUCONF_CHAR_BITS (8)
+#define CPUCONF_SHORT_BITS (16)
+#define CPUCONF_INT_BITS (32)
+#define CPUCONF_LONG_BITS (32)
+#define CPUCONF_LONG_LONG_BITS (64)
+#define CPUCONF_INTPTR_BITS (64)
+#define CPUCONF_POINTER_BITS (64)
+#define CPUCONF_FLOAT_BITS (32)
+#define CPUCONF_DOUBLE_BITS (64)
+#define CPUCONF_LONG_DOUBLE_BITS (128)
+
+#define CPUCONF_CHAR_SIZESHIFT (0)
+#define CPUCONF_SHORT_SIZESHIFT (1)
+#define CPUCONF_INT_SIZESHIFT (2)
+#define CPUCONF_LONG_SIZESHIFT (2)
+#define CPUCONF_LONG_LONG_SIZESHIFT (3)
+#define CPUCONF_INTPTR_SIZESHIFT (3)
+#define CPUCONF_POINTER_SIZESHIFT (3)
+#define CPUCONF_FLOAT_SIZESHIFT (2)
+#define CPUCONF_DOUBLE_SIZESHIFT (3)
+#define CPUCONF_LONG_DOUBLE_SIZESHIFT (4)
+
+#define CPUCONF_CHAR_BITSHIFT (3)
+#define CPUCONF_SHORT_BITSHIFT (4)
+#define CPUCONF_INT_BITSHIFT (5)
+#define CPUCONF_LONG_BITSHIFT (5)
+#define CPUCONF_LONG_LONG_BITSHIFT (6)
+#define CPUCONF_INTPTR_BITSHIFT (6)
+#define CPUCONF_POINTER_BITSHIFT (6)
+#define CPUCONF_FLOAT_BITSHIFT (5)
+#define CPUCONF_DOUBLE_BITSHIFT (6)
+#define CPUCONF_LONG_DOUBLE_BITSHIFT (7)
+
+#define CPUCONF_LITTLE_ENDIAN
+#define CPUCONF_ARCH_AMD64
+#define CPUCONF_VENDOR_INTEL
+#define CPUCONF_IDENTIFIER "Intel(R) Core(TM) i7-2600K CPU @ 3.40GHz"
+//#define CPUCONF_CLASS_COREI7-AVX2
+#define CPUCONF_SOCKET_LOGICAL_CORES (16)
+#define CPUCONF_SOCKET_PHYSICAL_CORES (8)
+#define CPUCONF_TOTAL_CORE_COUNT (8)
+#define CPUCONF_SYSTEM_MEMORY (17072009216LL)
+#define CPUCONF_WORD_SIZE (64)
+#define CPUCONF_CACHE_LINE_SIZE (64)
+#define CPUCONF_CACHE_L1CODE_SIZE (32768)
+#define CPUCONF_CACHE_L1CODE_LINE (64)
+#define CPUCONF_CACHE_L1CODE_ASSOCIATIVITY (8)
+#define CPUCONF_CACHE_L1CODE_SHARED (2)
+#define CPUCONF_CACHE_L1DATA_SIZE (32768)
+#define CPUCONF_CACHE_L1DATA_LINE (64)
+#define CPUCONF_CACHE_L1DATA_ASSOCIATIVITY (8)
+#define CPUCONF_CACHE_L1DATA_SHARED (2)
+#define CPUCONF_CACHE_L1_UNIFIED_FLAG (0)
+#define CPUCONF_CACHE_L2_SIZE (262144)
+#define CPUCONF_CACHE_L2_LINE (64)
+#define CPUCONF_CACHE_L2_ASSOCIATIVITY (8)
+#define CPUCONF_CACHE_L2_SHARED (2)
+#define CPUCONF_CACHE_L3_SIZE (8388608)
+#define CPUCONF_CACHE_L3_LINE (64)
+#define CPUCONF_CACHE_L3_ASSOCIATIVITY (16)
+#define CPUCONF_CACHE_L3_SHARED (16)
+#define CPUCONF_CAP_GPREGS (16)
+#define CPUCONF_CAP_FPREGS (16)
+
+#define CPUCONF_CAP_CMOV
+#define CPUCONF_CAP_CLFLUSH
+#define CPUCONF_CAP_TSC
+#define CPUCONF_CAP_MMX
+#define CPUCONF_CAP_SSE
+#define CPUCONF_CAP_SSE2
+#define CPUCONF_CAP_SSE3
+#define CPUCONF_CAP_SSSE3
+#define CPUCONF_CAP_SSE4_1
+#define CPUCONF_CAP_SSE4_2
+#define CPUCONF_CAP_AVX
+#define CPUCONF_CAP_AVX2
+#define CPUCONF_CAP_AES
+#define CPUCONF_CAP_PCLMUL
+#define CPUCONF_CAP_CMPXCHG16B
+#define CPUCONF_CAP_MOVBE
+#define CPUCONF_CAP_RDTSCP
+#define CPUCONF_CAP_CONSTANTTSC
+#define CPUCONF_CAP_HYPERTHREADING
+#define CPUCONF_CAP_MWAIT
+#define CPUCONF_CAP_THERMALSENSOR
+#define CPUCONF_CAP_CLOCKMODULATION
+
+#endif
diff --git a/ecere/src/gfx/newFonts/cc/mm.c b/ecere/src/gfx/newFonts/cc/mm.c
new file mode 100644 (file)
index 0000000..c44012f
--- /dev/null
@@ -0,0 +1,3430 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/**
+ * @file
+ *
+ * Global memory management routines.
+ *
+ * This file includes all the generic memory management routines used
+ * throughout the code : linked lists, balanced trees, block memory allocation,
+ * growing memory allocation, page-based pointer directories, aligned memory
+ * allocation, memory volume management, memory leak or buffer overrun
+ * tracking, etc.
+ */
+
+
+#define MM_THREADING (1)
+
+
+#ifdef MM_THREADING
+ #define _GNU_SOURCE
+ #include <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
diff --git a/ecere/src/gfx/newFonts/cc/mm.h b/ecere/src/gfx/newFonts/cc/mm.h
new file mode 100644 (file)
index 0000000..077e9c2
--- /dev/null
@@ -0,0 +1,786 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/**
+ * @file
+ *
+ * Global memory management header.
+ */
+
+#ifndef MM_H
+#define MM_H
+
+#include <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
diff --git a/ecere/src/gfx/newFonts/cc/mmatomic.h b/ecere/src/gfx/newFonts/cc/mmatomic.h
new file mode 100644 (file)
index 0000000..6b5729a
--- /dev/null
@@ -0,0 +1,2475 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/**
+ * @file
+ *
+ * Atomic memory operations.
+ */
+
+
+
+
+#if ( defined(CPUCONF_ARCH_IA32) || defined(CPUCONF_ARCH_AMD64) ) && defined(__GNUC__) && !defined(MM_ATOMIC_SUPPORT)
+
+
+#define MM_ATOMIC_SUPPORT
+
+
+#ifdef CPUCONF_ARCH_AMD64
+ #define MM_ATOMIC_64_BITS_SUPPORT
+#endif
+
+
+typedef struct { volatile int8_t value; } mmAtomic8;
+typedef struct { volatile int16_t value; } mmAtomic16;
+typedef struct { volatile int32_t value; } mmAtomic32;
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+typedef struct { volatile int64_t value; } mmAtomic64;
+#endif
+
+
+////
+
+
+/*
+
+Architecture Memory Ordering
+
+Memory model for x86 and amd64
+--- Aligned stores can not be partially seen by loads
+--- Loads can NOT be reordered after loads
+--- Loads can NOT be reordered after stores
+--- Stores can NOT be reordered after stores
+-X- Stores CAN be reordered after loads
+--- Atomic instructions are NOT reordered with loads
+--- Atomic instructions are NOT reordered with stores
+--- Dependent loads can NOT be reordered
+
+*/
+
+
+/* Memory model configuration for x86/amd64 */
+// #define CPUCONF_LOAD_REODERING_AFTER_LOAD
+// #define CPUCONF_LOAD_REODERING_AFTER_STORE
+#define CPUCONF_STORE_REODERING_AFTER_LOAD
+// #define CPUCONF_STORE_REODERING_AFTER_STORE
+// #define CPUCONF_ATOMIC_REODERING_WITH_LOAD
+// #define CPUCONF_ATOMIC_REODERING_WITH_STORE
+// #define CPUCONF_DEPENDENT_REODERING
+
+
+////
+
+
+/* Do nothing, prevent compiler reordering */
+static inline void mmBarrier()
+{
+  __asm__ __volatile__( "":::"memory" );
+  return;
+}
+
+/* All previous loads must complete before future loads */
+static inline void mmReadBarrier()
+{
+#ifdef CPUCONF_CAP_SSE2
+  __asm__ __volatile__( "lfence":::"memory" );
+#else
+  __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" );
+#endif
+  return;
+}
+
+/* All previous stores must complete before future stores */
+static inline void mmWriteBarrier()
+{
+  /* x86 and AMD64 never reorder stores : the sfence instruction is useless unless chatting with devices on MMIO */
+  __asm__ __volatile__( "":::"memory" );
+  return;
+}
+
+/* All previous loads/stores must complete before future loads/stores */
+static inline void mmFullBarrier()
+{
+#ifdef CPUCONF_CAP_SSE2
+  __asm__ __volatile__( "mfence":::"memory" );
+#else
+  __asm__ __volatile__( "lock ; addl $0,(%%esp)":::"memory" );
+#endif
+  return;
+}
+
+
+////
+
+
+/* Direct access to the atomic variables, for use when the caller knows no atomicity is needed */
+#define MM_ATOMIC_ACCESS_8(v) ((v)->value)
+#define MM_ATOMIC_ACCESS_16(v) ((v)->value)
+#define MM_ATOMIC_ACCESS_32(v) ((v)->value)
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+ #define MM_ATOMIC_ACCESS_64(v) ((v)->value)
+#endif
+
+
+////
+
+
+/*
+mmAtomicRead*()
+Atomically read the value
+*/
+static inline int8_t mmAtomicRead8( mmAtomic8 *v )
+{
+  mmBarrier();
+  return v->value;
+}
+
+static inline int16_t mmAtomicRead16( mmAtomic16 *v )
+{
+  mmBarrier();
+  return v->value;
+}
+
+static inline int32_t mmAtomicRead32( mmAtomic32 *v )
+{
+  mmBarrier();
+  return v->value;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicRead64( mmAtomic64 *v )
+{
+  mmBarrier();
+  return v->value;
+}
+#endif
+
+
+////
+
+
+/*
+mmAtomicWrite*()
+Atomically write the value
+*/
+static inline void mmAtomicWrite8( mmAtomic8 *v, int8_t i )
+{
+  mmBarrier();
+  v->value = i;
+  return;
+}
+
+static inline void mmAtomicWrite16( mmAtomic16 *v, int16_t i )
+{
+  mmBarrier();
+  v->value = i;
+  return;
+}
+
+static inline void mmAtomicWrite32( mmAtomic32 *v, int32_t i )
+{
+  mmBarrier();
+  v->value = i;
+  return;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicWrite64( mmAtomic64 *v, int64_t i )
+{
+  mmBarrier();
+  v->value = i;
+  return;
+}
+#endif
+
+
+////
+
+
+/*
+mmAtomicBarrierWrite*()
+Atomically write the value and act as a full memory barrier
+*/
+static inline void mmAtomicBarrierWrite8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgb %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return;
+}
+
+static inline void mmAtomicBarrierWrite16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgw %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return;
+}
+
+static inline void mmAtomicBarrierWrite32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgl %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicBarrierWrite64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgq %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return;
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicAdd8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; addb %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicAdd16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; addw %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicAdd32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; addl %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicAdd64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; addq %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicSub8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; subb %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicSub16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; subw %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicSub32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; subl %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicSub64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; subq %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicAddTestZero8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addb %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAddTestZero16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addw %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAddTestZero32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addl %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicAddTestZero64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addq %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicSubTestZero8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subb %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicSubTestZero16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subw %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicSubTestZero32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subl %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicSubTestZero64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subq %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicInc8( mmAtomic8 *v )
+{
+  __asm__ __volatile__(
+    "lock ; incb %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicInc16( mmAtomic16 *v )
+{
+  __asm__ __volatile__(
+    "lock ; incw %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicInc32( mmAtomic32 *v )
+{
+  __asm__ __volatile__(
+    "lock ; incl %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicInc64( mmAtomic64 *v )
+{
+  __asm__ __volatile__(
+    "lock ; incq %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicDec8( mmAtomic8 *v )
+{
+  __asm__ __volatile__(
+    "lock ; decl %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicDec16( mmAtomic16 *v )
+{
+  __asm__ __volatile__(
+    "lock ; decl %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicDec32( mmAtomic32 *v )
+{
+  __asm__ __volatile__(
+    "lock ; decl %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicDec64( mmAtomic64 *v )
+{
+  __asm__ __volatile__(
+    "lock ; decq %0"
+    :"=m"(v->value)
+    :"m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicIncTestZero8( mmAtomic8 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; incb %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+static inline int mmAtomicIncTestZero16( mmAtomic16 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; incw %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+static inline int mmAtomicIncTestZero32( mmAtomic32 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; incl %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicIncTestZero64( mmAtomic64 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; incq %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicDecTestZero8( mmAtomic8 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; decb %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+static inline int mmAtomicDecTestZero16( mmAtomic16 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; decw %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+static inline int mmAtomicDecTestZero32( mmAtomic32 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; decl %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicDecTestZero64( mmAtomic64 *v )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; decq %0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"m"(v->value) :"memory" );
+  return c != 0;
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicAddTestNegative8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addb %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAddTestNegative16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addw %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAddTestNegative32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addl %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicAddTestNegative64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; addq %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicSubTestNegative8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subb %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicSubTestNegative16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subw %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicSubTestNegative32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subl %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicSubTestNegative64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; subq %2,%0 ; sets %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+
+////////////////
+
+
+
+static inline void mmAtomicAnd8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; andb %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicAnd16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; andw %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicAnd32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; andl %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicAnd64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; andq %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicAndTestZero8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; andb %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAndTestZero16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; andw %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicAndTestZero32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; andl %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicAndTestZero64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; andq %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicOr8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; orb %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicOr16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; orw %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicOr32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; orl %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicOr64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; orq %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicOrTestZero8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; orb %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicOrTestZero16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; orw %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicOrTestZero32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; orl %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicOrTestZero64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; orq %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicXor8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xorb %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicXor16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xorw %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+static inline void mmAtomicXor32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xorl %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicXor64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xorq %1,%0"
+    :"=m"(v->value)
+    :"ir"(i), "m"(v->value) :"memory" );
+}
+#endif
+
+
+////
+
+
+static inline int mmAtomicXorTestZero8( mmAtomic8 *v, int8_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; xorb %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicXorTestZero16( mmAtomic16 *v, int16_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; xorw %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+static inline int mmAtomicXorTestZero32( mmAtomic32 *v, int32_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; xorl %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicXorTestZero64( mmAtomic64 *v, int64_t i )
+{
+  unsigned char c;
+  __asm__ __volatile__(
+    "lock ; xorq %2,%0 ; setz %1"
+    :"=m"(v->value), "=qm"(c)
+    :"ir"(i), "m"(v->value) :"memory" );
+  return c;
+}
+#endif
+
+
+
+////////////////
+
+
+
+static inline int8_t mmAtomicXchg8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgb %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return i;
+}
+
+static inline int16_t mmAtomicXchg16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgw %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return i;
+}
+
+static inline int32_t mmAtomicXchg32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgl %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicXchg64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "lock ; xchgq %0,%1"
+    :"=q"(i)
+    :"m"(v->value), "0"(i) :"memory" );
+  return i;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicCmpXchg8( mmAtomic8 *v, int8_t old, int8_t n )
+{
+  int8_t prev;
+  __asm__ __volatile__(
+    "lock ; cmpxchgb %1,%2"
+    :"=a"(prev)
+    :"r"(n), "m"(v->value), "a"(old) :"memory" );
+  return prev;
+}
+
+static inline int16_t mmAtomicCmpXchg16( mmAtomic16 *v, int16_t old, int16_t n )
+{
+  int16_t prev;
+  __asm__ __volatile__(
+    "lock ; cmpxchgw %1,%2"
+    :"=a"(prev)
+    :"r"(n), "m"(v->value), "a"(old) :"memory" );
+  return prev;
+}
+
+static inline int32_t mmAtomicCmpXchg32( mmAtomic32 *v, int32_t old, int32_t n )
+{
+  int32_t prev;
+  __asm__ __volatile__(
+    "lock ; cmpxchgl %1,%2"
+    :"=a"(prev)
+    :"r"(n), "m"(v->value), "a"(old) :"memory" );
+  return prev;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicCmpXchg64( mmAtomic64 *v, int64_t old, int64_t n )
+{
+  int64_t prev;
+  __asm__ __volatile__(
+    "lock ; cmpxchgq %1,%2"
+    :"=a"(prev)
+    :"r"(n), "m"(v->value), "a"(old) :"memory" );
+  return prev;
+}
+#endif
+
+
+
+////////////////
+
+
+
+static inline void mmAtomicPause()
+{
+  __asm__ __volatile__(
+    "rep ; nop"
+    :
+    ::"memory" );
+  return;
+}
+
+
+static inline void mmAtomicSpinWaitEq8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpb %1,%0\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+static inline void mmAtomicSpinWaitEq16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpw %1,%0\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+static inline void mmAtomicSpinWaitEq32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpl %1,%0\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicSpinWaitEq64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpq %1,%0\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+#endif
+
+
+static inline int32_t mmAtomicSpinWaitEq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpb %2,%1\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+static inline int32_t mmAtomicSpinWaitEq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpw %2,%1\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+static inline int32_t mmAtomicSpinWaitEq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpl %2,%1\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int32_t mmAtomicSpinWaitEq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpq %2,%1\n"
+    "jnz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+#endif
+
+
+static inline void mmAtomicSpinWaitNeq8( mmAtomic8 *v, int8_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpb %1,%0\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+static inline void mmAtomicSpinWaitNeq16( mmAtomic16 *v, int16_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpw %1,%0\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+static inline void mmAtomicSpinWaitNeq32( mmAtomic32 *v, int32_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpl %1,%0\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicSpinWaitNeq64( mmAtomic64 *v, int64_t i )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpq %1,%0\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :
+    :"m"(v->value), "r"(i) :"memory" );
+  return;
+}
+#endif
+
+
+static inline int32_t mmAtomicSpinWaitNeq8Count( mmAtomic8 *v, int8_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpb %2,%1\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+static inline int32_t mmAtomicSpinWaitNeq16Count( mmAtomic16 *v, int16_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpw %2,%1\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+static inline int32_t mmAtomicSpinWaitNeq32Count( mmAtomic32 *v, int32_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpl %2,%1\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int32_t mmAtomicSpinWaitNeq64Count( mmAtomic64 *v, int64_t i, int32_t spinmaxcount )
+{
+  __asm__ __volatile__(
+    "jmp 1f\n"
+    ".p2align 6\n"
+    "2:\n"
+    "subl $1,%0\n"
+    "jz 3f\n"
+    "rep ; nop\n"
+    "1:\n"
+    "cmpq %2,%1\n"
+    "jz 2b\n"
+    ".p2align 4\n"
+    "3:\n"
+    :"=q"(spinmaxcount)
+    :"m"(v->value), "r"(i), "0"(spinmaxcount) :"memory" );
+  return spinmaxcount;
+}
+#endif
+
+
+////
+
+
+static inline void mmAtomicSpin8( mmAtomic8 *v, int8_t old, int8_t n )
+{
+  for( ; mmAtomicCmpXchg8( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead8( v ) != old ; )
+      mmAtomicPause();
+  }
+  return;
+}
+
+static inline void mmAtomicSpin16( mmAtomic16 *v, int16_t old, int16_t n )
+{
+  for( ; mmAtomicCmpXchg16( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead16( v ) != old ; )
+      mmAtomicPause();
+  }
+  return;
+}
+
+static inline void mmAtomicSpin32( mmAtomic32 *v, int32_t old, int32_t n )
+{
+  for( ; mmAtomicCmpXchg32( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead32( v ) != old ; )
+      mmAtomicPause();
+  }
+  return;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline void mmAtomicSpin64( mmAtomic64 *v, int64_t old, int64_t n )
+{
+  for( ; mmAtomicCmpXchg64( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead64( v ) != old ; )
+      mmAtomicPause();
+  }
+  return;
+}
+#endif
+
+
+static inline int mmAtomicTrySpin8( mmAtomic8 *v, int8_t old, int8_t n, int spincount )
+{
+  for( ; mmAtomicCmpXchg8( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead8( v ) != old ; )
+    {
+      if( !( --spincount ) )
+        return 0;
+      mmAtomicPause();
+    }
+  }
+  return 1;
+}
+
+static inline int mmAtomicTrySpin16( mmAtomic16 *v, int16_t old, int16_t n, int spincount )
+{
+  for( ; mmAtomicCmpXchg16( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead16( v ) != old ; )
+    {
+      if( !( --spincount ) )
+        return 0;
+      mmAtomicPause();
+    }
+  }
+  return 1;
+}
+
+static inline int mmAtomicTrySpin32( mmAtomic32 *v, int32_t old, int32_t n, int spincount )
+{
+  for( ; mmAtomicCmpXchg32( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead32( v ) != old ; )
+    {
+      if( !( --spincount ) )
+        return 0;
+      mmAtomicPause();
+    }
+  }
+  return 1;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int mmAtomicTrySpin64( mmAtomic64 *v, int64_t old, int64_t n, int spincount )
+{
+  for( ; mmAtomicCmpXchg64( v, old, n ) != old ; )
+  {
+    for( ; mmAtomicRead64( v ) != old ; )
+    {
+      if( !( --spincount ) )
+        return 0;
+      mmAtomicPause();
+    }
+  }
+  return 1;
+}
+#endif
+
+
+////////////////
+
+
+static inline int8_t mmAtomicAddRead8( mmAtomic8 *v, int8_t add )
+{
+  int8_t i;
+  do
+  {
+    i = mmAtomicRead8( v );
+  } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i );
+  return (int8_t)(i + add);
+}
+
+static inline int16_t mmAtomicAddRead16( mmAtomic16 *v, int16_t add )
+{
+  int16_t i;
+  do
+  {
+    i = mmAtomicRead16( v );
+  } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i );
+  return (int16_t)(i + add);
+}
+
+static inline int32_t mmAtomicAddRead32( mmAtomic32 *v, int32_t add )
+{
+  int32_t i;
+  do
+  {
+    i = mmAtomicRead32( v );
+  } while( mmAtomicCmpXchg32( v, i, i + add ) != i );
+  return i + add;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicAddRead64( mmAtomic64 *v, int64_t add )
+{
+  int64_t i;
+  do
+  {
+    i = mmAtomicRead64( v );
+  } while( mmAtomicCmpXchg64( v, i, i + add ) != i );
+  return i + add;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicReadAdd8( mmAtomic8 *v, int8_t add )
+{
+  int8_t i;
+  do
+  {
+    i = mmAtomicRead8( v );
+  } while( mmAtomicCmpXchg8( v, i, (int8_t)(i + add) ) != i );
+  return i;
+}
+
+static inline int16_t mmAtomicReadAdd16( mmAtomic16 *v, int16_t add )
+{
+  int16_t i;
+  do
+  {
+    i = mmAtomicRead16( v );
+  } while( mmAtomicCmpXchg16( v, i, (int16_t)(i + add) ) != i );
+  return i;
+}
+
+static inline int32_t mmAtomicReadAdd32( mmAtomic32 *v, int32_t add )
+{
+  int32_t i;
+  do
+  {
+    i = mmAtomicRead32( v );
+  } while( mmAtomicCmpXchg32( v, i, i + add ) != i );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicReadAdd64( mmAtomic64 *v, int64_t add )
+{
+  int64_t i;
+  do
+  {
+    i = mmAtomicRead64( v );
+  } while( mmAtomicCmpXchg64( v, i, i + add ) != i );
+  return i;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicReadAnd8( mmAtomic8 *v, int8_t mask )
+{
+  int8_t i, j;
+  do
+  {
+    i = mmAtomicRead8( v );
+    j = i & mask;
+  } while( mmAtomicCmpXchg8( v, i, j ) != i );
+  return i;
+}
+
+static inline int16_t mmAtomicReadAnd16( mmAtomic16 *v, int16_t mask )
+{
+  int16_t i, j;
+  do
+  {
+    i = mmAtomicRead16( v );
+    j = i & mask;
+  } while( mmAtomicCmpXchg16( v, i, j ) != i );
+  return i;
+}
+
+static inline int32_t mmAtomicReadAnd32( mmAtomic32 *v, int32_t mask )
+{
+  int32_t i, j;
+  do
+  {
+    i = mmAtomicRead32( v );
+    j = i & mask;
+  } while( mmAtomicCmpXchg32( v, i, j ) != i );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicReadAnd64( mmAtomic64 *v, int64_t mask )
+{
+  int64_t i, j;
+  do
+  {
+    i = mmAtomicRead64( v );
+    j = i & mask;
+  } while( mmAtomicCmpXchg64( v, i, j ) != i );
+  return i;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicReadOr8( mmAtomic8 *v, int8_t mask )
+{
+  int8_t i, j;
+  do
+  {
+    i = mmAtomicRead8( v );
+    j = i | mask;
+  } while( mmAtomicCmpXchg8( v, i, j ) != i );
+  return i;
+}
+
+static inline int16_t mmAtomicReadOr16( mmAtomic16 *v, int16_t mask )
+{
+  int16_t i, j;
+  do
+  {
+    i = mmAtomicRead16( v );
+    j = i | mask;
+  } while( mmAtomicCmpXchg16( v, i, j ) != i );
+  return i;
+}
+
+static inline int32_t mmAtomicReadOr32( mmAtomic32 *v, int32_t mask )
+{
+  int32_t i, j;
+  do
+  {
+    i = mmAtomicRead32( v );
+    j = i | mask;
+  } while( mmAtomicCmpXchg32( v, i, j ) != i );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicReadOr64( mmAtomic64 *v, int64_t mask )
+{
+  int64_t i, j;
+  do
+  {
+    i = mmAtomicRead64( v );
+    j = i | mask;
+  } while( mmAtomicCmpXchg64( v, i, j ) != i );
+  return i;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicReadIncLoop8( mmAtomic8 *v, int8_t max )
+{
+  int8_t i, j;
+  do
+  {
+    i = mmAtomicRead8( v );
+    j = (int8_t)(i + 1);
+    if( j >= max )
+      j = 0;
+  } while( mmAtomicCmpXchg8( v, i, j ) != i );
+  return i;
+}
+
+static inline int16_t mmAtomicReadIncLoop16( mmAtomic16 *v, int16_t max )
+{
+  int16_t i, j;
+  do
+  {
+    i = mmAtomicRead16( v );
+    j = (int8_t)(i + 1);
+    if( j >= max )
+      j = 0;
+  } while( mmAtomicCmpXchg16( v, i, j ) != i );
+  return i;
+}
+
+static inline int32_t mmAtomicReadIncLoop32( mmAtomic32 *v, int32_t max )
+{
+  int32_t i, j;
+  do
+  {
+    i = mmAtomicRead32( v );
+    j = i + 1;
+    if( j >= max )
+      j = 0;
+  } while( mmAtomicCmpXchg32( v, i, j ) != i );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicReadIncLoop64( mmAtomic64 *v, int64_t max )
+{
+  int64_t i, j;
+  do
+  {
+    i = mmAtomicRead64( v );
+    j = i + 1;
+    if( j >= max )
+      j = 0;
+  } while( mmAtomicCmpXchg64( v, i, j ) != i );
+  return i;
+}
+#endif
+
+
+////
+
+
+static inline int8_t mmAtomicReadAddLoop8( mmAtomic8 *v, int8_t add, int8_t base, int8_t max )
+{
+  int8_t i, j;
+  do
+  {
+    i = mmAtomicRead8( v );
+    j = (int8_t)(i + add);
+    if( j >= max )
+      j = base;
+  } while( mmAtomicCmpXchg8( v, i, j ) != i );
+  return i;
+}
+
+static inline int16_t mmAtomicReadAddLoop16( mmAtomic16 *v, int16_t add, int16_t base, int16_t max )
+{
+  int16_t i, j;
+  do
+  {
+    i = mmAtomicRead16( v );
+    j = (int16_t)(i + add);
+    if( j >= max )
+      j = base;
+  } while( mmAtomicCmpXchg16( v, i, j ) != i );
+  return i;
+}
+
+static inline int32_t mmAtomicReadAddLoop32( mmAtomic32 *v, int32_t add, int32_t base, int32_t max )
+{
+  int32_t i, j;
+  do
+  {
+    i = mmAtomicRead32( v );
+    j = i + add;
+    if( j >= max )
+      j = base;
+  } while( mmAtomicCmpXchg32( v, i, j ) != i );
+  return i;
+}
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+static inline int64_t mmAtomicReadAddLoop64( mmAtomic64 *v, int64_t add, int64_t base, int64_t max )
+{
+  int64_t i, j;
+  do
+  {
+    i = mmAtomicRead64( v );
+    j = i + add;
+    if( j >= max )
+      j = base;
+  } while( mmAtomicCmpXchg64( v, i, j ) != i );
+  return i;
+}
+#endif
+
+
+
+////////////////
+
+
+
+
+#define mmAtomicCmpReplace8(v,old,new) (mmAtomicCmpXchg8(v,old,new)==(old))
+#define mmAtomicCmpReplace16(v,old,new) (mmAtomicCmpXchg16(v,old,new)==(old))
+#define mmAtomicCmpReplace32(v,old,new) (mmAtomicCmpXchg32(v,old,new)==(old))
+#define mmAtomicCmpReplace64(v,old,new) (mmAtomicCmpXchg64(v,old,new)==(old))
+
+
+#if CPUCONF_POINTER_BITS == 64
+ #define mmAtomicP mmAtomic64
+ #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_64(v)
+ #define mmAtomicReadP(v) (void *)mmAtomicRead64(v)
+ #define mmAtomicWriteP(v,i) mmAtomicWrite64(v,(int64_t)i)
+ #define mmAtomicAddP(v,i) mmAtomicAdd64(v,(int64_t)i)
+ #define mmAtomicSubP(v,i) mmAtomicSub64(v,(int64_t)i)
+ #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero64(v,(int64_t)i)
+ #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero64(v,(int64_t)i)
+ #define mmAtomicIncP(v) mmAtomicInc64(v)
+ #define mmAtomicDecP(v) mmAtomicDec64(v)
+ #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero64(v)
+ #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero64(v)
+ #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative64(v,(int64_t)i)
+ #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative64(v,(int64_t)i)
+ #define mmAtomicAndP(v,i) mmAtomicAnd64(v,(int64_t)i)
+ #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero64(v,(int64_t)i)
+ #define mmAtomicOrP(v,i) mmAtomicOr64(v,(int64_t)i)
+ #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero64(v,(int64_t)i)
+ #define mmAtomicXorP(v,i) mmAtomicXor64(v,(int64_t)i)
+ #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero64(v,(int64_t)i)
+ #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg64(v,(int64_t)i)
+ #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg64(v,(int64_t)i,(int64_t)j)
+ #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace64(v,(int64_t)i,(int64_t)j)
+ #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin64(v,(int64_t)i,(int64_t)j)
+ #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead64(v,(int64_t)i)
+#elif CPUCONF_POINTER_BITS == 32
+ #define mmAtomicP mmAtomic32
+ #define MM_ATOMIC_ACCESS_P(v) (void *)MM_ATOMIC_ACCESS_32(v)
+ #define mmAtomicReadP(v) (void *)mmAtomicRead32(v)
+ #define mmAtomicWriteP(v,i) mmAtomicWrite32(v,(int32_t)i)
+ #define mmAtomicAddP(v,i) mmAtomicAdd32(v,(int32_t)i)
+ #define mmAtomicSubP(v,i) mmAtomicSub32(v,(int32_t)i)
+ #define mmAtomicAddTestZeroP(v,i) mmAtomicAddTestZero32(v,(int32_t)i)
+ #define mmAtomicSubTestZeroP(v,i) mmAtomicSubTestZero32(v,(int32_t)i)
+ #define mmAtomicIncP(v) mmAtomicInc32(v)
+ #define mmAtomicDecP(v) mmAtomicDec32(v)
+ #define mmAtomicIncTestZeroP(v) mmAtomicIncTestZero32(v)
+ #define mmAtomicDecTestZeroP(v) mmAtomicDecTestZero32(v)
+ #define mmAtomicAddTestNegativeP(v,i) mmAtomicAddTestNegative32(v,(int32_t)i)
+ #define mmAtomicSubTestNegativeP(v,i) mmAtomicSubTestNegative32(v,(int32_t)i)
+ #define mmAtomicAndP(v,i) mmAtomicAnd32(v,(int32_t)i)
+ #define mmAtomicAndTestZeroP(v,i) mmAtomicAndTestZero32(v,(int32_t)i)
+ #define mmAtomicOrP(v,i) mmAtomicOr32(v,(int32_t)i)
+ #define mmAtomicOrTestZeroP(v,i) mmAtomicOrTestZero32(v,(int32_t)i)
+ #define mmAtomicXorP(v,i) mmAtomicXor32(v,(int32_t)i)
+ #define mmAtomicXorTestZeroP(v,i) mmAtomicXorTestZero32(v,(int32_t)i)
+ #define mmAtomicXchgP(v,i) (void *)mmAtomicXchg32(v,(int32_t)i)
+ #define mmAtomicCmpXchgP(v,i,j) (void *)mmAtomicCmpXchg32(v,(int32_t)i,(int32_t)j)
+ #define mmAtomicCmpReplaceP(v,i,j) mmAtomicCmpReplace32(v,(int32_t)i,(int32_t)j)
+ #define mmAtomicSpinP(v,i,j) (void *)mmAtomicSpin32(v,(int32_t)i,(int32_t)j)
+ #define mmAtomicAddReadP(v,i) (void *)mmAtomicAddRead32(v,(int32_t)i)
+#else
+ #error CPUCONF_POINTER_BITS undefined
+#endif
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+ #define intlarge int64_t
+ #define uintlarge uint64_t
+ #define mmAtomicL mmAtomic64
+ #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_64(v)
+ #define mmAtomicReadL(v) mmAtomicRead64(v)
+ #define mmAtomicWriteL(v,i) mmAtomicWrite64(v,i)
+ #define mmAtomicAddL(v,i) mmAtomicAdd64(v,i)
+ #define mmAtomicSubL(v,i) mmAtomicSub64(v,i)
+ #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero64(v,i)
+ #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero64(v,i)
+ #define mmAtomicIncL(v) mmAtomicInc64(v)
+ #define mmAtomicDecL(v) mmAtomicDec64(v)
+ #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero64(v)
+ #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero64(v)
+ #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative64(v,i)
+ #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative64(v,i)
+ #define mmAtomicAndL(v,i) mmAtomicAnd64(v,i)
+ #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero64(v,i)
+ #define mmAtomicOrL(v,i) mmAtomicOr64(v,i)
+ #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero64(v,i)
+ #define mmAtomicXorL(v,i) mmAtomicXor64(v,i)
+ #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero64(v,i)
+ #define mmAtomicXchgL(v,i) mmAtomicXchg64(v,i)
+ #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg64(v,i,j)
+ #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace64(v,i,j)
+ #define mmAtomicSpinL(v,i,j) mmAtomicSpin64(v,i,j)
+ #define mmAtomicAddReadL(v,i) mmAtomicAddRead64(v,(int64_t)i)
+#else
+ #define intlarge int32_t
+ #define uintlarge uint32_t
+ #define mmAtomicL mmAtomic32
+ #define MM_ATOMIC_ACCESS_L(v) MM_ATOMIC_ACCESS_32(v)
+ #define mmAtomicReadL(v) mmAtomicRead32(v)
+ #define mmAtomicWriteL(v,i) mmAtomicWrite32(v,i)
+ #define mmAtomicAddL(v,i) mmAtomicAdd32(v,i)
+ #define mmAtomicSubL(v,i) mmAtomicSub32(v,i)
+ #define mmAtomicAddTestZeroL(v,i) mmAtomicAddTestZero32(v,i)
+ #define mmAtomicSubTestZeroL(v,i) mmAtomicSubTestZero32(v,i)
+ #define mmAtomicIncL(v) mmAtomicInc32(v)
+ #define mmAtomicDecL(v) mmAtomicDec32(v)
+ #define mmAtomicIncTestZeroL(v) mmAtomicIncTestZero32(v)
+ #define mmAtomicDecTestZeroL(v) mmAtomicDecTestZero32(v)
+ #define mmAtomicAddTestNegativeL(v,i) mmAtomicAddTestNegative32(v,i)
+ #define mmAtomicSubTestNegativeL(v,i) mmAtomicSubTestNegative32(v,i)
+ #define mmAtomicAndL(v,i) mmAtomicAnd32(v,i)
+ #define mmAtomicAndTestZeroL(v,i) mmAtomicAndTestZero32(v,i)
+ #define mmAtomicOrL(v,i) mmAtomicOr32(v,i)
+ #define mmAtomicOrTestZeroL(v,i) mmAtomicOrTestZero32(v,i)
+ #define mmAtomicXorL(v,i) mmAtomicXor32(v,i)
+ #define mmAtomicXorTestZeroL(v,i) mmAtomicXorTestZero32(v,i)
+ #define mmAtomicXchgL(v,i) mmAtomicXchg32(v,i)
+ #define mmAtomicCmpXchgL(v,i,j) mmAtomicCmpXchg32(v,i,j)
+ #define mmAtomicCmpReplaceL(v,i,j) mmAtomicCmpReplace32(v,i,j)
+ #define mmAtomicSpinL(v,i,j) mmAtomicSpin32(v,i,j)
+ #define mmAtomicAddReadL(v,i) mmAtomicAddRead32(v,(int32_t)i)
+#endif
+
+
+
+////////////////
+
+
+
+typedef struct { mmAtomic8 v; } mmAtomicLock8;
+
+#define MM_ATOMIC_LOCK8_WRITE (-((int8_t)0x7f))
+
+static inline void mmAtomicLockInit8( mmAtomicLock8 *v )
+{
+  mmAtomicWrite8( &v->v, 0 );
+  return;
+}
+
+static inline int mmAtomicLockAttemptRead8( mmAtomicLock8 *v )
+{
+  if( mmAtomicAddTestNegative8( &v->v, 1 ) )
+  {
+    mmAtomicAdd8( &v->v, -1 );
+    return 0;
+  }
+  return 1;
+}
+
+static inline int mmAtomicLockAttemptWrite8( mmAtomicLock8 *v )
+{
+  if( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) )
+    return 0;
+  return 1;
+}
+
+static inline void mmAtomicLockSpinRead8( mmAtomicLock8 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead8( &v->v ) < 0 ; )
+      mmAtomicPause();
+    if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) )
+      break;
+    mmAtomicAdd8( &v->v, -1 );
+  }
+  return;
+}
+
+static inline void mmAtomicLockSpinWrite8( mmAtomicLock8 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead8( &v->v ) ; )
+      mmAtomicPause();
+    if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) )
+      break;
+  }
+  return;
+}
+
+static inline int mmAtomicLockTryRead8( mmAtomicLock8 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead8( &v->v ) < 0 )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicAddTestNegative8( &v->v, 1 ) ) )
+        return 1;
+      mmAtomicAdd8( &v->v, -1 );
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline int mmAtomicLockTryWrite8( mmAtomicLock8 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead8( &v->v ) )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicCmpXchg8( &v->v, 0, MM_ATOMIC_LOCK8_WRITE ) ) )
+        return 1;
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline void mmAtomicLockDoneRead8( mmAtomicLock8 *v )
+{
+  mmAtomicAdd8( &v->v, -1 );
+  return;
+}
+
+static inline void mmAtomicLockDoneWrite8( mmAtomicLock8 *v )
+{
+  mmAtomicAdd8( &v->v, -MM_ATOMIC_LOCK8_WRITE );
+  return;
+}
+
+
+////
+
+
+typedef struct { mmAtomic16 v; } mmAtomicLock16;
+
+#define MM_ATOMIC_LOCK16_WRITE (-((int16_t)0x7fff))
+
+static inline void mmAtomicLockInit16( mmAtomicLock16 *v )
+{
+  mmAtomicWrite16( &v->v, 0 );
+  return;
+}
+
+static inline int mmAtomicLockAttemptRead16( mmAtomicLock16 *v )
+{
+  if( mmAtomicAddTestNegative16( &v->v, 1 ) )
+  {
+    mmAtomicAdd16( &v->v, -1 );
+    return 0;
+  }
+  return 1;
+}
+
+static inline int mmAtomicLockAttemptWrite16( mmAtomicLock16 *v )
+{
+  if( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) )
+    return 0;
+  return 1;
+}
+
+static inline void mmAtomicLockSpinRead16( mmAtomicLock16 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead16( &v->v ) < 0 ; )
+      mmAtomicPause();
+    if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) )
+      break;
+    mmAtomicAdd16( &v->v, -1 );
+  }
+  return;
+}
+
+static inline void mmAtomicLockSpinWrite16( mmAtomicLock16 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead16( &v->v ) ; )
+      mmAtomicPause();
+    if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) )
+      break;
+  }
+  return;
+}
+
+static inline int mmAtomicLockTryRead16( mmAtomicLock16 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead16( &v->v ) < 0 )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicAddTestNegative16( &v->v, 1 ) ) )
+        return 1;
+      mmAtomicAdd16( &v->v, -1 );
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline int mmAtomicLockTryWrite16( mmAtomicLock16 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead16( &v->v ) )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicCmpXchg16( &v->v, 0, MM_ATOMIC_LOCK16_WRITE ) ) )
+        return 1;
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline void mmAtomicLockDoneRead16( mmAtomicLock16 *v )
+{
+  mmAtomicAdd16( &v->v, -1 );
+  return;
+}
+
+static inline void mmAtomicLockDoneWrite16( mmAtomicLock16 *v )
+{
+  mmAtomicAdd16( &v->v, -MM_ATOMIC_LOCK16_WRITE );
+  return;
+}
+
+
+////
+
+
+typedef struct { mmAtomic32 v; } mmAtomicLock32;
+
+/*
+#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x7fffffff))
+*/
+#define MM_ATOMIC_LOCK32_WRITE (-((int32_t)0x10000000))
+
+static inline void mmAtomicLockInit32( mmAtomicLock32 *v )
+{
+  mmAtomicWrite32( &v->v, 0 );
+  return;
+}
+
+static inline int mmAtomicLockAttemptRead32( mmAtomicLock32 *v )
+{
+  if( mmAtomicAddTestNegative32( &v->v, 1 ) )
+  {
+    mmAtomicAdd32( &v->v, -1 );
+    return 0;
+  }
+  return 1;
+}
+
+static inline int mmAtomicLockAttemptWrite32( mmAtomicLock32 *v )
+{
+  if( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) )
+    return 0;
+  return 1;
+}
+
+static inline void mmAtomicLockSpinRead32( mmAtomicLock32 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead32( &v->v ) < 0 ; )
+      mmAtomicPause();
+    if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) )
+      break;
+    mmAtomicAdd32( &v->v, -1 );
+  }
+  return;
+}
+
+static inline void mmAtomicLockSpinWrite32( mmAtomicLock32 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead32( &v->v ) ; )
+      mmAtomicPause();
+    if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) )
+      break;
+  }
+  return;
+}
+
+static inline int mmAtomicLockTryRead32( mmAtomicLock32 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead32( &v->v ) < 0 )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicAddTestNegative32( &v->v, 1 ) ) )
+        return 1;
+      mmAtomicAdd32( &v->v, -1 );
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline int mmAtomicLockTryWrite32( mmAtomicLock32 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead32( &v->v ) )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicCmpXchg32( &v->v, 0, MM_ATOMIC_LOCK32_WRITE ) ) )
+        return 1;
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline void mmAtomicLockDoneRead32( mmAtomicLock32 *v )
+{
+  mmAtomicAdd32( &v->v, -1 );
+  return;
+}
+
+static inline void mmAtomicLockDoneWrite32( mmAtomicLock32 *v )
+{
+  mmAtomicAdd32( &v->v, -MM_ATOMIC_LOCK32_WRITE );
+  return;
+}
+
+
+////
+
+
+#ifdef MM_ATOMIC_64_BITS_SUPPORT
+
+typedef struct { mmAtomic64 v; } mmAtomicLock64;
+
+#define MM_ATOMIC_LOCK64_WRITE (-((int64_t)0x7fffffffffffffff))
+
+static inline void mmAtomicLockInit64( mmAtomicLock64 *v )
+{
+  mmAtomicWrite64( &v->v, 0 );
+  return;
+}
+
+static inline int mmAtomicLockAttemptRead64( mmAtomicLock64 *v )
+{
+  if( mmAtomicAddTestNegative64( &v->v, 1 ) )
+  {
+    mmAtomicAdd64( &v->v, -1 );
+    return 0;
+  }
+  return 1;
+}
+
+static inline int mmAtomicLockAttemptWrite64( mmAtomicLock64 *v )
+{
+  if( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) )
+    return 0;
+  return 1;
+}
+
+static inline void mmAtomicLockSpinRead64( mmAtomicLock64 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead64( &v->v ) < 0 ; )
+      mmAtomicPause();
+    if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) )
+      break;
+    mmAtomicAdd64( &v->v, -1 );
+  }
+  return;
+}
+
+static inline void mmAtomicLockSpinWrite64( mmAtomicLock64 *v )
+{
+  for( ; ; )
+  {
+    for( ; mmAtomicRead64( &v->v ) ; )
+      mmAtomicPause();
+    if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) )
+      break;
+  }
+  return;
+}
+
+static inline int mmAtomicLockTryRead64( mmAtomicLock64 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead64( &v->v ) < 0 )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicAddTestNegative64( &v->v, 1 ) ) )
+        return 1;
+      mmAtomicAdd64( &v->v, -1 );
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline int mmAtomicLockTryWrite64( mmAtomicLock64 *v, int spincount )
+{
+  do
+  {
+    if( mmAtomicRead64( &v->v ) )
+      mmAtomicPause();
+    else
+    {
+      if( !( mmAtomicCmpXchg64( &v->v, 0, MM_ATOMIC_LOCK64_WRITE ) ) )
+        return 1;
+    }
+  } while( --spincount );
+  return 0;
+}
+
+static inline void mmAtomicLockDoneRead64( mmAtomicLock64 *v )
+{
+  mmAtomicAdd64( &v->v, -1 );
+  return;
+}
+
+static inline void mmAtomicLockDoneWrite64( mmAtomicLock64 *v )
+{
+  mmAtomicAdd64( &v->v, -MM_ATOMIC_LOCK64_WRITE );
+  return;
+}
+
+#endif
+
+
+
+////////////////
+
+
+
+#define MM_ATOMIC_LIST_BUSY ((void *)0x1)
+
+typedef struct
+{
+  mmAtomicP prev; /* mmAtomicP* to &(prev->next) */
+  mmAtomicP next; /* void* to next */
+  mmAtomic32 status;
+} mmAtomicListNode;
+
+#define MM_ATOMIC_LIST_VALID (0x0)
+#define MM_ATOMIC_LIST_DELETED (0x1)
+
+typedef struct
+{
+  mmAtomicP first; /* void* to item */
+  mmAtomicP last; /* mmAtomicP* to &(lastitem->next) */
+} mmAtomicListDualHead;
+
+
+void mmAtomicListAdd( mmAtomicP *list, void *item, intptr_t offset );
+void mmAtomicListRemove( void *item, intptr_t offset );
+
+static inline void *mmAtomicListFirst( mmAtomicP *head )
+{
+  void *item;
+  for( ; ( item = mmAtomicReadP( head ) ) == MM_ATOMIC_LIST_BUSY ; )
+    mmAtomicPause();
+  return item;
+}
+
+static inline void *mmAtomicListNext( mmAtomicListNode *node )
+{
+  void *item;
+  for( ; ; )
+  {
+    item = mmAtomicReadP( &node->next );
+    if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED )
+      return 0;
+    /* At the time we have read node->next, the node has not been deleted yet */
+    if( item != MM_ATOMIC_LIST_BUSY )
+      break;
+    mmAtomicPause();
+  }
+  return item;
+}
+
+
+void mmAtomicListDualInit( mmAtomicListDualHead *head );
+void mmAtomicListDualAddFirst( mmAtomicListDualHead *head, void *item, intptr_t offset );
+void mmAtomicListDualAddLast( mmAtomicListDualHead *head, void *item, intptr_t offset );
+void mmAtomicListDualRemove( mmAtomicListDualHead *head, void *item, intptr_t offset );
+
+static inline void *mmAtomicListDualFirst( mmAtomicListDualHead *head )
+{
+  void *item;
+  for( ; ( item = mmAtomicReadP( &head->first ) ) == MM_ATOMIC_LIST_BUSY ; )
+    mmAtomicPause();
+  return item;
+}
+
+static inline void *mmAtomicListDualNext( mmAtomicListNode *node )
+{
+  void *item;
+  for( ; ; )
+  {
+    item = mmAtomicReadP( &node->next );
+    if( mmAtomicRead32( &node->status ) == MM_ATOMIC_LIST_DELETED )
+      return 0;
+    /* At the time we have read node->next, the node has not been deleted yet */
+    if( item != MM_ATOMIC_LIST_BUSY )
+      break;
+    mmAtomicPause();
+  }
+  return item;
+}
+
+
+
+////////////////
+
+
+
+/*
+#define MM_ATOMIC_BARRIER_DEBUG
+*/
+
+
+#define MM_ATOMIC_BARRIER_DELAYED_RESET
+
+
+typedef struct
+{
+  int32_t clearcounter;
+  int32_t yieldcounter;
+} mmAtomicBarrierStat;
+
+typedef struct
+{
+  mmAtomic32 flag MM_CACHE_ALIGN;
+  mmAtomic32 counter MM_CACHE_ALIGN;
+  volatile int32_t flagref MM_CACHE_ALIGN;
+  void *parent MM_CACHE_ALIGN;
+  int32_t resetvalue;
+} mmAtomicBarrier;
+
+void mmAtomicBarrierBuild( mmAtomicBarrier *barrier, int childcount, mmAtomicBarrier *parent );
+int mmAtomicBarrierWait( mmAtomicBarrier *barrier, int32_t spinwaitcounter, mmAtomicBarrierStat *barrierstat );
+
+
+
+
+////
+
+
+
+typedef struct
+{
+  mmAtomic32 counter MM_CACHE_ALIGN;
+  /* Data below remains constant */
+  void *parent MM_CACHE_ALIGN;
+  int32_t resetvalue;
+} mmAtomicCounterNode;
+
+typedef struct
+{
+  int32_t lockcount;
+  int32_t nodecount;
+  mmAtomicCounterNode *nodearray;
+  mmAtomicCounterNode **locknode;
+} mmAtomicCounter;
+
+void mmAtomicCounterInit( mmAtomicCounter *counter, int lockcount, int stagesize );
+void mmAtomicCounterDestroy( mmAtomicCounter *counter );
+int mmAtomicCounterHit( mmAtomicCounter *counter, int lockindex );
+
+
+
+////
+
+
+/*
+Stockpile callbacks for freeing items
+
+*/
+
+typedef struct
+{
+  mmAtomicP p;
+
+
+} mmAtomicRcu;
+
+static inline void mmAtomicRcuEnter( mmAtomicRcu *rcu )
+{
+  /* On archs with messed up memory models like Alpha, we would need some memory barrier here */
+  return;
+}
+
+static inline void mmAtomicRcuLeave( mmAtomicRcu *rcu )
+{
+  /* On archs with messed up memory models like Alpha, we would need some memory barrier here */
+  return;
+}
+
+
+/*
+To reiterate, synchronize_rcu() waits only for ongoing RCU read-side critical sections to complete,
+not necessarily for any that begin after synchronize_rcu() is invoked.
+*/
+static inline void mmAtomicRcuSync()
+{
+
+}
+
+static inline void mmAtomicRcuRead()
+{
+
+
+}
+
+
+static inline void mmAtomicRcuWrite()
+{
+
+
+}
+
+
+
+#endif
+
+
diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.c b/ecere/src/gfx/newFonts/cc/mmbitmap.c
new file mode 100644 (file)
index 0000000..dcd2bf0
--- /dev/null
@@ -0,0 +1,334 @@
+/* *****************************************************************************
+ * 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;
+}
diff --git a/ecere/src/gfx/newFonts/cc/mmbitmap.h b/ecere/src/gfx/newFonts/cc/mmbitmap.h
new file mode 100644 (file)
index 0000000..75ba883
--- /dev/null
@@ -0,0 +1,211 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+#include "cpuconfig.h"
+#include "mm.h"
+
+#if !defined(CPUCONF_LONG_BITSHIFT) || !defined(CPUCONF_LONG_BITS)
+ #error Preprocessor symbols CPUCONF_LONG_BITSHIFT and CPUCONF_LONG_BITS are undefined!
+ #error This header requires cpuconfig.h
+#endif
+
+#if !defined(MM_ATOMIC_SUPPORT)
+ #warning Compiling mmbitmap without atomic support, it is going to be SLOW.
+ #warning This header requires mm.h
+#endif
+
+
+typedef struct
+{
+  size_t entrycount;
+  size_t mapsize;
+#ifdef MM_ATOMIC_SUPPORT
+  mmAtomicL *map;
+#else
+  long *map;
+  mtMutex mutex;
+#endif
+} mmBitMap;
+
+int mmBitMapInit( mmBitMap *bitmap, size_t entrycount, int initvalue );
+void mmBitMapReset( mmBitMap *bitmap, int resetvalue );
+void mmBitMapResetRange( mmBitMap *bitmap, int minimumentrycount, int resetvalue );
+void mmBitMapFree( mmBitMap *bitmap );
+
+int mmBitMapFindSet( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex );
+int mmBitMapFindClear( mmBitMap *bitmap, size_t entryindex, size_t entryindexlast, size_t *retentryindex );
+
+
+////
+
+
+/* No atomic locking, single-threaded access */
+
+static inline int mmBitMapDirectGet( mmBitMap *bitmap, size_t entryindex )
+{
+  int value;
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & 0x1);
+#else
+  value = ( bitmap->map[index] >> shift ) & 0x1;
+#endif
+  return value;
+}
+
+static inline void mmBitMapDirectSet( mmBitMap *bitmap, size_t entryindex )
+{
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) |= (long)1 << shift;
+#else
+  bitmap->map[index] |= (long)1 << shift;
+#endif
+  return;
+}
+
+static inline void mmBitMapDirectClear( mmBitMap *bitmap, size_t entryindex )
+{
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) &= ~( (long)1 << shift );
+#else
+  bitmap->map[index] &= ~( (long)1 << shift );
+#endif
+  return;
+}
+
+static inline int mmBitMapDirectMaskGet( mmBitMap *bitmap, size_t entryindex, long mask )
+{
+  int value;
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  value = (int)(( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) >> shift ) & mask);
+#else
+  value = ( bitmap->map[index] >> shift ) & mask;
+#endif
+  return value;
+}
+
+static inline void mmBitMapDirectMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask )
+{
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) = ( MM_ATOMIC_ACCESS_L( &bitmap->map[index] ) & ~( mask << shift ) ) | ( value << shift );
+#else
+  bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift );
+#endif
+  return;
+}
+
+
+////
+
+
+/* Atomic locking, multi-threaded access */
+
+static inline int mmBitMapGet( mmBitMap *bitmap, size_t entryindex )
+{
+  int value;
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & 0x1);
+#else
+  mtMutexLock( &bitmap->mutex );
+  value = ( bitmap->map[index] >> shift ) & 0x1;
+  mtMutexUnlock( &bitmap->mutex );
+#endif
+  return value;
+}
+
+static inline void mmBitMapSet( mmBitMap *bitmap, size_t entryindex )
+{
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+ #ifdef BP_BITMAP_PREWRITE_CHECK
+  if( !( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) ) )
+    mmAtomicOrL( &bitmap->map[index], (long)1 << shift );
+ #else
+  mmAtomicOrL( &bitmap->map[index], (long)1 << shift );
+ #endif
+#else
+  mtMutexLock( &bitmap->mutex );
+  bitmap->map[index] |= (long)1 << shift;
+  mtMutexUnlock( &bitmap->mutex );
+#endif
+  return;
+}
+
+static inline void mmBitMapClear( mmBitMap *bitmap, size_t entryindex )
+{
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+ #ifdef BP_BITMAP_PREWRITE_CHECK
+  if( mmAtomicReadL( &bitmap->map[index] ) & ( (long)1 << shift ) )
+    mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) );
+ #else
+  mmAtomicAndL( &bitmap->map[index], ~( (long)1 << shift ) );
+ #endif
+#else
+  mtMutexLock( &bitmap->mutex );
+  bitmap->map[index] &= ~( (long)1 << shift );
+  mtMutexUnlock( &bitmap->mutex );
+#endif
+  return;
+}
+
+static inline int mmBitMapMaskGet( mmBitMap *bitmap, size_t entryindex, long mask )
+{
+  int value;
+  size_t index, shift;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  value = (int)(( mmAtomicReadL( &bitmap->map[index] ) >> shift ) & mask);
+#else
+  mtMutexLock( &bitmap->mutex );
+  value = ( bitmap->map[index] >> shift ) & mask;
+  mtMutexUnlock( &bitmap->mutex );
+#endif
+  return value;
+}
+
+static inline void mmBitMapMaskSet( mmBitMap *bitmap, size_t entryindex, long value, long mask )
+{
+  size_t index, shift;
+  long oldvalue, newvalue;
+  index = entryindex >> CPUCONF_LONG_BITSHIFT;
+  shift = entryindex & ( CPUCONF_LONG_BITS - 1 );
+#ifdef MM_ATOMIC_SUPPORT
+  for( ; ; )
+  {
+    oldvalue = (int)mmAtomicReadL( &bitmap->map[index] );
+    newvalue = ( oldvalue & ~( mask << shift ) ) | ( value << shift );
+    if( mmAtomicCmpReplaceL( &bitmap->map[index], oldvalue, newvalue ) )
+      break;
+  }
+#else
+  mtMutexLock( &bitmap->mutex );
+  bitmap->map[index] = ( bitmap->map[index] & ~( mask << shift ) ) | ( value << shift );
+  mtMutexUnlock( &bitmap->mutex );
+#endif
+  return;
+}
diff --git a/ecere/src/gfx/newFonts/cc/mmthread.h b/ecere/src/gfx/newFonts/cc/mmthread.h
new file mode 100644 (file)
index 0000000..71f07ab
--- /dev/null
@@ -0,0 +1,530 @@
+/* *****************************************************************************
+ * Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+/**
+ * @file
+ *
+ * Threading interface, POSIX implementation.
+ */
+
+
+
+#if defined(COMPILE_FOR_VSL)
+ #define MT_QT
+#endif
+
+
+
+#if defined(MT_QT)
+
+ #include "mmthreadqt.h"
+
+#elif defined(MT_DISABLED)
+
+struct mtNull
+{
+};
+
+typedef struct mtNull mtMutex;
+typedef struct mtNull mtSpin;
+typedef struct mtNull mtSignal;
+
+ #define mtMutexInit(a)
+ #define mtMutexDestroy(a)
+ #define mtMutexLock(a)
+ #define mtMutexUnlock(a)
+ #define mtMutexTryLock(a)
+
+ #define mtSpinInit(a)
+ #define mtSpinDestroy(a)
+ #define mtSpinLock(a)
+ #define mtSpinUnlock(a)
+ #define mtSpinTryLock(a)
+
+ #define mtSignalInit(a)
+ #define mtSignalDestroy(a)
+ #define mtSignalDispatch(a)
+ #define mtSignalBroadcast(a)
+ #define mtSignalMutexWait(a,b)
+
+#else
+
+ #include <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
+
+
diff --git a/ecere/src/gfx/newFonts/drawManager.ec b/ecere/src/gfx/newFonts/drawManager.ec
new file mode 100644 (file)
index 0000000..09ddc0c
--- /dev/null
@@ -0,0 +1,862 @@
+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;
+   }
+}
diff --git a/ecere/src/gfx/newFonts/fontManager.ec b/ecere/src/gfx/newFonts/fontManager.ec
new file mode 100644 (file)
index 0000000..d783bbd
--- /dev/null
@@ -0,0 +1,1666 @@
+/* *****************************************************************************
+ * Original Version Copyright (c) 2007-2014 Alexis Naveros.
+ *
+ * Ecere Corporation has unlimited/unrestricted rights.
+ * *****************************************************************************/
+import "LinkList"
+import "File"
+
+import "atlasBuilder"
+
+#include <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);
+   }
+}
diff --git a/ecere/src/gfx/newFonts/fontRenderer.ec b/ecere/src/gfx/newFonts/fontRenderer.ec
new file mode 100644 (file)
index 0000000..00f361b
--- /dev/null
@@ -0,0 +1,196 @@
+/* *****************************************************************************
+ * 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;
+   }
+}
diff --git a/ecere/src/gfx/newFonts/textureManager.ec b/ecere/src/gfx/newFonts/textureManager.ec
new file mode 100644 (file)
index 0000000..cdca66f
--- /dev/null
@@ -0,0 +1,154 @@
+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 );
+   }
+}