]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
gnulib module 'linkedhash-list'.
authorBruno Haible <bruno@clisp.org>
Thu, 27 Jul 2006 12:09:06 +0000 (12:09 +0000)
committerBruno Haible <bruno@clisp.org>
Tue, 23 Jun 2009 10:13:42 +0000 (12:13 +0200)
gettext-tools/lib/gl_anyhash_list1.h [new file with mode: 0644]
gettext-tools/lib/gl_anyhash_list2.h [new file with mode: 0644]
gettext-tools/lib/gl_anylinked_list1.h [new file with mode: 0644]
gettext-tools/lib/gl_anylinked_list2.h [new file with mode: 0644]
gettext-tools/lib/gl_linkedhash_list.c [new file with mode: 0644]
gettext-tools/lib/gl_linkedhash_list.h [new file with mode: 0644]

diff --git a/gettext-tools/lib/gl_anyhash_list1.h b/gettext-tools/lib/gl_anyhash_list1.h
new file mode 100644 (file)
index 0000000..1f1351e
--- /dev/null
@@ -0,0 +1,28 @@
+/* Sequential list data type implemented by a hash table with another list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Common code of
+   gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c.  */
+
+/* Hash table entry.  */
+struct gl_hash_entry
+{
+  struct gl_hash_entry *hash_next;  /* chain of entries in same bucket */
+  size_t hashcode;                  /* cache of values' common hash code */
+};
+typedef struct gl_hash_entry * gl_hash_entry_t;
diff --git a/gettext-tools/lib/gl_anyhash_list2.h b/gettext-tools/lib/gl_anyhash_list2.h
new file mode 100644 (file)
index 0000000..eb8d6e9
--- /dev/null
@@ -0,0 +1,128 @@
+/* Sequential list data type implemented by a hash table with another list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Common code of
+   gl_linkedhash_list.c, gl_avltreehash_list.c, gl_rbtreehash_list.c.  */
+
+/* Array of primes, approximately in steps of factor 1.2.
+   This table was computed by executing the Common Lisp expression
+     (dotimes (i 244) (format t "nextprime(~D)~%" (ceiling (expt 1.2d0 i))))
+   and feeding the result to PARI/gp.  */
+static const size_t primes[] =
+  {
+    11, 13, 17, 19, 23, 29, 37, 41, 47, 59, 67, 83, 97, 127, 139, 167, 199,
+    239, 293, 347, 419, 499, 593, 709, 853, 1021, 1229, 1471, 1777, 2129, 2543,
+    3049, 3659, 4391, 5273, 6323, 7589, 9103, 10937, 13109, 15727, 18899,
+    22651, 27179, 32609, 39133, 46957, 56359, 67619, 81157, 97369, 116849,
+    140221, 168253, 201907, 242309, 290761, 348889, 418667, 502409, 602887,
+    723467, 868151, 1041779, 1250141, 1500181, 1800191, 2160233, 2592277,
+    3110741, 3732887, 4479463, 5375371, 6450413, 7740517, 9288589, 11146307,
+    13375573, 16050689, 19260817, 23112977, 27735583, 33282701, 39939233,
+    47927081, 57512503, 69014987, 82818011, 99381577, 119257891, 143109469,
+    171731387, 206077643, 247293161, 296751781, 356102141, 427322587,
+    512787097, 615344489, 738413383, 886096061, 1063315271, 1275978331,
+    1531174013, 1837408799, 2204890543UL, 2645868653UL, 3175042391UL,
+    3810050851UL,
+#if SIZE_MAX > 4294967295UL
+    4572061027UL, 5486473229UL, 6583767889UL, 7900521449UL, 9480625733UL,
+    11376750877UL, 13652101063UL, 16382521261UL, 19659025513UL, 23590830631UL,
+    28308996763UL, 33970796089UL, 40764955463UL, 48917946377UL, 58701535657UL,
+    70441842749UL, 84530211301UL, 101436253561UL, 121723504277UL,
+    146068205131UL, 175281846149UL, 210338215379UL, 252405858521UL,
+    302887030151UL, 363464436191UL, 436157323417UL, 523388788231UL,
+    628066545713UL, 753679854847UL, 904415825857UL, 1085298991109UL,
+    1302358789181UL, 1562830547009UL, 1875396656429UL, 2250475987709UL,
+    2700571185239UL, 3240685422287UL, 3888822506759UL, 4666587008147UL,
+    5599904409713UL, 6719885291641UL, 8063862349969UL, 9676634819959UL,
+    11611961783951UL, 13934354140769UL, 16721224968907UL, 20065469962669UL,
+    24078563955191UL, 28894276746229UL, 34673132095507UL, 41607758514593UL,
+    49929310217531UL, 59915172260971UL, 71898206713183UL, 86277848055823UL,
+    103533417666967UL, 124240101200359UL, 149088121440451UL, 178905745728529UL,
+    214686894874223UL, 257624273849081UL, 309149128618903UL, 370978954342639UL,
+    445174745211143UL, 534209694253381UL, 641051633104063UL, 769261959724877UL,
+    923114351670013UL, 1107737222003791UL, 1329284666404567UL,
+    1595141599685509UL, 1914169919622551UL, 2297003903547091UL,
+    2756404684256459UL, 3307685621107757UL, 3969222745329323UL,
+    4763067294395177UL, 5715680753274209UL, 6858816903929113UL,
+    8230580284714831UL, 9876696341657791UL, 11852035609989371UL,
+    14222442731987227UL, 17066931278384657UL, 20480317534061597UL,
+    24576381040873903UL, 29491657249048679UL, 35389988698858471UL,
+    42467986438630267UL, 50961583726356109UL, 61153900471627387UL,
+    73384680565952851UL, 88061616679143347UL, 105673940014972061UL,
+    126808728017966413UL, 152170473621559703UL, 182604568345871671UL,
+    219125482015045997UL, 262950578418055169UL, 315540694101666193UL,
+    378648832921999397UL, 454378599506399233UL, 545254319407679131UL,
+    654305183289214771UL, 785166219947057701UL, 942199463936469157UL,
+    1130639356723763129UL, 1356767228068515623UL, 1628120673682218619UL,
+    1953744808418662409UL, 2344493770102394881UL, 2813392524122873857UL,
+    3376071028947448339UL, 4051285234736937517UL, 4861542281684325481UL,
+    5833850738021191727UL, 7000620885625427969UL, 8400745062750513217UL,
+    10080894075300616261UL, 12097072890360739951UL, 14516487468432885797UL,
+    17419784962119465179UL,
+#endif
+    SIZE_MAX /* sentinel, to ensure the search terminates */
+  };
+
+/* Return a suitable prime >= ESTIMATE.  */
+static size_t
+next_prime (size_t estimate)
+{
+  size_t i;
+
+  for (i = 0; i < sizeof (primes) / sizeof (primes[0]); i++)
+    if (primes[i] >= estimate)
+      return primes[i];
+  return SIZE_MAX; /* not a prime, but better than nothing */
+}
+
+/* Resize the hash table with a new estimated size.  */
+static void
+hash_resize (gl_list_t list, size_t estimate)
+{
+  size_t new_size = next_prime (estimate);
+
+  if (new_size > list->table_size)
+    {
+      gl_hash_entry_t *old_table = list->table;
+      /* Allocate the new table.  */
+      gl_hash_entry_t *new_table =
+       (gl_hash_entry_t *) xzalloc (new_size * sizeof (gl_hash_entry_t));
+      size_t i;
+
+      /* Iterate through the entries of the old table.  */
+      for (i = list->table_size; i > 0; )
+       {
+         gl_hash_entry_t node = old_table[--i];
+
+         while (node != NULL)
+           {
+             gl_hash_entry_t next = node->hash_next;
+             /* Add the entry to the new table.  */
+             size_t index = node->hashcode % new_size;
+             node->hash_next = new_table[index];
+             new_table[index] = node;
+
+             node = next;
+           }
+       }
+
+      list->table = new_table;
+      list->table_size = new_size;
+      free (old_table);
+    }
+}
diff --git a/gettext-tools/lib/gl_anylinked_list1.h b/gettext-tools/lib/gl_anylinked_list1.h
new file mode 100644 (file)
index 0000000..988657e
--- /dev/null
@@ -0,0 +1,49 @@
+/* Sequential list data type implemented by a linked list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Common code of gl_linked_list.c and gl_linkedhash_list.c.  */
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Concrete list node implementation, valid for this file only.  */
+struct gl_list_node_impl
+{
+#if WITH_HASHTABLE
+  struct gl_hash_entry h;           /* hash table entry fields; must be first */
+#endif
+  struct gl_list_node_impl *next;
+  struct gl_list_node_impl *prev;
+  const void *value;
+};
+
+/* Concrete gl_list_impl type, valid for this file only.  */
+struct gl_list_impl
+{
+  struct gl_list_impl_base base;
+#if WITH_HASHTABLE
+  /* A hash table: managed as an array of collision lists.  */
+  struct gl_hash_entry **table;
+  size_t table_size;
+#endif
+  /* A circular list anchored at root.
+     The first node is = root.next, the last node is = root.prev.
+     The root's value is unused.  */
+  struct gl_list_node_impl root;
+  /* Number of list nodes, excluding the root.  */
+  size_t count;
+};
diff --git a/gettext-tools/lib/gl_anylinked_list2.h b/gettext-tools/lib/gl_anylinked_list2.h
new file mode 100644 (file)
index 0000000..da4e881
--- /dev/null
@@ -0,0 +1,860 @@
+/* Sequential list data type implemented by a linked list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+/* Common code of gl_linked_list.c and gl_linkedhash_list.c.  */
+
+/* If the symbol SIGNAL_SAFE_LIST is defined, the code is compiled in such
+   a way that a gl_list_t data structure may be used from within a signal
+   handler.  The operations allowed in the signal handler are:
+     gl_list_iterator, gl_list_iterator_next, gl_list_iterator_free.
+   The list and node fields that are therefore accessed from the signal handler
+   are:
+     list->root, node->next, node->value.
+   We are careful to make modifications to these fields only in an order
+   that maintains the consistency of the list data structure at any moment,
+   and we use 'volatile' assignments to prevent the compiler from reordering
+   such assignments.  */
+#ifdef SIGNAL_SAFE_LIST
+# define ASYNCSAFE(type) *(volatile type *)
+#else
+# define ASYNCSAFE(type)
+#endif
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+static gl_list_t
+gl_linked_create_empty (gl_list_implementation_t implementation,
+                       gl_listelement_equals_fn equals_fn,
+                       gl_listelement_hashcode_fn hashcode_fn,
+                       bool allow_duplicates)
+{
+  struct gl_list_impl *list =
+    (struct gl_list_impl *) xmalloc (sizeof (struct gl_list_impl));
+
+  list->base.vtable = implementation;
+  list->base.equals_fn = equals_fn;
+  list->base.hashcode_fn = hashcode_fn;
+  list->base.allow_duplicates = allow_duplicates;
+#if WITH_HASHTABLE
+  list->table_size = 11;
+  list->table =
+    (gl_hash_entry_t *) xzalloc (list->table_size * sizeof (gl_hash_entry_t));
+#endif
+  list->root.next = &list->root;
+  list->root.prev = &list->root;
+  list->count = 0;
+
+  return list;
+}
+
+static gl_list_t
+gl_linked_create (gl_list_implementation_t implementation,
+                 gl_listelement_equals_fn equals_fn,
+                 gl_listelement_hashcode_fn hashcode_fn,
+                 bool allow_duplicates,
+                 size_t count, const void **contents)
+{
+  struct gl_list_impl *list =
+    (struct gl_list_impl *) xmalloc (sizeof (struct gl_list_impl));
+  gl_list_node_t tail;
+
+  list->base.vtable = implementation;
+  list->base.equals_fn = equals_fn;
+  list->base.hashcode_fn = hashcode_fn;
+  list->base.allow_duplicates = allow_duplicates;
+#if WITH_HASHTABLE
+  {
+    size_t estimate = xsum (count, count / 2); /* 1.5 * count */
+    if (estimate < 10)
+      estimate = 10;
+    list->table_size = next_prime (estimate);
+    list->table =
+      (gl_hash_entry_t *) xzalloc (list->table_size * sizeof (gl_hash_entry_t));
+  }
+#endif
+  list->count = count;
+  tail = &list->root;
+  for (; count > 0; contents++, count--)
+    {
+      gl_list_node_t node =
+       (struct gl_list_node_impl *)
+       xmalloc (sizeof (struct gl_list_node_impl));
+
+      node->value = *contents;
+#if WITH_HASHTABLE
+      node->h.hashcode =
+       (list->base.hashcode_fn != NULL
+        ? list->base.hashcode_fn (node->value)
+        : (size_t)(uintptr_t) node->value);
+
+      /* Add node to the hash table.  */
+      add_to_bucket (list, node);
+#endif
+
+      /* Add node to the list.  */
+      node->prev = tail;
+      tail->next = node;
+      tail = node;
+    }
+  tail->next = &list->root;
+  list->root.prev = tail;
+
+  return list;
+}
+
+static size_t
+gl_linked_size (gl_list_t list)
+{
+  return list->count;
+}
+
+static const void *
+gl_linked_node_value (gl_list_t list, gl_list_node_t node)
+{
+  return node->value;
+}
+
+static gl_list_node_t
+gl_linked_next_node (gl_list_t list, gl_list_node_t node)
+{
+  return (node->next != &list->root ? node->next : NULL);
+}
+
+static gl_list_node_t
+gl_linked_previous_node (gl_list_t list, gl_list_node_t node)
+{
+  return (node->prev != &list->root ? node->prev : NULL);
+}
+
+static const void *
+gl_linked_get_at (gl_list_t list, size_t position)
+{
+  size_t count = list->count;
+  gl_list_node_t node;
+
+  if (!(position < count))
+    /* Invalid argument.  */
+    abort ();
+  /* Here we know count > 0.  */
+  if (position <= ((count - 1) / 2))
+    {
+      node = list->root.next;
+      for (; position > 0; position--)
+       node = node->next;
+    }
+  else
+    {
+      position = count - 1 - position;
+      node = list->root.prev;
+      for (; position > 0; position--)
+       node = node->prev;
+    }
+  return node->value;
+}
+
+static gl_list_node_t
+gl_linked_set_at (gl_list_t list, size_t position, const void *elt)
+{
+  size_t count = list->count;
+  gl_list_node_t node;
+
+  if (!(position < count))
+    /* Invalid argument.  */
+    abort ();
+  /* Here we know count > 0.  */
+  if (position <= ((count - 1) / 2))
+    {
+      node = list->root.next;
+      for (; position > 0; position--)
+       node = node->next;
+    }
+  else
+    {
+      position = count - 1 - position;
+      node = list->root.prev;
+      for (; position > 0; position--)
+       node = node->prev;
+    }
+#if WITH_HASHTABLE
+  if (elt != node->value)
+    {
+      size_t new_hashcode =
+       (list->base.hashcode_fn != NULL
+        ? list->base.hashcode_fn (elt)
+        : (size_t)(uintptr_t) elt);
+
+      if (new_hashcode != node->h.hashcode)
+       {
+         remove_from_bucket (list, node);
+         node->value = elt;
+         node->h.hashcode = new_hashcode;
+         add_to_bucket (list, node);
+       }
+      else
+       node->value = elt;
+    }
+#else
+  node->value = elt;
+#endif
+  return node;
+}
+
+static gl_list_node_t
+gl_linked_search (gl_list_t list, const void *elt)
+{
+#if WITH_HASHTABLE
+  size_t hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (elt)
+     : (size_t)(uintptr_t) elt);
+  size_t index = hashcode % list->table_size;
+  gl_listelement_equals_fn equals = list->base.equals_fn;
+
+  if (!list->base.allow_duplicates)
+    {
+      /* Look for the first match in the hash bucket.  */
+      gl_list_node_t node;
+
+      for (node = (gl_list_node_t) list->table[index];
+          node != NULL;
+          node = (gl_list_node_t) node->h.hash_next)
+       if (node->h.hashcode == hashcode
+           && (equals != NULL
+               ? equals (elt, node->value)
+               : elt == node->value))
+         return node;
+      return NULL;
+    }
+  else
+    {
+      /* Look whether there is more than one match in the hash bucket.  */
+      bool multiple_matches = false;
+      gl_list_node_t first_match = NULL;
+      gl_list_node_t node;
+
+      for (node = (gl_list_node_t) list->table[index];
+          node != NULL;
+          node = (gl_list_node_t) node->h.hash_next)
+       if (node->h.hashcode == hashcode
+           && (equals != NULL
+               ? equals (elt, node->value)
+               : elt == node->value))
+         {
+           if (first_match == NULL)
+             first_match = node;
+           else
+             {
+               multiple_matches = true;
+               break;
+             }
+         }
+      if (!multiple_matches)
+       return first_match;
+      else
+       {
+         /* We need the match with the smallest index.  But we don't have
+            a fast mapping node -> index.  So we have to walk the list.  */
+         for (node = list->root.next; node != &list->root; node = node->next)
+           if (node->h.hashcode == hashcode
+               && (equals != NULL
+                   ? equals (elt, node->value)
+                   : elt == node->value))
+             return node;
+         /* We know there are at least two matches.  */
+         abort ();
+       }
+    }
+#else
+  gl_listelement_equals_fn equals = list->base.equals_fn;
+  gl_list_node_t node;
+
+  if (equals != NULL)
+    {
+      for (node = list->root.next; node != &list->root; node = node->next)
+       if (equals (elt, node->value))
+         return node;
+    }
+  else
+    {
+      for (node = list->root.next; node != &list->root; node = node->next)
+       if (elt == node->value)
+         return node;
+    }
+  return NULL;
+#endif
+}
+
+static size_t
+gl_linked_indexof (gl_list_t list, const void *elt)
+{
+#if WITH_HASHTABLE
+  /* Here the hash table doesn't help much.  It only allows us to minimize
+     the number of equals() calls, by looking up first the node and then
+     its index.  */
+  size_t hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (elt)
+     : (size_t)(uintptr_t) elt);
+  size_t index = hashcode % list->table_size;
+  gl_listelement_equals_fn equals = list->base.equals_fn;
+  gl_list_node_t node;
+
+  /* First step: Look up the node.  */
+  if (!list->base.allow_duplicates)
+    {
+      /* Look for the first match in the hash bucket.  */
+      for (node = (gl_list_node_t) list->table[index];
+          node != NULL;
+          node = (gl_list_node_t) node->h.hash_next)
+       if (node->h.hashcode == hashcode
+           && (equals != NULL
+               ? equals (elt, node->value)
+               : elt == node->value))
+         break;
+    }
+  else
+    {
+      /* Look whether there is more than one match in the hash bucket.  */
+      bool multiple_matches = false;
+      gl_list_node_t first_match = NULL;
+
+      for (node = (gl_list_node_t) list->table[index];
+          node != NULL;
+          node = (gl_list_node_t) node->h.hash_next)
+       if (node->h.hashcode == hashcode
+           && (equals != NULL
+               ? equals (elt, node->value)
+               : elt == node->value))
+         {
+           if (first_match == NULL)
+             first_match = node;
+           else
+             {
+               multiple_matches = true;
+               break;
+             }
+         }
+      if (multiple_matches)
+       {
+         /* We need the match with the smallest index.  But we don't have
+            a fast mapping node -> index.  So we have to walk the list.  */
+         size_t index;
+
+         for (node = list->root.next, index = 0;
+              node != &list->root;
+              node = node->next, index++)
+           if (node->h.hashcode == hashcode
+               && (equals != NULL
+                   ? equals (elt, node->value)
+                   : elt == node->value))
+             return index;
+         /* We know there are at least two matches.  */
+         abort ();
+       }
+      node = first_match;
+    }
+
+  /* Second step: Look up the index of the node.  */
+  if (node == NULL)
+    return (size_t)(-1);
+  else
+    {
+      size_t index = 0;
+
+      for (; node->prev != &list->root; node = node->prev)
+       index++;
+
+      return index;
+    }
+#else
+  gl_listelement_equals_fn equals = list->base.equals_fn;
+  gl_list_node_t node;
+
+  if (equals != NULL)
+    {
+      size_t index;
+      for (node = list->root.next, index = 0;
+          node != &list->root;
+          node = node->next, index++)
+       if (equals (elt, node->value))
+         return index;
+    }
+  else
+    {
+      size_t index;
+      for (node = list->root.next, index = 0;
+          node != &list->root;
+          node = node->next, index++)
+       if (elt == node->value)
+         return index;
+    }
+  return (size_t)(-1);
+#endif
+}
+
+static gl_list_node_t
+gl_linked_add_first (gl_list_t list, const void *elt)
+{
+  gl_list_node_t node =
+    (struct gl_list_node_impl *) xmalloc (sizeof (struct gl_list_node_impl));
+
+  ASYNCSAFE(const void *) node->value = elt;
+#if WITH_HASHTABLE
+  node->h.hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (node->value)
+     : (size_t)(uintptr_t) node->value);
+
+  /* Add node to the hash table.  */
+  add_to_bucket (list, node);
+#endif
+
+  /* Add node to the list.  */
+  node->prev = &list->root;
+  ASYNCSAFE(gl_list_node_t) node->next = list->root.next;
+  node->next->prev = node;
+  ASYNCSAFE(gl_list_node_t) list->root.next = node;
+  list->count++;
+
+#if WITH_HASHTABLE
+  hash_resize_after_add (list);
+#endif
+
+  return node;
+}
+
+static gl_list_node_t
+gl_linked_add_last (gl_list_t list, const void *elt)
+{
+  gl_list_node_t node =
+    (struct gl_list_node_impl *) xmalloc (sizeof (struct gl_list_node_impl));
+
+  ASYNCSAFE(const void *) node->value = elt;
+#if WITH_HASHTABLE
+  node->h.hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (node->value)
+     : (size_t)(uintptr_t) node->value);
+
+  /* Add node to the hash table.  */
+  add_to_bucket (list, node);
+#endif
+
+  /* Add node to the list.  */
+  ASYNCSAFE(gl_list_node_t) node->next = &list->root;
+  node->prev = list->root.prev;
+  ASYNCSAFE(gl_list_node_t) node->prev->next = node;
+  list->root.prev = node;
+  list->count++;
+
+#if WITH_HASHTABLE
+  hash_resize_after_add (list);
+#endif
+
+  return node;
+}
+
+static gl_list_node_t
+gl_linked_add_before (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+  gl_list_node_t new_node =
+    (struct gl_list_node_impl *) xmalloc (sizeof (struct gl_list_node_impl));
+
+  ASYNCSAFE(const void *) new_node->value = elt;
+#if WITH_HASHTABLE
+  new_node->h.hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (new_node->value)
+     : (size_t)(uintptr_t) new_node->value);
+
+  /* Add new_node to the hash table.  */
+  add_to_bucket (list, new_node);
+#endif
+
+  /* Add new_node to the list.  */
+  ASYNCSAFE(gl_list_node_t) new_node->next = node;
+  new_node->prev = node->prev;
+  ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node;
+  node->prev = new_node;
+  list->count++;
+
+#if WITH_HASHTABLE
+  hash_resize_after_add (list);
+#endif
+
+  return new_node;
+}
+
+static gl_list_node_t
+gl_linked_add_after (gl_list_t list, gl_list_node_t node, const void *elt)
+{
+  gl_list_node_t new_node =
+    (struct gl_list_node_impl *) xmalloc (sizeof (struct gl_list_node_impl));
+
+  ASYNCSAFE(const void *) new_node->value = elt;
+#if WITH_HASHTABLE
+  new_node->h.hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (new_node->value)
+     : (size_t)(uintptr_t) new_node->value);
+
+  /* Add new_node to the hash table.  */
+  add_to_bucket (list, new_node);
+#endif
+
+  /* Add new_node to the list.  */
+  new_node->prev = node;
+  ASYNCSAFE(gl_list_node_t) new_node->next = node->next;
+  new_node->next->prev = new_node;
+  ASYNCSAFE(gl_list_node_t) node->next = new_node;
+  list->count++;
+
+#if WITH_HASHTABLE
+  hash_resize_after_add (list);
+#endif
+
+  return new_node;
+}
+
+static gl_list_node_t
+gl_linked_add_at (gl_list_t list, size_t position, const void *elt)
+{
+  size_t count = list->count;
+  gl_list_node_t new_node;
+
+  if (!(position <= count))
+    /* Invalid argument.  */
+    abort ();
+
+  new_node =
+    (struct gl_list_node_impl *) xmalloc (sizeof (struct gl_list_node_impl));
+  ASYNCSAFE(const void *) new_node->value = elt;
+#if WITH_HASHTABLE
+  new_node->h.hashcode =
+    (list->base.hashcode_fn != NULL
+     ? list->base.hashcode_fn (new_node->value)
+     : (size_t)(uintptr_t) new_node->value);
+
+  /* Add new_node to the hash table.  */
+  add_to_bucket (list, new_node);
+#endif
+
+  /* Add new_node to the list.  */
+  if (position <= (count / 2))
+    {
+      gl_list_node_t node;
+
+      node = &list->root;
+      for (; position > 0; position--)
+       node = node->next;
+      new_node->prev = node;
+      ASYNCSAFE(gl_list_node_t) new_node->next = node->next;
+      new_node->next->prev = new_node;
+      ASYNCSAFE(gl_list_node_t) node->next = new_node;
+    }
+  else
+    {
+      gl_list_node_t node;
+
+      position = count - position;
+      node = &list->root;
+      for (; position > 0; position--)
+       node = node->prev;
+      ASYNCSAFE(gl_list_node_t) new_node->next = node;
+      new_node->prev = node->prev;
+      ASYNCSAFE(gl_list_node_t) new_node->prev->next = new_node;
+      node->prev = new_node;
+    }
+  list->count++;
+
+#if WITH_HASHTABLE
+  hash_resize_after_add (list);
+#endif
+
+  return new_node;
+}
+
+static bool
+gl_linked_remove_node (gl_list_t list, gl_list_node_t node)
+{
+  gl_list_node_t prev;
+  gl_list_node_t next;
+
+#if WITH_HASHTABLE
+  /* Remove node from the hash table.  */
+  remove_from_bucket (list, node);
+#endif
+
+  /* Remove node from the list.  */
+  prev = node->prev;
+  next = node->next;
+
+  ASYNCSAFE(gl_list_node_t) prev->next = next;
+  next->prev = prev;
+  list->count--;
+
+  free (node);
+  return true;
+}
+
+static bool
+gl_linked_remove_at (gl_list_t list, size_t position)
+{
+  size_t count = list->count;
+  gl_list_node_t removed_node;
+
+  if (!(position < count))
+    /* Invalid argument.  */
+    abort ();
+  /* Here we know count > 0.  */
+  if (position <= ((count - 1) / 2))
+    {
+      gl_list_node_t node;
+      gl_list_node_t after_removed;
+
+      node = &list->root;
+      for (; position > 0; position--)
+       node = node->next;
+      removed_node = node->next;
+      after_removed = node->next->next;
+      ASYNCSAFE(gl_list_node_t) node->next = after_removed;
+      after_removed->prev = node;
+    }
+  else
+    {
+      gl_list_node_t node;
+      gl_list_node_t before_removed;
+
+      position = count - 1 - position;
+      node = &list->root;
+      for (; position > 0; position--)
+       node = node->prev;
+      removed_node = node->prev;
+      before_removed = node->prev->prev;
+      node->prev = before_removed;
+      ASYNCSAFE(gl_list_node_t) before_removed->next = node;
+    }
+#if WITH_HASHTABLE
+  remove_from_bucket (list, removed_node);
+#endif
+  list->count--;
+
+  free (removed_node);
+  return true;
+}
+
+static bool
+gl_linked_remove (gl_list_t list, const void *elt)
+{
+  gl_list_node_t node = gl_linked_search (list, elt);
+
+  if (node != NULL)
+    return gl_linked_remove_node (list, node);
+  else
+    return false;
+}
+
+static void
+gl_linked_list_free (gl_list_t list)
+{
+  gl_list_node_t node;
+
+  for (node = list->root.next; node != &list->root; )
+    {
+      gl_list_node_t next = node->next;
+      free (node);
+      node = next;
+    }
+#if WITH_HASHTABLE
+  free (list->table);
+#endif
+  free (list);
+}
+
+/* --------------------- gl_list_iterator_t Data Type --------------------- */
+
+static gl_list_iterator_t
+gl_linked_iterator (gl_list_t list)
+{
+  gl_list_iterator_t result;
+
+  result.vtable = list->base.vtable;
+  result.list = list;
+  result.p = list->root.next;
+  result.q = &list->root;
+
+  return result;
+}
+
+static gl_list_iterator_t
+gl_linked_iterator_from_to (gl_list_t list,
+                           size_t start_index, size_t end_index)
+{
+  gl_list_iterator_t result;
+  size_t n1, n2, n3;
+
+  if (!(start_index <= end_index && end_index <= list->count))
+    /* Invalid arguments.  */
+    abort ();
+  result.vtable = list->base.vtable;
+  result.list = list;
+  n1 = start_index;
+  n2 = end_index - start_index;
+  n3 = list->count - end_index;
+  /* Find the maximum among n1, n2, n3, so as to reduce the number of
+     loop iterations to n1 + n2 + n3 - max(n1,n2,n3).  */
+  if (n1 > n2 && n1 > n3)
+    {
+      /* n1 is the maximum, use n2 and n3.  */
+      gl_list_node_t node;
+      size_t i;
+
+      node = &list->root;
+      for (i = n3; i > 0; i--)
+       node = node->prev;
+      result.q = node;
+      for (i = n2; i > 0; i--)
+       node = node->prev;
+      result.p = node;
+    }
+  else if (n2 > n3)
+    {
+      /* n2 is the maximum, use n1 and n3.  */
+      gl_list_node_t node;
+      size_t i;
+
+      node = list->root.next;
+      for (i = n1; i > 0; i--)
+       node = node->next;
+      result.p = node;
+
+      node = &list->root;
+      for (i = n3; i > 0; i--)
+       node = node->prev;
+      result.q = node;
+    }
+  else
+    {
+      /* n3 is the maximum, use n1 and n2.  */
+      gl_list_node_t node;
+      size_t i;
+
+      node = list->root.next;
+      for (i = n1; i > 0; i--)
+       node = node->next;
+      result.p = node;
+      for (i = n2; i > 0; i--)
+       node = node->next;
+      result.q = node;
+    }
+
+  return result;
+}
+
+static bool
+gl_linked_iterator_next (gl_list_iterator_t *iterator,
+                        const void **eltp, gl_list_node_t *nodep)
+{
+  if (iterator->p != iterator->q)
+    {
+      gl_list_node_t node = (gl_list_node_t) iterator->p;
+      *eltp = node->value;
+      if (nodep != NULL)
+       *nodep = node;
+      iterator->p = node->next;
+      return true;
+    }
+  else
+    return false;
+}
+
+static void
+gl_linked_iterator_free (gl_list_iterator_t *iterator)
+{
+}
+
+/* ---------------------- Sorted gl_list_t Data Type ---------------------- */
+
+static gl_list_node_t
+gl_linked_sortedlist_search (gl_list_t list, gl_listelement_compar_fn compar,
+                            const void *elt)
+{
+  gl_list_node_t node;
+
+  for (node = list->root.next; node != &list->root; node = node->next)
+    {
+      int cmp = compar (node->value, elt);
+
+      if (cmp > 0)
+       break;
+      if (cmp == 0)
+       return node;
+    }
+  return NULL;
+}
+
+static size_t
+gl_linked_sortedlist_indexof (gl_list_t list, gl_listelement_compar_fn compar,
+                             const void *elt)
+{
+  gl_list_node_t node;
+  size_t index;
+
+  for (node = list->root.next, index = 0;
+       node != &list->root;
+       node = node->next, index++)
+    {
+      int cmp = compar (node->value, elt);
+
+      if (cmp > 0)
+       break;
+      if (cmp == 0)
+       return index;
+    }
+  return (size_t)(-1);
+}
+
+static gl_list_node_t
+gl_linked_sortedlist_add (gl_list_t list, gl_listelement_compar_fn compar,
+                         const void *elt)
+{
+  gl_list_node_t node;
+
+  for (node = list->root.next; node != &list->root; node = node->next)
+    if (compar (node->value, elt) >= 0)
+      return gl_linked_add_before (list, node, elt);
+  return gl_linked_add_last (list, elt);
+}
+
+static bool
+gl_linked_sortedlist_remove (gl_list_t list, gl_listelement_compar_fn compar,
+                            const void *elt)
+{
+  gl_list_node_t node;
+
+  for (node = list->root.next; node != &list->root; node = node->next)
+    {
+      int cmp = compar (node->value, elt);
+
+      if (cmp > 0)
+       break;
+      if (cmp == 0)
+       return gl_linked_remove_node (list, node);
+    }
+  return false;
+}
diff --git a/gettext-tools/lib/gl_linkedhash_list.c b/gettext-tools/lib/gl_linkedhash_list.c
new file mode 100644 (file)
index 0000000..ca7f707
--- /dev/null
@@ -0,0 +1,123 @@
+/* Sequential list data type implemented by a hash table with a linked list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification.  */
+#include "gl_linkedhash_list.h"
+
+#include <stdlib.h>
+
+#include "xalloc.h"
+#include "xsize.h"
+#include "size_max.h"
+
+#ifndef uintptr_t
+# define uintptr_t unsigned long
+#endif
+
+#define WITH_HASHTABLE 1
+
+/* -------------------------- gl_list_t Data Type -------------------------- */
+
+/* Generic hash-table code.  */
+#include "gl_anyhash_list1.h"
+
+/* Generic linked list code.  */
+#include "gl_anylinked_list1.h"
+
+/* Generic hash-table code.  */
+#include "gl_anyhash_list2.h"
+
+/* Resize the hash table if needed, after list->count was incremented.  */
+static inline void
+hash_resize_after_add (gl_list_t list)
+{
+  size_t count = list->count;
+  size_t estimate = xsum (count, count / 2); /* 1.5 * count */
+  if (estimate > list->table_size)
+    hash_resize (list, estimate);
+}
+
+/* Add a node to the hash table structure.  */
+static inline void
+add_to_bucket (gl_list_t list, gl_list_node_t node)
+{
+  size_t index = node->h.hashcode % list->table_size;
+
+  node->h.hash_next = list->table[index];
+  list->table[index] = &node->h;
+}
+
+/* Remove a node from the hash table structure.  */
+static inline void
+remove_from_bucket (gl_list_t list, gl_list_node_t node)
+{
+  size_t index = node->h.hashcode % list->table_size;
+  gl_hash_entry_t *p;
+
+  for (p = &list->table[index]; ; p = &(*p)->hash_next)
+    {
+      if (*p == &node->h)
+       {
+         *p = node->h.hash_next;
+         break;
+       }
+      if (*p == NULL)
+       /* node is not in the right bucket.  Did the hash codes
+          change inadvertently?  */
+       abort ();
+    }
+}
+
+/* Generic linked list code.  */
+#include "gl_anylinked_list2.h"
+
+
+const struct gl_list_implementation gl_linkedhash_list_implementation =
+  {
+    gl_linked_create_empty,
+    gl_linked_create,
+    gl_linked_size,
+    gl_linked_node_value,
+    gl_linked_next_node,
+    gl_linked_previous_node,
+    gl_linked_get_at,
+    gl_linked_set_at,
+    gl_linked_search,
+    gl_linked_indexof,
+    gl_linked_add_first,
+    gl_linked_add_last,
+    gl_linked_add_before,
+    gl_linked_add_after,
+    gl_linked_add_at,
+    gl_linked_remove_node,
+    gl_linked_remove_at,
+    gl_linked_remove,
+    gl_linked_list_free,
+    gl_linked_iterator,
+    gl_linked_iterator_from_to,
+    gl_linked_iterator_next,
+    gl_linked_iterator_free,
+    gl_linked_sortedlist_search,
+    gl_linked_sortedlist_indexof,
+    gl_linked_sortedlist_add,
+    gl_linked_sortedlist_remove
+  };
diff --git a/gettext-tools/lib/gl_linkedhash_list.h b/gettext-tools/lib/gl_linkedhash_list.h
new file mode 100644 (file)
index 0000000..d3e71ed
--- /dev/null
@@ -0,0 +1,35 @@
+/* Sequential list data type implemented by a hash table with a linked list.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2006.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2, or (at your option)
+   any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software Foundation,
+   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
+
+#ifndef _GL_LINKEDHASH_LIST_H
+#define _GL_LINKEDHASH_LIST_H
+
+#include "gl_list.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const struct gl_list_implementation gl_linkedhash_list_implementation;
+#define GL_LINKEDHASH_LIST &gl_linkedhash_list_implementation
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GL_LINKEDHASH_LIST_H */