cb22561995-10-11Fredrik Hübinette (Hubbe) /*\
06983f1996-09-22Fredrik Hübinette (Hubbe) ||| This file a part of Pike, and is copyright by Fredrik Hubinette ||| Pike is distributed as GPL (General Public License)
cb22561995-10-11Fredrik Hübinette (Hubbe) ||| See the files COPYING and DISCLAIMER for more information. \*/
5267b71995-08-09Fredrik Hübinette (Hubbe) #include "global.h" #include "stralloc.h" #include "macros.h" #include "dynamic_buffer.h" #include "macros.h" #include "memory.h" #include "error.h"
af93211996-10-12Fredrik Hübinette (Hubbe) #define BEGIN_HASH_SIZE 997 #define MAX_AVG_LINK_LENGTH 3 unsigned INT32 htable_size=0; static struct pike_string **base_table=0;
5267b71995-08-09Fredrik Hübinette (Hubbe) static unsigned INT32 full_hash_value;
af93211996-10-12Fredrik Hübinette (Hubbe) unsigned INT32 num_strings=0;
5267b71995-08-09Fredrik Hübinette (Hubbe) 
af93211996-10-12Fredrik Hübinette (Hubbe) /*** Main string hash function ***/
5267b71995-08-09Fredrik Hübinette (Hubbe) static unsigned int StrHash(const char *s,int len) { full_hash_value=hashmem((unsigned char *)s, len, 20);
af93211996-10-12Fredrik Hübinette (Hubbe)  return full_hash_value % htable_size;
5267b71995-08-09Fredrik Hübinette (Hubbe) }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** find a string in the shared string table. ***/
06983f1996-09-22Fredrik Hübinette (Hubbe) static struct pike_string *internal_findstring(const char *s,int len,int h)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *curr,**prev, **base;
5267b71995-08-09Fredrik Hübinette (Hubbe)  for(base = prev = base_table + h;( curr=*prev ); prev=&curr->next) { #ifdef DEBUG if(curr->refs<1) fatal("String with no references.\n"); #endif if (full_hash_value == curr->hval && len==curr->len && !MEMCMP(curr->str, s,len)) /* found it */ { *prev = curr->next; curr->next = *base; *base = curr; return curr; /* pointer to string */ } } return 0; /* not found */ }
ca74dd1996-10-08Fredrik Hübinette (Hubbe) struct pike_string *binary_findstring(const char *foo, INT32 l)
5267b71995-08-09Fredrik Hübinette (Hubbe) { return internal_findstring(foo, l, StrHash(foo,l)); }
ca74dd1996-10-08Fredrik Hübinette (Hubbe) struct pike_string *findstring(const char *foo) { return binary_findstring(foo, strlen(foo)); }
5267b71995-08-09Fredrik Hübinette (Hubbe) /* * find a string that is already shared and move it to the head * of that list in the hastable */
06983f1996-09-22Fredrik Hübinette (Hubbe) static struct pike_string *propagate_shared_string(const struct pike_string *s,int h)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *curr, **prev, **base;
5267b71995-08-09Fredrik Hübinette (Hubbe)  for(base = prev = base_table + h;( curr=*prev ); prev=&curr->next) { if (curr == s) /* found it */ { *prev=curr->next; curr->next=*base; *base=curr; return curr; } #ifdef DEBUG if(curr->refs<1) fatal("String with no references.\n"); #endif } return 0; /* not found */ }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** rehash ***/ static void rehash_string_backwards(struct pike_string *s)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
af93211996-10-12Fredrik Hübinette (Hubbe)  int h; if(!s) return; rehash_string_backwards(s->next); h=s->hval % htable_size; s->next=base_table[h]; base_table[h]=s; }
aef30b1996-10-11Fredrik Hübinette (Hubbe) 
af93211996-10-12Fredrik Hübinette (Hubbe) static void rehash() { int h,old; struct pike_string **old_base;
aef30b1996-10-11Fredrik Hübinette (Hubbe) 
af93211996-10-12Fredrik Hübinette (Hubbe)  old=htable_size; old_base=base_table; htable_size=htable_size*2 +1; base_table=(struct pike_string **)xalloc(sizeof(struct pike_string *)*htable_size); MEMSET((char *)base_table,0,sizeof(struct pike_string *)*htable_size); for(h=0;h<old;h++) rehash_string_backwards(old_base[h]); if(old_base) free((char *)old_base);
5267b71995-08-09Fredrik Hübinette (Hubbe) }
af93211996-10-12Fredrik Hübinette (Hubbe)  /*** Make new strings ***/
5267b71995-08-09Fredrik Hübinette (Hubbe)  /* note that begin_shared_string expects the _exact_ size of the string, * not the maximum size */
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *begin_shared_string(int len)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *t; t=(struct pike_string *)xalloc(len + sizeof(struct pike_string));
ca74dd1996-10-08Fredrik Hübinette (Hubbe)  t->str[len]=0;
5267b71995-08-09Fredrik Hübinette (Hubbe)  t->len=len; return t; }
af93211996-10-12Fredrik Hübinette (Hubbe) static void link_pike_string(struct pike_string *s, int h) { s->refs = 0; s->next = base_table[h]; base_table[h] = s; s->hval=full_hash_value; num_strings++; if(num_strings > MAX_AVG_LINK_LENGTH * htable_size) rehash(); }
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *end_shared_string(struct pike_string *s)
5267b71995-08-09Fredrik Hübinette (Hubbe) { int len,h;
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *s2;
5267b71995-08-09Fredrik Hübinette (Hubbe)  len=s->len; h=StrHash(s->str,len); s2=internal_findstring(s->str,len,h); if(s2) { free((char *)s); s=s2; }else{
af93211996-10-12Fredrik Hübinette (Hubbe)  link_pike_string(s, h);
5267b71995-08-09Fredrik Hübinette (Hubbe)  } s->refs++; return s; }
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string * make_shared_binary_string(const char *str,int len)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *s;
5267b71995-08-09Fredrik Hübinette (Hubbe)  int h=StrHash(str,len); s = internal_findstring(str,len,h); if (!s) { s=begin_shared_string(len); MEMCPY(s->str, str, len);
af93211996-10-12Fredrik Hübinette (Hubbe)  link_pike_string(s, h);
5267b71995-08-09Fredrik Hübinette (Hubbe)  } s->refs++; return s; }
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *make_shared_string(const char *str)
5267b71995-08-09Fredrik Hübinette (Hubbe) { return make_shared_binary_string(str, strlen(str)); }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** Free strings ***/
5267b71995-08-09Fredrik Hübinette (Hubbe) 
0a3d601996-10-09Fredrik Hübinette (Hubbe) void unlink_pike_string(struct pike_string *s)
5267b71995-08-09Fredrik Hübinette (Hubbe) { int h; h=StrHash(s->str,s->len); propagate_shared_string(s,h); base_table[h]=s->next;
760b261996-12-03Fredrik Hübinette (Hubbe)  num_strings--;
0a3d601996-10-09Fredrik Hübinette (Hubbe) } void really_free_string(struct pike_string *s) { unlink_pike_string(s);
5267b71995-08-09Fredrik Hübinette (Hubbe)  free((char *)s); }
af93211996-10-12Fredrik Hübinette (Hubbe) 
5267b71995-08-09Fredrik Hübinette (Hubbe) /*
af93211996-10-12Fredrik Hübinette (Hubbe)  * String table status
5267b71995-08-09Fredrik Hübinette (Hubbe)  */
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *add_string_status(int verbose)
5267b71995-08-09Fredrik Hübinette (Hubbe) { char b[200]; init_buf(); if (verbose) { int allocd_strings=0; int allocd_bytes=0; int num_distinct_strings=0; int bytes_distinct_strings=0; int overhead_bytes=0;
af93211996-10-12Fredrik Hübinette (Hubbe)  unsigned INT32 e;
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *p;
af93211996-10-12Fredrik Hübinette (Hubbe)  for(e=0;e<htable_size;e++)
5267b71995-08-09Fredrik Hübinette (Hubbe)  { for(p=base_table[e];p;p=p->next) { num_distinct_strings++;
f90e541995-08-17Fredrik Hübinette (Hubbe)  bytes_distinct_strings+=MY_ALIGN(p->len);
5267b71995-08-09Fredrik Hübinette (Hubbe)  allocd_strings+=p->refs;
f90e541995-08-17Fredrik Hübinette (Hubbe)  allocd_bytes+=p->refs*MY_ALIGN(p->len+3);
5267b71995-08-09Fredrik Hübinette (Hubbe)  } }
06983f1996-09-22Fredrik Hübinette (Hubbe)  overhead_bytes=(sizeof(struct pike_string)-1)*num_distinct_strings;
5267b71995-08-09Fredrik Hübinette (Hubbe)  my_strcat("\nShared string hash table:\n"); my_strcat("-------------------------\t Strings Bytes\n"); sprintf(b,"Total asked for\t\t\t%8ld %8ld\n", (long)allocd_strings, (long)allocd_bytes); my_strcat(b); sprintf(b,"Strings malloced\t\t%8ld %8ld + %ld overhead\n", (long)num_distinct_strings, (long)bytes_distinct_strings, (long)overhead_bytes); my_strcat(b); sprintf(b,"Space actually required/total string bytes %d%%\n", (bytes_distinct_strings + overhead_bytes)*100 / allocd_bytes); my_strcat(b); } /* sprintf(b,"Searches: %ld Average search length: %6.3f\n", (long)num_str_searches, (double)search_len / num_str_searches); my_strcat(b); */ return free_buf(); }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** DEBUG ***/ #ifdef DEBUG void check_string(struct pike_string *s) { StrHash(s->str, s->len); if(full_hash_value != s->hval) fatal("Hash value changed?\n"); if(debug_findstring(s) !=s) fatal("Shared string not shared.\n"); if(s->str[s->len]) fatal("Shared string is not zero terminated properly.\n"); } void verify_shared_strings_tables() { unsigned INT32 e, h; struct pike_string *s; for(e=0;e<htable_size;e++) { h=0; for(s=base_table[e];s;s=s->next) { h++; if(s->len < 0) fatal("Shared string shorter than zero bytes.\n"); if(s->refs <= 0) fatal("Shared string had too few references.\n"); if(s->str[s->len]) fatal("Shared string didn't end with a zero.\n"); if(StrHash(s->str, s->len) != e) fatal("Shared string hashed to wrong place.\n"); if(s->hval != full_hash_value) fatal("Shared string hashed to other number.\n"); if(h>10000) { struct pike_string *s2; for(s2=s;s2;s2=s2->next) if(s2 == s) fatal("Shared string table is cyclic.\n"); h=0; } } } } struct pike_string *debug_findstring(const struct pike_string *foo) { struct pike_string *tmp; tmp=propagate_shared_string(foo, foo->hval % htable_size); #if 0 if(!tmp) { unsigned INT32 e; struct pike_string *tmp2; fprintf(stderr,"String %p %ld %ld %s\n", foo, (long)foo->hval, (long)foo->len, foo->str); StrHash(foo->str,foo->len); fprintf(stderr,"------ %p %ld\n", base_table[foo->hval %htable_size], (long)full_hash_value); for(tmp2=base_table[foo->hval % htable_size];tmp2;tmp2=tmp2->next) { if(tmp2 == tmp) fprintf(stderr,"!!%p!!->",tmp2); else fprintf(stderr,"%p->",tmp2); } fprintf(stderr,"0\n"); for(e=0;e<htable_size;e++) { for(tmp2=base_table[e];tmp2;tmp2=tmp2->next) { if(tmp2 == tmp) fprintf(stderr,"String found in hashbin %ld (not %ld)\n", (long)e, (long)(foo->hval % htable_size)); } } } #endif return tmp; }
5267b71995-08-09Fredrik Hübinette (Hubbe) void dump_stralloc_strings() {
af93211996-10-12Fredrik Hübinette (Hubbe)  unsigned INT32 e;
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *p;
af93211996-10-12Fredrik Hübinette (Hubbe)  for(e=0;e<htable_size;e++)
5267b71995-08-09Fredrik Hübinette (Hubbe)  for(p=base_table[e];p;p=p->next)
f90e541995-08-17Fredrik Hübinette (Hubbe)  printf("%ld refs \"%s\"\n",(long)p->refs,p->str);
5267b71995-08-09Fredrik Hübinette (Hubbe) }
af93211996-10-12Fredrik Hübinette (Hubbe) #endif /*** String compare functions ***/ /* does not take locale into account */ int low_quick_binary_strcmp(char *a,INT32 alen, char *b,INT32 blen) { int tmp; if(alen > blen) { tmp=MEMCMP(a, b, blen); if(tmp) return tmp; return 1; }else if(alen < blen){ tmp=MEMCMP(a, b, alen); if(tmp) return tmp; return -1; }else{ return MEMCMP(a, b, alen); } } #ifndef HAVE_STRCOLL /* No locale function available */ static int low_binary_strcmp(char *a,INT32 alen, char *b,INT32 blen) { low_quick_binary_strcmp(a,alen,b,blen); } #else /* takes locale into account */ static int low_binary_strcmp(char *a,INT32 alen, char *b,INT32 blen) { INT32 tmp; while(alen>0 && blen>0) { tmp=strcoll(a,b); if(tmp) return (int)tmp; tmp=strlen(a)+1; a+=tmp; b+=tmp; alen-=tmp; blen-=tmp; } if(alen==blen) return 0; if(alen > blen) return 1; return -1; } #endif /* Does not take locale into account */ int my_quick_strcmp(struct pike_string *a,struct pike_string *b) { if(a==b) return 0; return low_quick_binary_strcmp(a->str,a->len,b->str,b->len); } /* Does take locale into account */ int my_strcmp(struct pike_string *a,struct pike_string *b) { if(a==b) return 0; return low_binary_strcmp(a->str,a->len,b->str,b->len); } /*** Add strings ***/
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *add_shared_strings(struct pike_string *a, struct pike_string *b)
5267b71995-08-09Fredrik Hübinette (Hubbe) { INT32 size;
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *ret;
5267b71995-08-09Fredrik Hübinette (Hubbe)  char *buf; size = a->len + b->len; ret=begin_shared_string(size); buf=ret->str; MEMCPY(buf,a->str,a->len); MEMCPY(buf+a->len,b->str,b->len); ret=end_shared_string(ret); return ret; }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** replace function ***/
06983f1996-09-22Fredrik Hübinette (Hubbe) struct pike_string *string_replace(struct pike_string *str, struct pike_string *del, struct pike_string *to)
5267b71995-08-09Fredrik Hübinette (Hubbe) {
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *ret;
5267b71995-08-09Fredrik Hübinette (Hubbe)  INT32 delimeters; char *s,*tmp,*r,*end;
3108991996-06-21Fredrik Hübinette (Hubbe)  struct mem_searcher searcher;
5267b71995-08-09Fredrik Hübinette (Hubbe)  s=str->str; end=s+str->len; delimeters=0;
3108991996-06-21Fredrik Hübinette (Hubbe)  init_memsearch(&searcher, del->str, del->len, str->len * 2); while((s=memory_search(&searcher,s,end-s)))
5267b71995-08-09Fredrik Hübinette (Hubbe)  { delimeters++; s+=del->len; } if(!delimeters) { str->refs++; return str; } ret=begin_shared_string(str->len + (to->len-del->len)*delimeters); s=str->str; r=ret->str;
3108991996-06-21Fredrik Hübinette (Hubbe)  while((tmp=memory_search(&searcher,s,end-s)))
5267b71995-08-09Fredrik Hübinette (Hubbe)  { MEMCPY(r,s,tmp-s); r+=tmp-s; MEMCPY(r,to->str,to->len); r+=to->len; s=tmp+del->len; } MEMCPY(r,s,end-s); return end_shared_string(ret); }
af93211996-10-12Fredrik Hübinette (Hubbe) /*** init/exit memory ***/ void init_shared_string_table() { htable_size=BEGIN_HASH_SIZE; base_table=(struct pike_string **)xalloc(sizeof(struct pike_string *)*htable_size); MEMSET((char *)base_table,0,sizeof(struct pike_string *)*htable_size); }
5267b71995-08-09Fredrik Hübinette (Hubbe) void cleanup_shared_string_table() {
af93211996-10-12Fredrik Hübinette (Hubbe)  unsigned INT32 e;
06983f1996-09-22Fredrik Hübinette (Hubbe)  struct pike_string *s,*next;
af93211996-10-12Fredrik Hübinette (Hubbe)  for(e=0;e<htable_size;e++)
5267b71995-08-09Fredrik Hübinette (Hubbe)  { for(s=base_table[e];s;s=next) { next=s->next; #ifdef REALLY_FREE free((char *)s); #else s->next=0; #endif } base_table[e]=0; }
b906cf1996-11-27Fredrik Hübinette (Hubbe)  free((char *)base_table);
5267b71995-08-09Fredrik Hübinette (Hubbe) }