Feature #10096 » khash.patch
| common.mk | ||
|---|---|---|
| 
     		     {$(VPATH)}node.h {$(VPATH)}method.h {$(VPATH)}ruby_atomic.h \ 
   | 
||
| 
     	             {$(VPATH)}vm_debug.h {$(VPATH)}id.h {$(VPATH)}thread_native.h \ 
   | 
||
| 
     	             $(CCAN_LIST_INCLUDES) 
   | 
||
| 
     RUBY_KHASH_INCLUDES = {$(VPATH)}ruby_khash.h {$(VPATH)}klib/khash.h 
   | 
||
| 
     ### 
   | 
||
| ... | ... | |
| 
     strftime.$(OBJEXT): {$(VPATH)}strftime.c $(RUBY_H_INCLUDES) \ 
   | 
||
| 
       {$(VPATH)}timev.h $(ENCODING_H_INCLUDES) 
   | 
||
| 
     string.$(OBJEXT): {$(VPATH)}string.c $(RUBY_H_INCLUDES) {$(VPATH)}re.h {$(VPATH)}gc.h \ 
   | 
||
| 
       {$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES) 
   | 
||
| 
       {$(VPATH)}regex.h $(ENCODING_H_INCLUDES) {$(VPATH)}internal.h $(PROBES_H_INCLUDES) \ 
   | 
||
| 
       $(RUBY_KHASH_INCLUDES) 
   | 
||
| 
     struct.$(OBJEXT): {$(VPATH)}struct.c $(RUBY_H_INCLUDES) {$(VPATH)}internal.h 
   | 
||
| 
     symbol.$(OBJEXT): {$(VPATH)}symbol.c $(RUBY_H_INCLUDES) $(ENCODING_H_INCLUDES) \ 
   | 
||
| 
       {$(VPATH)}internal.h {$(VPATH)}node.h {$(VPATH)}id.h {$(VPATH)}symbol.h \ 
   | 
||
| klib/khash.h | ||
|---|---|---|
| 
     /* The MIT License 
   | 
||
| 
        Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk> 
   | 
||
| 
        Permission is hereby granted, free of charge, to any person obtaining 
   | 
||
| 
        a copy of this software and associated documentation files (the 
   | 
||
| 
        "Software"), to deal in the Software without restriction, including 
   | 
||
| 
        without limitation the rights to use, copy, modify, merge, publish, 
   | 
||
| 
        distribute, sublicense, and/or sell copies of the Software, and to 
   | 
||
| 
        permit persons to whom the Software is furnished to do so, subject to 
   | 
||
| 
        the following conditions: 
   | 
||
| 
        The above copyright notice and this permission notice shall be 
   | 
||
| 
        included in all copies or substantial portions of the Software. 
   | 
||
| 
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
   | 
||
| 
        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
   | 
||
| 
        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
   | 
||
| 
        NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
   | 
||
| 
        BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
   | 
||
| 
        ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
   | 
||
| 
        CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
   | 
||
| 
        SOFTWARE. 
   | 
||
| 
     */ 
   | 
||
| 
     /* 
   | 
||
| 
       An example: 
   | 
||
| 
     #include "khash.h" 
   | 
||
| 
     KHASH_MAP_INIT_INT(32, char) 
   | 
||
| 
     int main() { 
   | 
||
| 
     	int ret, is_missing; 
   | 
||
| 
     	khiter_t k; 
   | 
||
| 
     	khash_t(32) *h = kh_init(32); 
   | 
||
| 
     	k = kh_put(32, h, 5, &ret); 
   | 
||
| 
     	kh_value(h, k) = 10; 
   | 
||
| 
     	k = kh_get(32, h, 10); 
   | 
||
| 
     	is_missing = (k == kh_end(h)); 
   | 
||
| 
     	k = kh_get(32, h, 5); 
   | 
||
| 
     	kh_del(32, h, k); 
   | 
||
| 
     	for (k = kh_begin(h); k != kh_end(h); ++k) 
   | 
||
| 
     		if (kh_exist(h, k)) kh_value(h, k) = 1; 
   | 
||
| 
     	kh_destroy(32, h); 
   | 
||
| 
     	return 0; 
   | 
||
| 
     } 
   | 
||
| 
     */ 
   | 
||
| 
     /* 
   | 
||
| 
       2013-05-02 (0.2.8): 
   | 
||
| 
     	* Use quadratic probing. When the capacity is power of 2, stepping function 
   | 
||
| 
     	  i*(i+1)/2 guarantees to traverse each bucket. It is better than double 
   | 
||
| 
     	  hashing on cache performance and is more robust than linear probing. 
   | 
||
| 
     	  In theory, double hashing should be more robust than quadratic probing. 
   | 
||
| 
     	  However, my implementation is probably not for large hash tables, because 
   | 
||
| 
     	  the second hash function is closely tied to the first hash function, 
   | 
||
| 
     	  which reduce the effectiveness of double hashing. 
   | 
||
| 
     	Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php 
   | 
||
| 
       2011-12-29 (0.2.7): 
   | 
||
| 
         * Minor code clean up; no actual effect. 
   | 
||
| 
       2011-09-16 (0.2.6): 
   | 
||
| 
     	* The capacity is a power of 2. This seems to dramatically improve the 
   | 
||
| 
     	  speed for simple keys. Thank Zilong Tan for the suggestion. Reference: 
   | 
||
| 
     	   - http://code.google.com/p/ulib/ 
   | 
||
| 
     	   - http://nothings.org/computer/judy/ 
   | 
||
| 
     	* Allow to optionally use linear probing which usually has better 
   | 
||
| 
     	  performance for random input. Double hashing is still the default as it 
   | 
||
| 
     	  is more robust to certain non-random input. 
   | 
||
| 
     	* Added Wang's integer hash function (not used by default). This hash 
   | 
||
| 
     	  function is more robust to certain non-random input. 
   | 
||
| 
       2011-02-14 (0.2.5): 
   | 
||
| 
         * Allow to declare global functions. 
   | 
||
| 
       2009-09-26 (0.2.4): 
   | 
||
| 
         * Improve portability 
   | 
||
| 
       2008-09-19 (0.2.3): 
   | 
||
| 
     	* Corrected the example 
   | 
||
| 
     	* Improved interfaces 
   | 
||
| 
       2008-09-11 (0.2.2): 
   | 
||
| 
     	* Improved speed a little in kh_put() 
   | 
||
| 
       2008-09-10 (0.2.1): 
   | 
||
| 
     	* Added kh_clear() 
   | 
||
| 
     	* Fixed a compiling error 
   | 
||
| 
       2008-09-02 (0.2.0): 
   | 
||
| 
     	* Changed to token concatenation which increases flexibility. 
   | 
||
| 
       2008-08-31 (0.1.2): 
   | 
||
| 
     	* Fixed a bug in kh_get(), which has not been tested previously. 
   | 
||
| 
       2008-08-31 (0.1.1): 
   | 
||
| 
     	* Added destructor 
   | 
||
| 
     */ 
   | 
||
| 
     #ifndef __AC_KHASH_H 
   | 
||
| 
     #define __AC_KHASH_H 
   | 
||
| 
     /*! 
   | 
||
| 
       @header 
   | 
||
| 
       Generic hash table library. 
   | 
||
| 
      */ 
   | 
||
| 
     #define AC_VERSION_KHASH_H "0.2.8" 
   | 
||
| 
     #include <stdlib.h> 
   | 
||
| 
     #include <string.h> 
   | 
||
| 
     #include <limits.h> 
   | 
||
| 
     /* compiler specific configuration */ 
   | 
||
| 
     #if UINT_MAX == 0xffffffffu 
   | 
||
| 
     typedef unsigned int khint32_t; 
   | 
||
| 
     #elif ULONG_MAX == 0xffffffffu 
   | 
||
| 
     typedef unsigned long khint32_t; 
   | 
||
| 
     #endif 
   | 
||
| 
     #if ULONG_MAX == ULLONG_MAX 
   | 
||
| 
     typedef unsigned long khint64_t; 
   | 
||
| 
     #else 
   | 
||
| 
     typedef unsigned long long khint64_t; 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifdef _MSC_VER 
   | 
||
| 
     #define kh_inline __inline 
   | 
||
| 
     #else 
   | 
||
| 
     #define kh_inline inline 
   | 
||
| 
     #endif 
   | 
||
| 
     typedef khint32_t khint_t; 
   | 
||
| 
     typedef khint_t khiter_t; 
   | 
||
| 
     #define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) 
   | 
||
| 
     #define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) 
   | 
||
| 
     #define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) 
   | 
||
| 
     #define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) 
   | 
||
| 
     #define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) 
   | 
||
| 
     #define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) 
   | 
||
| 
     #define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) 
   | 
||
| 
     #define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) 
   | 
||
| 
     #ifndef kroundup32 
   | 
||
| 
     #define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifndef kcalloc 
   | 
||
| 
     #define kcalloc(N,Z) calloc(N,Z) 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifndef kmalloc 
   | 
||
| 
     #define kmalloc(Z) malloc(Z) 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifndef krealloc 
   | 
||
| 
     #define krealloc(P,Z) realloc(P,Z) 
   | 
||
| 
     #endif 
   | 
||
| 
     #ifndef kfree 
   | 
||
| 
     #define kfree(P) free(P) 
   | 
||
| 
     #endif 
   | 
||
| 
     static const double __ac_HASH_UPPER = 0.77; 
   | 
||
| 
     #define __KHASH_TYPE(name, khkey_t, khval_t) \ 
   | 
||
| 
     	typedef struct { \ 
   | 
||
| 
     		khint_t n_buckets, size, n_occupied, upper_bound; \ 
   | 
||
| 
     		khint32_t *flags; \ 
   | 
||
| 
     		khkey_t *keys; \ 
   | 
||
| 
     		khval_t *vals; \ 
   | 
||
| 
     	} kh_##name##_t; 
   | 
||
| 
     #define __KHASH_PROTOTYPES(name, khkey_t, khval_t)						\ 
   | 
||
| 
     	extern kh_##name##_t *kh_init_##name(void);							\ 
   | 
||
| 
     	extern void kh_destroy_##name(kh_##name##_t *h);					\ 
   | 
||
| 
     	extern void kh_clear_##name(kh_##name##_t *h);						\ 
   | 
||
| 
     	extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key);	\ 
   | 
||
| 
     	extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ 
   | 
||
| 
     	extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ 
   | 
||
| 
     	extern void kh_del_##name(kh_##name##_t *h, khint_t x); 
   | 
||
| 
     #define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ 
   | 
||
| 
     	SCOPE kh_##name##_t *kh_init_##name(void) {							\ 
   | 
||
| 
     		return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t));		\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE void kh_destroy_##name(kh_##name##_t *h)						\ 
   | 
||
| 
     	{																	\ 
   | 
||
| 
     		if (h) {														\ 
   | 
||
| 
     			kfree((void *)h->keys); kfree(h->flags);					\ 
   | 
||
| 
     			kfree((void *)h->vals);										\ 
   | 
||
| 
     			kfree(h);													\ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE void kh_clear_##name(kh_##name##_t *h)						\ 
   | 
||
| 
     	{																	\ 
   | 
||
| 
     		if (h && h->flags) {											\ 
   | 
||
| 
     			memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \ 
   | 
||
| 
     			h->size = h->n_occupied = 0;								\ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key)	\ 
   | 
||
| 
     	{																	\ 
   | 
||
| 
     		if (h->n_buckets) {												\ 
   | 
||
| 
     			khint_t k, i, last, mask, step = 0; \ 
   | 
||
| 
     			mask = h->n_buckets - 1;									\ 
   | 
||
| 
     			k = __hash_func(key); i = k & mask;							\ 
   | 
||
| 
     			last = i; \ 
   | 
||
| 
     			while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ 
   | 
||
| 
     				i = (i + (++step)) & mask; \ 
   | 
||
| 
     				if (i == last) return h->n_buckets;						\ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     			return __ac_iseither(h->flags, i)? h->n_buckets : i;		\ 
   | 
||
| 
     		} else return 0;												\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ 
   | 
||
| 
     	{ /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \ 
   | 
||
| 
     		khint32_t *new_flags = 0;										\ 
   | 
||
| 
     		khint_t j = 1;													\ 
   | 
||
| 
     		{																\ 
   | 
||
| 
     			kroundup32(new_n_buckets);									\ 
   | 
||
| 
     			if (new_n_buckets < 4) new_n_buckets = 4;					\ 
   | 
||
| 
     			if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0;	/* requested size is too small */ \ 
   | 
||
| 
     			else { /* hash table size to be changed (shrink or expand); rehash */ \ 
   | 
||
| 
     				new_flags = (khint32_t*)kmalloc(__ac_fsize(new_n_buckets) * sizeof(khint32_t));	\ 
   | 
||
| 
     				if (!new_flags) return -1;								\ 
   | 
||
| 
     				memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \ 
   | 
||
| 
     				if (h->n_buckets < new_n_buckets) {	/* expand */		\ 
   | 
||
| 
     					khkey_t *new_keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ 
   | 
||
| 
     					if (!new_keys) return -1;							\ 
   | 
||
| 
     					h->keys = new_keys;									\ 
   | 
||
| 
     					if (kh_is_map) {									\ 
   | 
||
| 
     						khval_t *new_vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ 
   | 
||
| 
     						if (!new_vals) return -1;						\ 
   | 
||
| 
     						h->vals = new_vals;								\ 
   | 
||
| 
     					}													\ 
   | 
||
| 
     				} /* otherwise shrink */								\ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     		if (j) { /* rehashing is needed */								\ 
   | 
||
| 
     			for (j = 0; j != h->n_buckets; ++j) {						\ 
   | 
||
| 
     				if (__ac_iseither(h->flags, j) == 0) {					\ 
   | 
||
| 
     					khkey_t key = h->keys[j];							\ 
   | 
||
| 
     					khval_t val;										\ 
   | 
||
| 
     					khint_t new_mask;									\ 
   | 
||
| 
     					new_mask = new_n_buckets - 1;						\ 
   | 
||
| 
     					if (kh_is_map) val = h->vals[j];					\ 
   | 
||
| 
     					__ac_set_isdel_true(h->flags, j);					\ 
   | 
||
| 
     					while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \ 
   | 
||
| 
     						khint_t k, i, step = 0; \ 
   | 
||
| 
     						k = __hash_func(key);							\ 
   | 
||
| 
     						i = k & new_mask;								\ 
   | 
||
| 
     						while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \ 
   | 
||
| 
     						__ac_set_isempty_false(new_flags, i);			\ 
   | 
||
| 
     						if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \ 
   | 
||
| 
     							{ khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ 
   | 
||
| 
     							if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ 
   | 
||
| 
     							__ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \ 
   | 
||
| 
     						} else { /* write the element and jump out of the loop */ \ 
   | 
||
| 
     							h->keys[i] = key;							\ 
   | 
||
| 
     							if (kh_is_map) h->vals[i] = val;			\ 
   | 
||
| 
     							break;										\ 
   | 
||
| 
     						}												\ 
   | 
||
| 
     					}													\ 
   | 
||
| 
     				}														\ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     			if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \ 
   | 
||
| 
     				h->keys = (khkey_t*)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); \ 
   | 
||
| 
     				if (kh_is_map) h->vals = (khval_t*)krealloc((void *)h->vals, new_n_buckets * sizeof(khval_t)); \ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     			kfree(h->flags); /* free the working space */				\ 
   | 
||
| 
     			h->flags = new_flags;										\ 
   | 
||
| 
     			h->n_buckets = new_n_buckets;								\ 
   | 
||
| 
     			h->n_occupied = h->size;									\ 
   | 
||
| 
     			h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     		return 0;														\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ 
   | 
||
| 
     	{																	\ 
   | 
||
| 
     		khint_t x;														\ 
   | 
||
| 
     		if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \ 
   | 
||
| 
     			if (h->n_buckets > (h->size<<1)) {							\ 
   | 
||
| 
     				if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \ 
   | 
||
| 
     					*ret = -1; return h->n_buckets;						\ 
   | 
||
| 
     				}														\ 
   | 
||
| 
     			} else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \ 
   | 
||
| 
     				*ret = -1; return h->n_buckets;							\ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     		} /* TODO: to implement automatically shrinking; resize() already support shrinking */ \ 
   | 
||
| 
     		{																\ 
   | 
||
| 
     			khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \ 
   | 
||
| 
     			x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \ 
   | 
||
| 
     			if (__ac_isempty(h->flags, i)) x = i; /* for speed up */	\ 
   | 
||
| 
     			else {														\ 
   | 
||
| 
     				last = i; \ 
   | 
||
| 
     				while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ 
   | 
||
| 
     					if (__ac_isdel(h->flags, i)) site = i;				\ 
   | 
||
| 
     					i = (i + (++step)) & mask; \ 
   | 
||
| 
     					if (i == last) { x = site; break; }					\ 
   | 
||
| 
     				}														\ 
   | 
||
| 
     				if (x == h->n_buckets) {								\ 
   | 
||
| 
     					if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ 
   | 
||
| 
     					else x = i;											\ 
   | 
||
| 
     				}														\ 
   | 
||
| 
     			}															\ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     		if (__ac_isempty(h->flags, x)) { /* not present at all */		\ 
   | 
||
| 
     			h->keys[x] = key;											\ 
   | 
||
| 
     			__ac_set_isboth_false(h->flags, x);							\ 
   | 
||
| 
     			++h->size; ++h->n_occupied;									\ 
   | 
||
| 
     			*ret = 1;													\ 
   | 
||
| 
     		} else if (__ac_isdel(h->flags, x)) { /* deleted */				\ 
   | 
||
| 
     			h->keys[x] = key;											\ 
   | 
||
| 
     			__ac_set_isboth_false(h->flags, x);							\ 
   | 
||
| 
     			++h->size;													\ 
   | 
||
| 
     			*ret = 2;													\ 
   | 
||
| 
     		} else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \ 
   | 
||
| 
     		return x;														\ 
   | 
||
| 
     	}																	\ 
   | 
||
| 
     	SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x)				\ 
   | 
||
| 
     	{																	\ 
   | 
||
| 
     		if (x != h->n_buckets && !__ac_iseither(h->flags, x)) {			\ 
   | 
||
| 
     			__ac_set_isdel_true(h->flags, x);							\ 
   | 
||
| 
     			--h->size;													\ 
   | 
||
| 
     		}																\ 
   | 
||
| 
     	} 
   | 
||
| 
     #define KHASH_DECLARE(name, khkey_t, khval_t)							\ 
   | 
||
| 
     	__KHASH_TYPE(name, khkey_t, khval_t)								\ 
   | 
||
| 
     	__KHASH_PROTOTYPES(name, khkey_t, khval_t) 
   | 
||
| 
     #define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ 
   | 
||
| 
     	__KHASH_TYPE(name, khkey_t, khval_t)								\ 
   | 
||
| 
     	__KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) 
   | 
||
| 
     #define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ 
   | 
||
| 
     	KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) 
   | 
||
| 
     /* --- BEGIN OF HASH FUNCTIONS --- */ 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Integer hash function 
   | 
||
| 
       @param  key   The integer [khint32_t] 
   | 
||
| 
       @return       The hash value [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_int_hash_func(key) (khint32_t)(key) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Integer comparison function 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_int_hash_equal(a, b) ((a) == (b)) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     64-bit integer hash function 
   | 
||
| 
       @param  key   The integer [khint64_t] 
   | 
||
| 
       @return       The hash value [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     64-bit integer comparison function 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_int64_hash_equal(a, b) ((a) == (b)) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     const char* hash function 
   | 
||
| 
       @param  s     Pointer to a null terminated string 
   | 
||
| 
       @return       The hash value 
   | 
||
| 
      */ 
   | 
||
| 
     static kh_inline khint_t __ac_X31_hash_string(const char *s) 
   | 
||
| 
     { 
   | 
||
| 
     	khint_t h = (khint_t)*s; 
   | 
||
| 
     	if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s; 
   | 
||
| 
     	return h; 
   | 
||
| 
     } 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Another interface to const char* hash function 
   | 
||
| 
       @param  key   Pointer to a null terminated string [const char*] 
   | 
||
| 
       @return       The hash value [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_str_hash_func(key) __ac_X31_hash_string(key) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Const char* comparison function 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) 
   | 
||
| 
     static kh_inline khint_t __ac_Wang_hash(khint_t key) 
   | 
||
| 
     { 
   | 
||
| 
         key += ~(key << 15); 
   | 
||
| 
         key ^=  (key >> 10); 
   | 
||
| 
         key +=  (key << 3); 
   | 
||
| 
         key ^=  (key >> 6); 
   | 
||
| 
         key += ~(key << 11); 
   | 
||
| 
         key ^=  (key >> 16); 
   | 
||
| 
         return key; 
   | 
||
| 
     } 
   | 
||
| 
     #define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key) 
   | 
||
| 
     /* --- END OF HASH FUNCTIONS --- */ 
   | 
||
| 
     /* Other convenient macros... */ 
   | 
||
| 
     /*! 
   | 
||
| 
       @abstract Type of the hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
      */ 
   | 
||
| 
     #define khash_t(name) kh_##name##_t 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Initiate a hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @return       Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_init(name) kh_init_##name() 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Destroy a hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_destroy(name, h) kh_destroy_##name(h) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Reset a hash table without deallocating memory. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_clear(name, h) kh_clear_##name(h) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Resize a hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  s     New size [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_resize(name, h, s) kh_resize_##name(h, s) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Insert a key to the hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  k     Key [type of keys] 
   | 
||
| 
       @param  r     Extra return code: -1 if the operation failed; 
   | 
||
| 
                     0 if the key is present in the hash table; 
   | 
||
| 
                     1 if the bucket is empty (never used); 2 if the element in 
   | 
||
| 
     				the bucket has been deleted [int*] 
   | 
||
| 
       @return       Iterator to the inserted element [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_put(name, h, k, r) kh_put_##name(h, k, r) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Retrieve a key from the hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  k     Key [type of keys] 
   | 
||
| 
       @return       Iterator to the found element, or kh_end(h) if the element is absent [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_get(name, h, k) kh_get_##name(h, k) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Remove a key from the hash table. 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  k     Iterator to the element to be deleted [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_del(name, h, k) kh_del_##name(h, k) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Test whether a bucket contains data. 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  x     Iterator to the bucket [khint_t] 
   | 
||
| 
       @return       1 if containing data; 0 otherwise [int] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get key given an iterator 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  x     Iterator to the bucket [khint_t] 
   | 
||
| 
       @return       Key [type of keys] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_key(h, x) ((h)->keys[x]) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get value given an iterator 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  x     Iterator to the bucket [khint_t] 
   | 
||
| 
       @return       Value [type of values] 
   | 
||
| 
       @discussion   For hash sets, calling this results in segfault. 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_val(h, x) ((h)->vals[x]) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Alias of kh_val() 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_value(h, x) ((h)->vals[x]) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get the start iterator 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @return       The start iterator [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_begin(h) (khint_t)(0) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get the end iterator 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @return       The end iterator [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_end(h) ((h)->n_buckets) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get the number of elements in the hash table 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @return       Number of elements in the hash table [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_size(h) ((h)->size) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Get the number of buckets in the hash table 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @return       Number of buckets in the hash table [khint_t] 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_n_buckets(h) ((h)->n_buckets) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Iterate over the entries in the hash table 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  kvar  Variable to which key will be assigned 
   | 
||
| 
       @param  vvar  Variable to which value will be assigned 
   | 
||
| 
       @param  code  Block of code to execute 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_foreach(h, kvar, vvar, code) { khint_t __i;		\ 
   | 
||
| 
     	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\ 
   | 
||
| 
     		if (!kh_exist(h,__i)) continue;						\ 
   | 
||
| 
     		(kvar) = kh_key(h,__i);								\ 
   | 
||
| 
     		(vvar) = kh_val(h,__i);								\ 
   | 
||
| 
     		code;												\ 
   | 
||
| 
     	} } 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Iterate over the values in the hash table 
   | 
||
| 
       @param  h     Pointer to the hash table [khash_t(name)*] 
   | 
||
| 
       @param  vvar  Variable to which value will be assigned 
   | 
||
| 
       @param  code  Block of code to execute 
   | 
||
| 
      */ 
   | 
||
| 
     #define kh_foreach_value(h, vvar, code) { khint_t __i;		\ 
   | 
||
| 
     	for (__i = kh_begin(h); __i != kh_end(h); ++__i) {		\ 
   | 
||
| 
     		if (!kh_exist(h,__i)) continue;						\ 
   | 
||
| 
     		(vvar) = kh_val(h,__i);								\ 
   | 
||
| 
     		code;												\ 
   | 
||
| 
     	} } 
   | 
||
| 
     /* More conenient interfaces */ 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash set containing integer keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_SET_INIT_INT(name)										\ 
   | 
||
| 
     	KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash map containing integer keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  khval_t  Type of values [type] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_MAP_INIT_INT(name, khval_t)								\ 
   | 
||
| 
     	KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash map containing 64-bit integer keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_SET_INIT_INT64(name)										\ 
   | 
||
| 
     	KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash map containing 64-bit integer keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  khval_t  Type of values [type] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_MAP_INIT_INT64(name, khval_t)								\ 
   | 
||
| 
     	KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) 
   | 
||
| 
     typedef const char *kh_cstr_t; 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash map containing const char* keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_SET_INIT_STR(name)										\ 
   | 
||
| 
     	KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) 
   | 
||
| 
     /*! @function 
   | 
||
| 
       @abstract     Instantiate a hash map containing const char* keys 
   | 
||
| 
       @param  name  Name of the hash table [symbol] 
   | 
||
| 
       @param  khval_t  Type of values [type] 
   | 
||
| 
      */ 
   | 
||
| 
     #define KHASH_MAP_INIT_STR(name, khval_t)								\ 
   | 
||
| 
     	KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) 
   | 
||
| 
     #endif /* __AC_KHASH_H */ 
   | 
||
| ruby_khash.h | ||
|---|---|---|
| 
     #ifndef RUBY_KHASH_H 
   | 
||
| 
     #define RUBY_KHASH_H 
   | 
||
| 
     #include "ruby/ruby.h" 
   | 
||
| 
     #include "id.h" 
   | 
||
| 
     /* 
   | 
||
| 
      * Avoid modifying klib/khash directly since that is imported from 
   | 
||
| 
      * https://github.com/attractivechaos/klib.git 
   | 
||
| 
      */ 
   | 
||
| 
     /* override klib defaults to do GC accounting */ 
   | 
||
| 
     #define kcalloc(N,Z) ruby_xcalloc((N),(Z)) 
   | 
||
| 
     #define kmalloc(N) ruby_xmalloc((N)) 
   | 
||
| 
     #define krealloc(P,Z) ruby_xrealloc((P),(Z)) 
   | 
||
| 
     #define kfree(P) ruby_xfree((P)) 
   | 
||
| 
     #include "klib/khash.h" 
   | 
||
| 
     static inline khint_t 
   | 
||
| 
     rb_khash_str(VALUE str) 
   | 
||
| 
     { 
   | 
||
| 
         st_index_t h = rb_str_hash(str); 
   | 
||
| 
         if (sizeof(st_index_t) == (sizeof(khint_t) * 2)) { 
   | 
||
| 
     	union { st_index_t in; khint_t out[2]; } tmp; 
   | 
||
| 
     	tmp.in = h; 
   | 
||
| 
     	return tmp.out[0] ^ tmp.out[1]; 
   | 
||
| 
         } 
   | 
||
| 
         return (khint_t)h; 
   | 
||
| 
     } 
   | 
||
| 
     static inline khint_t 
   | 
||
| 
     rb_khash_id(ID id) 
   | 
||
| 
     { 
   | 
||
| 
         return (khint_t)(id >> RUBY_ID_SCOPE_SHIFT); 
   | 
||
| 
     } 
   | 
||
| 
     /* 
   | 
||
| 
      * TODO: send this to khash upstream if it works well 
   | 
||
| 
      * This is based on kh_foreach_value, but for keys. 
   | 
||
| 
      */ 
   | 
||
| 
     #define rb_kh_foreach_key(h, kvar, code) do { khint_t __i; \ 
   | 
||
| 
         for (__i = kh_begin(h); __i != kh_end(h); ++__i) {	\ 
   | 
||
| 
     	if (!kh_exist(h,__i)) continue;	\ 
   | 
||
| 
     	(kvar) = kh_key(h,__i); \ 
   | 
||
| 
     	code; \ 
   | 
||
| 
         } } while (0) 
   | 
||
| 
     #endif /* RUBY_KHASH_H */ 
   | 
||
| string.c | ||
|---|---|---|
| 
     #include "probes.h" 
   | 
||
| 
     #include "gc.h" 
   | 
||
| 
     #include <assert.h> 
   | 
||
| 
     #include "ruby_khash.h" 
   | 
||
| 
     #define BEG(no) (regs->beg[(no)]) 
   | 
||
| 
     #define END(no) (regs->end[(no)]) 
   | 
||
| ... | ... | |
| 
         } 
   | 
||
| 
     } 
   | 
||
| 
     static int fstring_cmp(VALUE a, VALUE b); 
   | 
||
| 
     static st_table* frozen_strings; 
   | 
||
| 
     static const struct st_hash_type fstring_hash_type = { 
   | 
||
| 
         fstring_cmp, 
   | 
||
| 
         rb_str_hash, 
   | 
||
| 
     }; 
   | 
||
| 
     static int fstring_eq(VALUE a, VALUE b); 
   | 
||
| 
     KHASH_INIT(fstring, VALUE, char, 0, rb_khash_str, fstring_eq); 
   | 
||
| 
     static khash_t(fstring) *frozen_strings; /* TODO: make mvm friendly */ 
   | 
||
| 
     static int 
   | 
||
| 
     fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing) 
   | 
||
| 
     fstring_eq(VALUE a, VALUE b) 
   | 
||
| 
     { 
   | 
||
| 
         int cmp = rb_str_hash_cmp(a, b); 
   | 
||
| 
         if (cmp == 0) { 
   | 
||
| 
     	return (ENCODING_GET(b) - ENCODING_GET(a)) == 0; 
   | 
||
| 
         } 
   | 
||
| 
         return 0; 
   | 
||
| 
     } 
   | 
||
| 
     VALUE 
   | 
||
| 
     rb_fstring(VALUE str) 
   | 
||
| 
     { 
   | 
||
| 
         VALUE *fstr = (VALUE *)arg; 
   | 
||
| 
         VALUE str = (VALUE)*key; 
   | 
||
| 
         VALUE fstr; 
   | 
||
| 
         khint_t k; 
   | 
||
| 
         int ret; 
   | 
||
| 
         Check_Type(str, T_STRING); 
   | 
||
| 
         if (!frozen_strings) 
   | 
||
| 
     	frozen_strings = kh_init(fstring); 
   | 
||
| 
         if (FL_TEST(str, RSTRING_FSTR)) 
   | 
||
| 
     	return str; 
   | 
||
| 
         k = kh_get(fstring, frozen_strings, str); 
   | 
||
| 
         if (k != kh_end(frozen_strings)) { /* existing */ 
   | 
||
| 
     	fstr = kh_key(frozen_strings, k); 
   | 
||
| 
         if (existing) { 
   | 
||
| 
     	/* because of lazy sweep, str may be unmarked already and swept 
   | 
||
| 
     	 * at next time */ 
   | 
||
| 
     	if (rb_objspace_garbage_object_p(str)) { 
   | 
||
| 
     	    str = *fstr; 
   | 
||
| 
     	if (rb_objspace_garbage_object_p(fstr)) { 
   | 
||
| 
     	    goto create_new_fstr; 
   | 
||
| 
     	} 
   | 
||
| 
     	*fstr = str; 
   | 
||
| 
     	return ST_STOP; 
   | 
||
| 
         } 
   | 
||
| 
         else { 
   | 
||
| 
     	if (STR_SHARED_P(str)) { /* str should not be shared */ 
   | 
||
| 
     	  create_new_fstr: 
   | 
||
| 
     	    str = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), STR_ENC_GET(str)); 
   | 
||
| 
     	    OBJ_FREEZE(str); 
   | 
||
| 
     	    fstr = rb_enc_str_new(RSTRING_PTR(str), RSTRING_LEN(str), STR_ENC_GET(str)); 
   | 
||
| 
     	    OBJ_FREEZE(fstr); 
   | 
||
| 
     	} 
   | 
||
| 
     	else { 
   | 
||
| 
     	    str = rb_str_new_frozen(str); 
   | 
||
| 
     	    fstr = rb_str_new_frozen(str); 
   | 
||
| 
     	} 
   | 
||
| 
     	RBASIC(str)->flags |= RSTRING_FSTR; 
   | 
||
| 
     	*key = *value = *fstr = str; 
   | 
||
| 
     	return ST_CONTINUE; 
   | 
||
| 
     	RBASIC(fstr)->flags |= RSTRING_FSTR; 
   | 
||
| 
     	kh_put(fstring, frozen_strings, fstr, &ret); 
   | 
||
| 
     	if (ret < 0) rb_memerror(); 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| 
     VALUE 
   | 
||
| 
     rb_fstring(VALUE str) 
   | 
||
| 
     { 
   | 
||
| 
         Check_Type(str, T_STRING); 
   | 
||
| 
         if (!frozen_strings) 
   | 
||
| 
     	frozen_strings = st_init_table(&fstring_hash_type); 
   | 
||
| 
         if (FL_TEST(str, RSTRING_FSTR)) 
   | 
||
| 
     	return str; 
   | 
||
| 
         st_update(frozen_strings, (st_data_t)str, fstr_update_callback, (st_data_t)&str); 
   | 
||
| 
         return str; 
   | 
||
| 
         return fstr; 
   | 
||
| 
     } 
   | 
||
| 
     static VALUE 
   | 
||
| ... | ... | |
| 
         return rb_fstring(setup_fake_str(&fake_str, ptr, len, ENCINDEX_US_ASCII)); 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| 
     fstring_set_class_i(st_data_t key, st_data_t val, st_data_t arg) 
   | 
||
| 
     { 
   | 
||
| 
         RBASIC_SET_CLASS((VALUE)key, (VALUE)arg); 
   | 
||
| 
         return ST_CONTINUE; 
   | 
||
| 
     } 
   | 
||
| 
     static int 
   | 
||
| 
     fstring_cmp(VALUE a, VALUE b) 
   | 
||
| 
     { 
   | 
||
| 
         int cmp = rb_str_hash_cmp(a, b); 
   | 
||
| 
         if (cmp != 0) { 
   | 
||
| 
     	return cmp; 
   | 
||
| 
         } 
   | 
||
| 
         return ENCODING_GET(b) - ENCODING_GET(a); 
   | 
||
| 
     } 
   | 
||
| 
     static inline int 
   | 
||
| 
     single_byte_optimizable(VALUE str) 
   | 
||
| 
     { 
   | 
||
| ... | ... | |
| 
     rb_str_free(VALUE str) 
   | 
||
| 
     { 
   | 
||
| 
         if (FL_TEST(str, RSTRING_FSTR)) { 
   | 
||
| 
     	st_data_t fstr = (st_data_t)str; 
   | 
||
| 
     	st_delete(frozen_strings, &fstr, NULL); 
   | 
||
| 
     	khint_t k = kh_get(fstring, frozen_strings, str); 
   | 
||
| 
     	if (k != kh_end(frozen_strings)) { 
   | 
||
| 
     	    kh_del(fstring, frozen_strings, k); 
   | 
||
| 
     	} 
   | 
||
| 
         } 
   | 
||
| 
         if (!STR_EMBED_P(str) && !FL_TEST(str, STR_SHARED)) { 
   | 
||
| ... | ... | |
| 
         rb_define_method(rb_cSymbol, "encoding", sym_encoding, 0); 
   | 
||
| 
         if (frozen_strings) 
   | 
||
| 
     	st_foreach(frozen_strings, fstring_set_class_i, rb_cString); 
   | 
||
| 
         if (frozen_strings) { 
   | 
||
| 
     	VALUE str; 
   | 
||
| 
     	rb_kh_foreach_key(frozen_strings, str, do { 
   | 
||
| 
     	    RBASIC_SET_CLASS(str, rb_cString); 
   | 
||
| 
     	} while (0)); 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| symbol.c | ||
|---|---|---|
| 
     #include "symbol.h" 
   | 
||
| 
     #include "gc.h" 
   | 
||
| 
     #include "probes.h" 
   | 
||
| 
     #include "ruby_khash.h" 
   | 
||
| 
     static ID register_static_symid(ID, const char *, long, rb_encoding *); 
   | 
||
| 
     static ID register_static_symid_str(ID, VALUE); 
   | 
||
| ... | ... | |
| 
     STATIC_ASSERT(op_tbl_name_size, sizeof(op_tbl[0].name) == 3); 
   | 
||
| 
     #define op_tbl_len(i) (!op_tbl[i].name[1] ? 1 : !op_tbl[i].name[2] ? 2 : 3) 
   | 
||
| 
     static int 
   | 
||
| 
     id_str_eq(ID a, ID b) 
   | 
||
| 
     { 
   | 
||
| 
         return a == b; 
   | 
||
| 
     } 
   | 
||
| 
     KHASH_INIT(id_str, ID, VALUE, 1, rb_khash_id, id_str_eq); 
   | 
||
| 
     static struct symbols { 
   | 
||
| 
         ID last_id; 
   | 
||
| 
         st_table *str_id; 
   | 
||
| 
         st_table *id_str; 
   | 
||
| 
         st_table *str_id; /* ordered for now in case of compatibility */ 
   | 
||
| 
         khash_t(id_str) *id_str; 
   | 
||
| 
         VALUE dsymbol_fstr_hash; 
   | 
||
| 
     } global_symbols = {tNEXT_ID-1}; 
   | 
||
| ... | ... | |
| 
         rb_str_hash, 
   | 
||
| 
     }; 
   | 
||
| 
     static int 
   | 
||
| 
     lookup_static_id_str(ID id, VALUE *str) 
   | 
||
| 
     { 
   | 
||
| 
         khint_t k = kh_get(id_str, global_symbols.id_str, id); 
   | 
||
| 
         if (k != kh_end(global_symbols.id_str)) { 
   | 
||
| 
     	*str = kh_val(global_symbols.id_str, k); 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } 
   | 
||
| 
         return FALSE; 
   | 
||
| 
     } 
   | 
||
| 
     void 
   | 
||
| 
     Init_sym(void) 
   | 
||
| 
     { 
   | 
||
| ... | ... | |
| 
         rb_gc_register_mark_object(dsym_fstrs); 
   | 
||
| 
         rb_obj_hide(dsym_fstrs); 
   | 
||
| 
         /* load factor in a chained hash table is often > 1 */ 
   | 
||
| 
         global_symbols.str_id = st_init_table_with_size(&symhash, 1000); 
   | 
||
| 
         global_symbols.id_str = st_init_numtable_with_size(1000); 
   | 
||
| 
         /* load factor in an open-addressed hash table is always < 1 */ 
   | 
||
| 
         global_symbols.id_str = kh_init(id_str); 
   | 
||
| 
         kh_resize(id_str, global_symbols.id_str, 4096); 
   | 
||
| 
         Init_id(); 
   | 
||
| 
     } 
   | 
||
| 
     static ID attrsetname_to_attr(VALUE name); 
   | 
||
| 
     static int lookup_id_str(ID id, st_data_t *data); 
   | 
||
| 
     static int lookup_id_str(ID id, VALUE *str); 
   | 
||
| 
     ID 
   | 
||
| 
     rb_id_attrset(ID id) 
   | 
||
| ... | ... | |
| 
     	    return id; 
   | 
||
| 
     	  default: 
   | 
||
| 
     	    { 
   | 
||
| 
     		st_data_t data; 
   | 
||
| 
     		if (lookup_id_str(id, &data)) { 
   | 
||
| 
     		VALUE str; 
   | 
||
| 
     		if (lookup_id_str(id, &str)) { 
   | 
||
| 
     		    rb_name_error(id, "cannot make unknown type ID %d:%"PRIsVALUE" attrset", 
   | 
||
| 
     				  scope, (VALUE)data); 
   | 
||
| 
     				  scope, str); 
   | 
||
| 
     		} 
   | 
||
| 
     		else { 
   | 
||
| 
     		    rb_name_error_str(Qnil, "cannot make unknown type anonymous ID %d:%"PRIxVALUE" attrset", 
   | 
||
| ... | ... | |
| 
         return register_static_symid_str(id, str); 
   | 
||
| 
     } 
   | 
||
| 
     static void 
   | 
||
| 
     register_direct(ID id, VALUE str) 
   | 
||
| 
     { 
   | 
||
| 
         int ret; 
   | 
||
| 
         khint_t k; 
   | 
||
| 
         st_add_direct(global_symbols.str_id, (st_data_t)str, id); 
   | 
||
| 
         k = kh_put(id_str, global_symbols.id_str, id, &ret); 
   | 
||
| 
         if (ret == 0) { 
   | 
||
| 
     	rb_bug("duplicate element (id_str): %s", RSTRING_PTR(str)); 
   | 
||
| 
         } else if (ret < 0) { 
   | 
||
| 
     	rb_memerror(); 
   | 
||
| 
         } else { 
   | 
||
| 
     	kh_val(global_symbols.id_str, k) = str; 
   | 
||
| 
         } 
   | 
||
| 
     } 
   | 
||
| 
     static ID 
   | 
||
| 
     register_static_symid_str(ID id, VALUE str) 
   | 
||
| 
     { 
   | 
||
| ... | ... | |
| 
     	RUBY_DTRACE_SYMBOL_CREATE(RSTRING_PTR(str), rb_sourcefile(), rb_sourceline()); 
   | 
||
| 
         } 
   | 
||
| 
         st_add_direct(global_symbols.str_id, (st_data_t)str, id); 
   | 
||
| 
         st_add_direct(global_symbols.id_str, id, (st_data_t)str); 
   | 
||
| 
         register_direct(id, str); 
   | 
||
| 
         rb_gc_register_mark_object(str); 
   | 
||
| 
         return id; 
   | 
||
| ... | ... | |
| 
     { 
   | 
||
| 
         if (UNLIKELY(!DYNAMIC_SYM_P(x))) { 
   | 
||
| 
     	if (STATIC_SYM_P(x)) { 
   | 
||
| 
     	    st_data_t str; 
   | 
||
| 
     	    VALUE str; 
   | 
||
| 
     	    if (lookup_id_str(RSHIFT((unsigned long)(x),RUBY_SPECIAL_SHIFT), &str)) { 
   | 
||
| 
     		rb_bug("wrong argument: %s (inappropriate Symbol)", RSTRING_PTR((VALUE)str)); 
   | 
||
| 
     		rb_bug("wrong argument: %s (inappropriate Symbol)", RSTRING_PTR(str)); 
   | 
||
| 
     	    } 
   | 
||
| 
     	    else { 
   | 
||
| 
     		rb_bug("wrong argument: inappropriate Symbol (%p)", (void *)x); 
   | 
||
| ... | ... | |
| 
         RB_OBJ_WRITE(dsym, &RSYMBOL(dsym)->fstr, str); 
   | 
||
| 
         RSYMBOL(dsym)->type = type; 
   | 
||
| 
         st_add_direct(global_symbols.str_id, (st_data_t)str, (st_data_t)dsym); 
   | 
||
| 
         st_add_direct(global_symbols.id_str, (ID)dsym, (st_data_t)str); 
   | 
||
| 
         register_direct((ID)dsym, str); 
   | 
||
| 
         rb_hash_aset(global_symbols.dsymbol_fstr_hash, str, Qtrue); 
   | 
||
| 
         if (RUBY_DTRACE_SYMBOL_CREATE_ENABLED()) { 
   | 
||
| ... | ... | |
| 
         return dsym; 
   | 
||
| 
     } 
   | 
||
| 
     static inline int 
   | 
||
| 
     id_str_delete(ID id) 
   | 
||
| 
     { 
   | 
||
| 
         khint_t k = kh_get(id_str, global_symbols.id_str, id); 
   | 
||
| 
         if (k != kh_end(global_symbols.id_str)) { 
   | 
||
| 
     	kh_del(id_str, global_symbols.id_str, k); 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } 
   | 
||
| 
         return FALSE; 
   | 
||
| 
     } 
   | 
||
| 
     static inline VALUE 
   | 
||
| 
     dsymbol_check(const VALUE sym) 
   | 
||
| 
     { 
   | 
||
| ... | ... | |
| 
     	if (st_delete(global_symbols.str_id, (st_data_t *)&fstr, NULL) == 0) { 
   | 
||
| 
     	    rb_bug("can't remove fstr from str_id (%s)", RSTRING_PTR(fstr)); 
   | 
||
| 
     	} 
   | 
||
| 
     	if (st_delete(global_symbols.id_str, (st_data_t *)&sym, NULL) == 0) { 
   | 
||
| 
     	if (!id_str_delete(sym)) { 
   | 
||
| 
     	    rb_bug("can't remove sym from id_sym (%s)", RSTRING_PTR(fstr)); 
   | 
||
| 
     	} 
   | 
||
| 
     	return dsymbol_alloc(rb_cSymbol, fstr, rb_enc_get(fstr)); 
   | 
||
| ... | ... | |
| 
     } 
   | 
||
| 
     static int 
   | 
||
| 
     lookup_id_str(ID id, st_data_t *data) 
   | 
||
| 
     lookup_id_str(ID id, VALUE *str) 
   | 
||
| 
     { 
   | 
||
| 
         if (ID_DYNAMIC_SYM_P(id)) { 
   | 
||
| 
     	*data = RSYMBOL(id)->fstr; 
   | 
||
| 
     	*str = RSYMBOL(id)->fstr; 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } 
   | 
||
| 
         if (st_lookup(global_symbols.id_str, id, data)) { 
   | 
||
| 
     	return TRUE; 
   | 
||
| 
         } 
   | 
||
| 
         return FALSE; 
   | 
||
| 
         return lookup_static_id_str(id, str); 
   | 
||
| 
     } 
   | 
||
| 
     ID 
   | 
||
| ... | ... | |
| 
     rb_gc_free_dsymbol(VALUE sym) 
   | 
||
| 
     { 
   | 
||
| 
         st_data_t str_data = (st_data_t)RSYMBOL(sym)->fstr; 
   | 
||
| 
         st_data_t sym_data = (st_data_t)sym; 
   | 
||
| 
         if (str_data) { 
   | 
||
| 
     	if (st_delete(global_symbols.str_id, &str_data, 0) == 0) { 
   | 
||
| 
     	    rb_bug("rb_gc_free_dsymbol: %p can't remove str from str_id (%s)", (void *)sym, RSTRING_PTR(RSYMBOL(sym)->fstr)); 
   | 
||
| 
     	} 
   | 
||
| 
     	if (st_delete(global_symbols.id_str, &sym_data, 0) == 0) { 
   | 
||
| 
     	if (!id_str_delete((ID)sym)) { 
   | 
||
| 
     	    rb_bug("rb_gc_free_dsymbol: %p can't remove sym from id_str (%s)", (void *)sym, RSTRING_PTR(RSYMBOL(sym)->fstr)); 
   | 
||
| 
     	} 
   | 
||
| 
     	RSYMBOL(sym)->fstr = (VALUE)NULL; 
   | 
||
| ... | ... | |
| 
         VALUE sym = ID2SYM((ID)value); 
   | 
||
| 
         if (DYNAMIC_SYM_P(sym) && !SYMBOL_PINNED_P(sym) && rb_objspace_garbage_object_p(sym)) { 
   | 
||
| 
     	st_data_t sym_data = (st_data_t)sym; 
   | 
||
| 
     	st_delete(global_symbols.id_str, &sym_data, NULL); 
   | 
||
| 
     	id_str_delete((ID)sym); 
   | 
||
| 
     	RSYMBOL(sym)->fstr = 0; 
   | 
||
| 
     	return ST_DELETE; 
   | 
||
| 
         } 
   | 
||