1 // This file is part of Freecell Solver. It is subject to the license terms in
2 // the COPYING.txt file found in the top-level directory of this distribution
3 // and at http://fc-solve.shlomifish.org/docs/distro/COPYING.html . No part of
4 // Freecell Solver, including this file, may be copied, modified, propagated,
5 // or distributed except according to the terms contained in the COPYING file.
6 //
7 // Copyright (c) 2010 Shlomi Fish
8 // google_hash.cpp - module file for Google's dense_hash_map as adapted
9 // for Freecell Solver.
10 #include "google_hash.h"
11 
12 #if ((FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__DENSE) ||  \
13      (FCS_WHICH_COLUMNS_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__DENSE))
14 #include <google/dense_hash_set>
15 #endif
16 
17 #if ((FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__SPARSE) || \
18      (FCS_WHICH_COLUMNS_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__SPARSE))
19 #include <google/sparse_hash_set>
20 #endif
21 
22 #include "state.h"
23 
24 #if ((FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__DENSE) ||  \
25      (FCS_WHICH_COLUMNS_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__DENSE))
26 using google::dense_hash_set; // namespace where class lives by default
27 #endif
28 
29 #if ((FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__SPARSE) || \
30      (FCS_WHICH_COLUMNS_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__SPARSE))
31 using google::sparse_hash_set; // namespace where class lives by default
32 #endif
33 
34 typedef unsigned long int ub4; // unsigned 4+ bytes quantities
35 typedef unsigned char ub1;
36 
perl_hash_function(register const ub1 * s_ptr,register const ub4 length)37 static inline ub4 perl_hash_function(
38     register const ub1 *s_ptr, register const ub4 length)
39 {
40     register ub4 hash_value_int = 0;
41     register const ub1 *s_end = s_ptr + length;
42 
43     while (s_ptr < s_end)
44     {
45         hash_value_int += (hash_value_int << 5) + *(s_ptr++);
46     }
47     hash_value_int += (hash_value_int >> 5);
48 
49     return hash_value_int;
50 }
51 
52 #if (FCS_STATE_STORAGE == FCS_STATE_STORAGE_GOOGLE_DENSE_HASH)
53 
54 struct state_equality
55 {
operator ()state_equality56     bool operator()(const char *s1, const char *s2) const
57     {
58         return (s1 == s2) ||
59                (s1 && s2 && (fc_solve_state_compare(s1, s2) == 0));
60     }
61 };
62 
63 struct state_hash
64 {
operator ()state_hash65     int operator()(const char *s1) const
66     {
67         return perl_hash_function((const ub1 *)s1, sizeof(fcs_state));
68     }
69 };
70 
71 #if (FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__SPARSE)
72 typedef sparse_hash_set<char *, state_hash, state_equality> StatesGoogleHash;
73 #else
74 typedef dense_hash_set<char *, state_hash, state_equality> StatesGoogleHash;
75 #endif
76 
fc_solve_states_google_hash_new()77 extern "C" fcs_states_google_hash_handle fc_solve_states_google_hash_new()
78 {
79     static fcs_state deleted_key = {0};
80     StatesGoogleHash *ret = new StatesGoogleHash;
81 
82 #if (FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_STATES_GOOGLE_HASH__DENSE)
83     ret->set_empty_key(NULL);
84 #endif
85     ret->set_deleted_key((char *)&deleted_key);
86 
87     return (fcs_states_google_hash_handle)(ret);
88 }
89 
90 // Returns 0 if the key is new and the key/val pair was inserted.
91 //      - in that case *existing_key / *existing_val will be set to key
92 //      and val respectively.
93 // Returns 1 if the key is not new and *existing_key / *existing_val
94 // was set to it.
fc_solve_states_google_hash_insert(fcs_states_google_hash_handle void_hash,void * key,void ** existing_key)95 extern "C" bool fc_solve_states_google_hash_insert(
96     fcs_states_google_hash_handle void_hash, void *key, void **existing_key)
97 {
98     StatesGoogleHash *hash = (StatesGoogleHash *)void_hash;
99     std::pair<StatesGoogleHash::iterator, bool> result =
100         hash->insert((char *)key);
101 
102     // If an insertion took place.
103     if (result.second)
104     {
105         *existing_key = NULL;
106         return false;
107     }
108     else
109     {
110         *existing_key = (*(result.first));
111 
112         return true;
113     }
114 }
115 
fc_solve_states_google_hash_free(fcs_states_google_hash_handle void_hash)116 extern "C" void fc_solve_states_google_hash_free(
117     fcs_states_google_hash_handle void_hash)
118 {
119     StatesGoogleHash *hash = (StatesGoogleHash *)void_hash;
120 
121     delete hash;
122 
123     return;
124 }
125 
fc_solve_states_google_hash_foreach(fcs_states_google_hash_handle void_hash,bool (* should_delete_ptr)(void * key,void * context),void * context)126 extern void fc_solve_states_google_hash_foreach(
127     fcs_states_google_hash_handle void_hash,
128     bool (*should_delete_ptr)(void *key, void *context), void *context)
129 {
130     StatesGoogleHash *hash = (StatesGoogleHash *)void_hash;
131 
132     for (StatesGoogleHash::iterator it = hash->begin(); it != hash->end(); ++it)
133     {
134         if (should_delete_ptr(*(it), context))
135         {
136             hash->erase(it);
137         }
138     }
139 }
140 
141 #endif
142 
143 #if (FCS_STACK_STORAGE == FCS_STACK_STORAGE_GOOGLE_DENSE_HASH)
144 
145 struct column_equality
146 {
operator ()column_equality147     bool operator()(const char *s1, const char *s2) const
148     {
149         return (s1 == s2) ||
150                (s1 && s2 &&
151                    (fc_solve_stack_compare_for_comparison(s1, s2) == 0));
152     }
153 };
154 
155 struct column_hash
156 {
operator ()column_hash157     int operator()(const char *s1) const
158     {
159         return perl_hash_function((const ub1 *)s1, fcs_col_len(s1) + 1);
160     }
161 };
162 
163 #if (FCS_WHICH_COLUMNS_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__SPARSE)
164 typedef sparse_hash_set<char *, column_hash, column_equality> ColumnsGoogleHash;
165 #else
166 typedef dense_hash_set<char *, column_hash, column_equality> ColumnsGoogleHash;
167 #endif
168 
fc_solve_columns_google_hash_new()169 extern "C" fcs_columns_google_hash_handle fc_solve_columns_google_hash_new()
170 {
171     static char deleted_key[3] = "\xFF\xFF";
172     ColumnsGoogleHash *ret = new ColumnsGoogleHash;
173 
174 #if (FCS_WHICH_STATES_GOOGLE_HASH == FCS_WHICH_COLUMNS_GOOGLE_HASH__DENSE)
175     ret->set_empty_key(NULL);
176 #endif
177     ret->set_deleted_key(deleted_key);
178 
179     return (fcs_columns_google_hash_handle)(ret);
180 }
181 
182 // Returns 0 if the key is new and the key/val pair was inserted.
183 //      - in that case *existing_key / *existing_val will be set to key
184 //      and val respectively.
185 // Returns 1 if the key is not new and *existing_key / *existing_val
186 // was set to it.
fc_solve_columns_google_hash_insert(fcs_columns_google_hash_handle void_hash,void * key,void ** existing_key)187 extern "C" bool fc_solve_columns_google_hash_insert(
188     fcs_columns_google_hash_handle void_hash, void *key, void **existing_key)
189 {
190     ColumnsGoogleHash *hash = (ColumnsGoogleHash *)void_hash;
191     std::pair<ColumnsGoogleHash::iterator, bool> result =
192         hash->insert((char *)key);
193 
194     // If an insertion took place.
195     if (result.second)
196     {
197         *existing_key = NULL;
198         return false;
199     }
200     else
201     {
202         *existing_key = (*(result.first));
203 
204         return true;
205     }
206 }
207 
fc_solve_columns_google_hash_free(fcs_columns_google_hash_handle void_hash)208 extern "C" void fc_solve_columns_google_hash_free(
209     fcs_columns_google_hash_handle void_hash)
210 {
211     ColumnsGoogleHash *hash = (ColumnsGoogleHash *)void_hash;
212 
213     delete hash;
214 
215     return;
216 }
217 
218 #endif
219