- Merge from trunk
[reactos.git] / dll / win32 / msi / string.c
index e5b5cc2..5644749 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2002-2004, Mike McCormack for CodeWeavers
  * Copyright 2007 Robert Shearman for CodeWeavers
+ * Copyright 2010 Hans Leidekker for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 
 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
 
-#define HASH_SIZE 0x101
-#define LONG_STR_BYTES 3
-
 typedef struct _msistring
 {
-    int hash_next;
-    UINT persistent_refcount;
-    UINT nonpersistent_refcount;
+    USHORT persistent_refcount;
+    USHORT nonpersistent_refcount;
     LPWSTR str;
 } msistring;
 
@@ -56,30 +53,14 @@ struct string_table
     UINT maxcount;         /* the number of strings */
     UINT freeslot;
     UINT codepage;
-    int hash[HASH_SIZE];
-    msistring *strings; /* an array of strings (in the tree) */
+    UINT sortcount;
+    msistring *strings; /* an array of strings */
+    UINT *sorted;       /* index */
 };
 
-static UINT msistring_makehash( const WCHAR *str )
-{
-    UINT hash = 0;
-
-    if (str==NULL)
-        return hash;
-
-    while( *str )
-    {
-        hash ^= *str++;
-        hash *= 53;
-        hash = (hash<<5) | (hash>>27);
-    }
-    return hash % HASH_SIZE;
-}
-
 static string_table *init_stringtable( int entries, UINT codepage )
 {
     string_table *st;
-    int i;
 
     if (codepage != CP_ACP && !IsValidCodePage(codepage))
     {
@@ -92,18 +73,26 @@ static string_table *init_stringtable( int entries, UINT codepage )
         return NULL;    
     if( entries < 1 )
         entries = 1;
+
     st->strings = msi_alloc_zero( sizeof (msistring) * entries );
     if( !st->strings )
     {
         msi_free( st );
         return NULL;    
     }
+
+    st->sorted = msi_alloc( sizeof (UINT) * entries );
+    if( !st->sorted )
+    {
+        msi_free( st->strings );
+        msi_free( st );
+        return NULL;
+    }
+
     st->maxcount = entries;
     st->freeslot = 1;
     st->codepage = codepage;
-
-    for( i=0; i<HASH_SIZE; i++ )
-        st->hash[i] = -1;
+    st->sortcount = 0;
 
     return st;
 }
@@ -119,12 +108,13 @@ VOID msi_destroy_stringtable( string_table *st )
             msi_free( st->strings[i].str );
     }
     msi_free( st->strings );
+    msi_free( st->sorted );
     msi_free( st );
 }
 
 static int st_find_free_entry( string_table *st )
 {
-    UINT i, sz;
+    UINT i, sz, *s;
     msistring *p;
 
     TRACE("%p\n", st);
@@ -146,7 +136,17 @@ static int st_find_free_entry( string_table *st )
     p = msi_realloc_zero( st->strings, sz*sizeof(msistring) );
     if( !p )
         return -1;
+
+    s = msi_realloc( st->sorted, sz*sizeof(UINT) );
+    if( !s )
+    {
+        msi_free( p );
+        return -1;
+    }
+
     st->strings = p;
+    st->sorted = s;
+
     st->freeslot = st->maxcount;
     st->maxcount = sz;
     if( st->strings[st->freeslot].persistent_refcount ||
@@ -155,10 +155,40 @@ static int st_find_free_entry( string_table *st )
     return st->freeslot;
 }
 
-static void set_st_entry( string_table *st, UINT n, LPWSTR str, UINT refcount, enum StringPersistence persistence )
+static int find_insert_index( const string_table *st, UINT string_id )
 {
-    UINT hash = msistring_makehash( str );
+    int i, c, low = 0, high = st->sortcount - 1;
+
+    while (low <= high)
+    {
+        i = (low + high) / 2;
+        c = lstrcmpW( st->strings[string_id].str, st->strings[st->sorted[i]].str );
 
+        if (c < 0)
+            high = i - 1;
+        else if (c > 0)
+            low = i + 1;
+        else
+            return -1; /* already exists */
+    }
+    return high + 1;
+}
+
+static void insert_string_sorted( string_table *st, UINT string_id )
+{
+    int i;
+
+    i = find_insert_index( st, string_id );
+    if (i == -1)
+        return;
+
+    memmove( &st->sorted[i] + 1, &st->sorted[i], (st->sortcount - i) * sizeof(UINT) );
+    st->sorted[i] = string_id;
+    st->sortcount++;
+}
+
+static void set_st_entry( string_table *st, UINT n, LPWSTR str, USHORT refcount, enum StringPersistence persistence )
+{
     if (persistence == StringPersistent)
     {
         st->strings[n].persistent_refcount = refcount;
@@ -172,8 +202,7 @@ static void set_st_entry( string_table *st, UINT n, LPWSTR str, UINT refcount, e
 
     st->strings[n].str = str;
 
-    st->strings[n].hash_next = st->hash[hash];
-    st->hash[hash] = n;
+    insert_string_sorted( st, n );
 
     if( n < st->maxcount )
         st->freeslot = n + 1;
@@ -207,7 +236,7 @@ static UINT msi_string2idA( const string_table *st, LPCSTR buffer, UINT *id )
     return r;
 }
 
-static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, UINT refcount, enum StringPersistence persistence )
+static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, USHORT refcount, enum StringPersistence persistence )
 {
     LPWSTR str;
     int sz;
@@ -258,42 +287,28 @@ static int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, U
     return n;
 }
 
-int msi_addstringW( string_table *st, UINT n, const WCHAR *data, int len, UINT refcount, enum StringPersistence persistence )
+int msi_addstringW( string_table *st, const WCHAR *data, int len, USHORT refcount, enum StringPersistence persistence )
 {
+    UINT n;
     LPWSTR str;
 
-    /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */
-
     if( !data )
         return 0;
     if( !data[0] )
         return 0;
-    if( n > 0 )
-    {
-        if( st->strings[n].persistent_refcount ||
-            st->strings[n].nonpersistent_refcount )
-            return -1;
-    }
-    else
+
+    if( msi_string2idW( st, data, &n ) == ERROR_SUCCESS )
     {
-        if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) )
-        {
-            if (persistence == StringPersistent)
-                st->strings[n].persistent_refcount += refcount;
-            else
-                st->strings[n].nonpersistent_refcount += refcount;
-            return n;
-        }
-        n = st_find_free_entry( st );
-        if( n == -1 )
-            return -1;
+        if (persistence == StringPersistent)
+            st->strings[n].persistent_refcount += refcount;
+        else
+            st->strings[n].nonpersistent_refcount += refcount;
+        return n;
     }
 
-    if( n < 1 )
-    {
-        ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n );
+    n = st_find_free_entry( st );
+    if( n == -1 )
         return -1;
-    }
 
     /* allocate a new string */
     if(len<0)
@@ -382,14 +397,20 @@ static UINT msi_id2stringA( const string_table *st, UINT id, LPSTR buffer, UINT
  */
 UINT msi_string2idW( const string_table *st, LPCWSTR str, UINT *id )
 {
-    UINT n, hash = msistring_makehash( str );
-    msistring *se = st->strings;
+    int i, c, low = 0, high = st->sortcount - 1;
 
-    for (n = st->hash[hash]; n != -1; n = st->strings[n].hash_next )
+    while (low <= high)
     {
-        if ((str == se[n].str) || !lstrcmpW(str, se[n].str))
+        i = (low + high) / 2;
+        c = lstrcmpW( str, st->strings[st->sorted[i]].str );
+
+        if (c < 0)
+            high = i - 1;
+        else if (c > 0)
+            low = i + 1;
+        else
         {
-            *id = n;
+            *id = st->sorted[i];
             return ERROR_SUCCESS;
         }
     }
@@ -542,7 +563,7 @@ end:
     return st;
 }
 
-UINT msi_save_string_table( const string_table *st, IStorage *storage )
+UINT msi_save_string_table( const string_table *st, IStorage *storage, UINT *bytes_per_strref )
 {
     UINT i, datasize = 0, poolsize = 0, sz, used, r, codepage, n;
     UINT ret = ERROR_FUNCTION_FAILED;
@@ -571,8 +592,16 @@ UINT msi_save_string_table( const string_table *st, IStorage *storage )
 
     used = 0;
     codepage = st->codepage;
-    pool[0]=codepage&0xffff;
-    pool[1]=(codepage>>16);
+    pool[0] = codepage & 0xffff;
+    pool[1] = codepage >> 16;
+    if (st->maxcount > 0xffff)
+    {
+        pool[1] |= 0x8000;
+        *bytes_per_strref = LONG_STR_BYTES;
+    }
+    else
+        *bytes_per_strref = sizeof(USHORT);
+
     n = 1;
     for( i=1; i<st->maxcount; i++ )
     {