@@ -18,6 +18,8 @@ along with LiveCode. If not see <http://www.gnu.org/licenses/>. */
1818
1919#include " foundation-private.h"
2020
21+ #include " foundation-string-hash.h"
22+
2123// //////////////////////////////////////////////////////////////////////////////
2224
2325MC_DLLEXPORT_DEF MCNameRef kMCEmptyName ;
@@ -114,6 +116,104 @@ static inline void __MCNameSetNext(__MCName* p_name, __MCName* p_next)
114116
115117// //////////////////////////////////////////////////////////////////////////////
116118
119+ static void __MCNameIndexToNativeChars (index_t p_value, char_t r_chars[16 ], uindex_t & r_char_count)
120+ {
121+ static const char kDigits [201 ] =
122+ " 0001020304050607080910111213141516171819"
123+ " 2021222324252627282930313233343536373839"
124+ " 4041424344454647484950515253545556575859"
125+ " 6061626364656667686970717273747576777879"
126+ " 8081828384858687888990919293949596979899" ;
127+
128+ r_char_count = 0 ;
129+
130+ uindex_t t_value;
131+ if (p_value < 0 )
132+ {
133+ r_chars[r_char_count++] = ' -' ;
134+ p_value = -p_value;
135+ }
136+
137+ t_value = p_value;
138+
139+ uint32_t t_length = 1 ;
140+ for (;;) {
141+ if (t_value < 10 ) break ;
142+ if (t_value < 100 ) { t_length += 1 ; break ; }
143+ if (t_value < 1000 ) { t_length += 2 ; break ; }
144+ if (t_value < 10000 ) { t_length += 3 ; break ; }
145+ t_value /= 10000U ;
146+ t_length += 4 ;
147+ }
148+
149+ t_value = p_value;
150+
151+ r_char_count += t_length;
152+
153+ uindex_t t_next =
154+ r_char_count - 1 ;
155+
156+ while (t_value >= 100 )
157+ {
158+ index_t t_offset =
159+ (t_value % 100 ) * 2 ;
160+
161+ t_value /= 100 ;
162+
163+ r_chars[t_next] = kDigits [t_offset + 1 ];
164+ r_chars[t_next - 1 ] = kDigits [t_offset];
165+
166+ t_next -= 2 ;
167+ }
168+
169+ if (t_value < 10 )
170+ {
171+ r_chars[t_next] = ' 0' + t_value;
172+ }
173+ else
174+ {
175+ index_t t_offset =
176+ t_value * 2 ;
177+
178+ r_chars[t_next] = kDigits [t_offset + 1 ];
179+ r_chars[t_next - 1 ] = kDigits [t_offset];
180+ }
181+ }
182+
183+ static hash_t __MCNameIndexHash (const char_t *p_chars, uindex_t p_char_count)
184+ {
185+ MCHashCharsContext t_hash;
186+ switch (p_char_count)
187+ {
188+ case 11 :
189+ t_hash.consume (*p_chars++);
190+ case 10 :
191+ t_hash.consume (*p_chars++);
192+ case 9 :
193+ t_hash.consume (*p_chars++);
194+ case 8 :
195+ t_hash.consume (*p_chars++);
196+ case 7 :
197+ t_hash.consume (*p_chars++);
198+ case 6 :
199+ t_hash.consume (*p_chars++);
200+ case 5 :
201+ t_hash.consume (*p_chars++);
202+ case 4 :
203+ t_hash.consume (*p_chars++);
204+ case 3 :
205+ t_hash.consume (*p_chars++);
206+ case 2 :
207+ t_hash.consume (*p_chars++);
208+ case 1 :
209+ t_hash.consume (*p_chars++);
210+ break ;
211+ }
212+ return t_hash;
213+ }
214+
215+ // //////////////////////////////////////////////////////////////////////////////
216+
117217MC_DLLEXPORT_DEF
118218MCNameRef MCNAME (const char *p_string)
119219{
@@ -245,6 +345,92 @@ bool MCNameCreate(MCStringRef p_string, MCNameRef& r_name)
245345 return t_success;
246346}
247347
348+ MC_DLLEXPORT_DEF
349+ bool MCNameCreateWithIndex (index_t p_index, MCNameRef& r_name)
350+ {
351+ char_t t_chars[11 ];
352+ uindex_t t_char_count;
353+ __MCNameIndexToNativeChars (p_index, t_chars, t_char_count);
354+
355+ // Compute the hash of the characters, up to case.
356+ hash_t t_hash;
357+ t_hash = __MCNameIndexHash (t_chars, t_char_count);
358+
359+ // Reduce the hash to the size we store
360+ t_hash = __MCNameReduceHash (t_hash);
361+
362+ // Calculate the index of the chain in the name table where this might be
363+ // found. The capacity is always a power-of-two, so its just a mask op.
364+ uindex_t t_index;
365+ t_index = t_hash & (s_name_table_capacity - 1 );
366+
367+ // Search for the first representation of the would-be name's equivalence
368+ // class.
369+ __MCName *t_key_name;
370+ t_key_name = s_name_table[t_index];
371+ while (t_key_name != nil)
372+ {
373+ // If the string matches, then we are done - notice we compare the
374+ // full hash first.
375+ if (t_hash == __MCNameGetHash (t_key_name) &&
376+ __MCNameGetKey (t_key_name) == t_key_name &&
377+ MCStringIsEqualToNativeChars (t_key_name -> string, t_chars, t_char_count, kMCStringOptionCompareExact ))
378+ {
379+ t_key_name -> references += 1 ;
380+ r_name = t_key_name;
381+ return true ;
382+ }
383+
384+ // Next name must be the next one.
385+ t_key_name = __MCNameGetNext (t_key_name);
386+ }
387+
388+ // We haven't found an exact match, so we create a new name...
389+ bool t_success;
390+ t_success = true ;
391+
392+ // Allocate a name record.
393+ __MCName *t_name;
394+ if (t_success)
395+ t_success = __MCValueCreate (kMCValueTypeCodeName , t_name);
396+
397+ // Copy the string (as immutable).
398+ if (t_success)
399+ t_success = MCStringCreateWithNativeChars (t_chars, t_char_count, t_name -> string);
400+
401+ // Now add the name to the table and fill in the rest of the fields.
402+ if (t_success)
403+ {
404+ // To keep hashin efficient, we (try to) double the size of the
405+ // table each time occupancy reaches capacity.
406+ if (s_name_table_occupancy == s_name_table_capacity)
407+ {
408+ __MCNameGrowTable ();
409+ t_index = t_hash & (s_name_table_capacity - 1 );
410+ }
411+
412+ // Increase occupancy.
413+ s_name_table_occupancy += 1 ;
414+
415+ __MCNameSetNext (t_name, s_name_table[t_index]);
416+ __MCNameSetKey (t_name, t_name);
417+ s_name_table[t_index] = t_name;
418+
419+ // Record the hash (speeds up searching and such).
420+ __MCNameSetHash (t_name, t_hash);
421+
422+ // Return the new name.
423+ r_name = t_name;
424+ }
425+ else
426+ {
427+ MCValueRelease (t_name -> string);
428+ MCMemoryDelete (t_name);
429+ }
430+
431+ return t_success;
432+ }
433+
248434MC_DLLEXPORT_DEF
249435bool MCNameCreateWithNativeChars (const char_t *p_chars, uindex_t p_count, MCNameRef& r_name)
250436{
@@ -318,6 +504,44 @@ MCNameRef MCNameLookupCaseless(MCStringRef p_string)
318504 return t_key_name;
319505}
320506
507+ MCNameRef MCNameLookupIndex (index_t p_index)
508+ {
509+ char_t t_chars[11 ];
510+ uindex_t t_char_count;
511+ __MCNameIndexToNativeChars (p_index, t_chars, t_char_count);
512+
513+ // Compute the hash of the characters, up to case.
514+ hash_t t_hash;
515+ t_hash = __MCNameIndexHash (t_chars, t_char_count);
516+
517+ // Reduce the hash to the size we store
518+ t_hash = __MCNameReduceHash (t_hash);
519+
520+ // Calculate the index of the chain in the name table where this name might
521+ // be found. The capacity is always a power-of-two, so its just a mask op.
522+ uindex_t t_index;
523+ t_index = t_hash & (s_name_table_capacity - 1 );
524+
525+ // Search for the first representative of the would-be name's equivalence class.
526+ __MCName *t_key_name;
527+ t_key_name = s_name_table[t_index];
528+ while (t_key_name != nil)
529+ {
530+ // If the string matches, then we are done - notice we compare the full
531+ // hash first. As an index as a string consists of folded chars, we can
532+ // use exact comparison.
533+ if (t_hash == __MCNameGetHash (t_key_name) &&
534+ __MCNameGetKey (t_key_name) == t_key_name &&
535+ MCStringIsEqualToNativeChars (t_key_name -> string, t_chars, t_char_count, kMCStringOptionCompareExact ))
536+ break ;
537+
538+ // Next name must be the next one
539+ t_key_name = __MCNameGetNext (t_key_name);
540+ }
541+
542+ return t_key_name;
543+ }
544+
321545MC_DLLEXPORT_DEF
322546uintptr_t MCNameGetCaselessSearchKey (MCNameRef self)
323547{
0 commit comments