Branch: Tag:

2015-08-18

2015-08-18 16:55:08 by Per Hedbor <ph@opera.com>

Merge branch 'arne/string_alloc' into 8.1

Conflicts:
src/backend.cmod
src/builtin.cmod
src/builtin_functions.c
src/stralloc.c

58:   #define low_do_hash(STR,LEN,SHIFT) low_hashmem( (STR), (LEN)<<(SHIFT), hash_prefix_len<<(SHIFT), hashkey )   #define do_hash(STR) low_do_hash(STR->str,STR->len,STR->size_shift)    + static INLINE int __attribute((unused)) string_is_block_allocated(const struct pike_string * s) { +  return (s->flags & STRING_ALLOC_MASK) == STRING_ALLOC_BA; + } +  + static INLINE int __attribute((unused)) string_is_malloced(const struct pike_string * s) { +  return (s->flags & STRING_ALLOC_MASK) == STRING_ALLOC_MALLOC; + } +  + static INLINE int __attribute((unused)) string_is_static(const struct pike_string * s) { +  return (s->flags & STRING_ALLOC_MASK) == STRING_ALLOC_STATIC; + } +  + static INLINE int __attribute((unused)) string_may_modify(const struct pike_string * s) { +  return !string_is_static(s) && s->refs == 1; + } +  +    /* Returns true if str could contain n. */   PMOD_EXPORT int string_range_contains( struct pike_string *str, int n )   {
263:   }      #ifdef PIKE_DEBUG - PMOD_EXPORT struct pike_string *debug_check_size_shift(const struct pike_string *a, -  int shift) + PMOD_EXPORT struct pike_string *debug_check_size_shift(struct pike_string *a, int shift)   {    if(a->size_shift != shift)    Pike_fatal("Wrong STRX macro used!\n");
622:      /* Allocation of strings */    - /* Without the str at the end, to get the size of the header. */ - struct pike_string_hdr { -  PIKE_STRING_CONTENTS; - }; + #define STRING_BLOCK 2048    - /* Allocate some fixed string sizes with BLOCK_ALLOC. */ + static struct block_allocator string_allocator = BA_INIT(sizeof(struct pike_string), STRING_BLOCK);    - #define SHORT_STRING_BLOCK 256 + static void free_unlinked_pike_string(struct pike_string * s) {    -  +  switch (s->flags & STRING_ALLOC_MASK) { +  case STRING_ALLOC_MALLOC: +  free(s->str); +  break; +  case STRING_ALLOC_BA: +  ba_free(&string_allocator, s->str); +  break; +  }    - /* breakpoint at 50% overhead. This doubles the size of short strings -  on 64-bit systems, but it only increases memory usage for short -  strings, on average, by 25% or so. */ +  ba_free(&string_allocator, s); + }    - #define SHORT_STRING_THRESHOLD ((ptrdiff_t)sizeof(struct pike_string_hdr)-1) -  - struct short_pike_string0 { -  PIKE_STRING_CONTENTS; -  p_wchar0 str[sizeof(struct pike_string_hdr)]; - }; -  - static struct block_allocator string_allocator = BA_INIT(sizeof(struct short_pike_string0), SHORT_STRING_BLOCK); -  - #define free_unlinked_pike_string(s) do { \ -  if (s->flags & STRING_IS_SHORT) { \ -  ba_free(&string_allocator, s); \ -  } else { \ -  debug_free(s, DMALLOC_LOCATION(), 1); \ -  } \ -  } while(0) -  +    /* note that begin_shared_string expects the _exact_ size of the string,    * not the maximum size    */   PMOD_EXPORT struct pike_string *debug_begin_shared_string(size_t len)   { -  struct pike_string *t; - #ifdef PIKE_DEBUG -  if(d_flag>10) -  verify_shared_strings_tables(); - #endif -  if (len <= SHORT_STRING_THRESHOLD) -  { -  t=ba_alloc(&string_allocator); -  t->flags = STRING_NOT_HASHED | STRING_NOT_SHARED | STRING_IS_SHORT; -  } else -  { -  t=xalloc(len + 1 + sizeof(struct pike_string_hdr)); -  t->flags = STRING_NOT_HASHED | STRING_NOT_SHARED; +  return debug_begin_wide_shared_string(len, 0);   } -  t->refs = 0; -  add_ref(t); /* For DMALLOC */ -  t->str[len]=0; -  t->len=len; - /* t->min = t->max = 0; */ -  t->size_shift=0; -  DO_IF_DEBUG(t->next = NULL); -  return t; - } +       static void link_pike_string(struct pike_string *s, size_t hval)   {
768:   PMOD_EXPORT struct pike_string *debug_begin_wide_shared_string(size_t len, int shift)   {    struct pike_string *t = NULL; +  size_t bytes = (len+1) << shift;   #ifdef PIKE_DEBUG    if(d_flag>10)    verify_shared_strings_tables();   #endif -  if ((len<<shift) <= SHORT_STRING_THRESHOLD) { +    #ifdef PIKE_DEBUG    if (shift > 2)    Pike_fatal("Unsupported string shift: %d\n", shift);   #endif /* PIKE_DEBUG */ -  t=ba_alloc(&string_allocator); -  t->flags = STRING_NOT_HASHED|STRING_NOT_SHARED|STRING_IS_SHORT; +  t=(struct pike_string *)ba_alloc(&string_allocator); +  /* we mark the string as static here, to avoid double free if the allocations +  * fail +  */ +  t->flags = STRING_NOT_HASHED|STRING_NOT_SHARED|STRING_ALLOC_STATIC; +  if (bytes <= sizeof(struct pike_string)) { +  /* FIXME: This can fail with an exception, which would leak +  * the string t. We should have ba_alloc and ba_xalloc, really. +  */ +  t->str = ba_alloc(&string_allocator); +  t->flags |= STRING_ALLOC_BA;    } else { -  t=xalloc(((len + 1)<<shift) + sizeof(struct pike_string_hdr)); -  t->flags = STRING_NOT_HASHED|STRING_NOT_SHARED; +  t->str = malloc(bytes); +  if (!t->str) { +  ba_free(&string_allocator, t); +  Pike_error("Out of memory.\n");    } -  +  t->flags |= STRING_ALLOC_MALLOC; +  }    t->refs = 0; -  +  t->size_shift=shift;    add_ref(t); /* For DMALLOC */    t->len=len; -  t->size_shift=shift; +     DO_IF_DEBUG(t->next = NULL);    low_set_index(t,len,0);    return t;   }    -  + PMOD_EXPORT struct pike_string * make_static_string(const char * str, size_t len, +  enum size_shift shift) { +  struct pike_string * t = ba_alloc(&string_allocator); +  +  t->flags = STRING_NOT_HASHED|STRING_NOT_SHARED|STRING_ALLOC_STATIC; +  t->str = str; +  t->refs = 0; +  t->len = len; +  t->size_shift = shift; +  add_ref(t); /* For DMALLOC */ +  +  return t; + } +  + PMOD_EXPORT struct pike_string * make_shared_static_string(const char *str, size_t len, +  enum size_shift shift) + { +  struct pike_string *s; +  ptrdiff_t h = StrHash(str, len); +  +  s = internal_findstring(str,len,shift,h); +  +  if (!s) { +  s = make_static_string(str, len, shift); +  link_pike_string(s, h); +  } else { +  +  if (!string_is_static(s)) { +  if (string_is_block_allocated(s)) { +  ba_free(&string_allocator, s->str); +  } else { +  free(s->str); +  } +  s->flags &= ~STRING_ALLOC_MASK; +  s->flags |= STRING_ALLOC_STATIC; +  s->str = str; +  } +  +  add_ref(s); +  } +  +  return s; + } +    /*    * This function assumes that the shift size is already the minimum it    * can be.
908: Inside #if defined(PIKE_DEBUG)
   if(len > str->len)    Pike_fatal("Cannot extend string here!\n");   #endif -  if( (str->len <= SHORT_STRING_THRESHOLD) || -  ((len > SHORT_STRING_THRESHOLD) && (str->len <= (len<<1))) ) -  { -  /* Short string before and after or -  * long string and at least half the length of the buffer. -  * -  * NOTE: We lose track of the actual buffer length here, so don't -  * use this function to shorten a string in decrements. -  */ -  str->len=len; -  SET_INDEX_PCHARP(MKPCHARP_STR(str), len, 0); -  return end_shared_string(str); -  } +     tmp = make_shared_binary_pcharp(MKPCHARP_STR(str),len);    free_string(str);    return tmp;
1157:    {    for(p=base_table[e];p;p=p->next)    { -  int is_short = (p->len <= SHORT_STRING_THRESHOLD); -  int key = (is_short?0:4) | p->size_shift; +  int key = p->size_shift;    num_distinct_strings[key]++; -  if (is_short) { -  bytes_distinct_strings[key] += -  DO_ALIGN(SHORT_STRING_THRESHOLD << p->size_shift, sizeof(void *)); -  } else { -  bytes_distinct_strings[key] += -  DO_ALIGN(p->len << p->size_shift, sizeof(void *)); -  } +  alloced_bytes[key] += p->refs*sizeof(struct pike_string);    alloced_strings[key] += p->refs; -  +  if (string_is_block_allocated(p)) {    alloced_bytes[key] += -  +  p->refs*sizeof(struct pike_string); +  } else { +  alloced_bytes[key] +=    p->refs*DO_ALIGN((p->len+3) << p->size_shift,sizeof(void *));    }    } -  +  }    string_builder_sprintf(&s,    "\nShared string hash table:\n"    "-------------------------\n"
1190:    }       overhead_bytes[e] = -  DO_NOT_WARN((long)sizeof(struct pike_string_hdr) * +  DO_NOT_WARN((long)sizeof(struct pike_string) *    num_distinct_strings[e]);       alloced_strings[e|3] += alloced_strings[e];
1587:   struct pike_string *realloc_unlinked_string(struct pike_string *a,    ptrdiff_t size)   { -  struct pike_string *r = NULL; +  char * s = NULL; +  size_t nbytes = (size_t)(size+1) << a->size_shift; +  size_t obytes = (size_t)(a->len+1) << a->size_shift;    -  if (a->flags & STRING_IS_SHORT ) { -  if (size <= SHORT_STRING_THRESHOLD/(1<<a->size_shift)) -  r = a; /* There's already space enough. */ -  } else -  r=realloc(a, sizeof(struct pike_string_hdr)+((size+1)<<a->size_shift)); -  -  if(!r) { -  r=begin_wide_shared_string(size, a->size_shift); -  r->flags |= a->flags & ~15; -  r->min = a->min; -  r->max = a->max; -  memcpy(r->str, a->str, (a->len <= size ? a->len : size)<<a->size_shift); -  free_string(a); +  switch ((a->flags & STRING_ALLOC_MASK) | (nbytes <= sizeof(struct pike_string))) +  { +  case STRING_ALLOC_MALLOC: +  s=realloc(a->str, nbytes); +  if (!s) +  Pike_error("Out of memory in realloc_unlinked_string. Could not allocate %"PRINTSIZET" bytes.\n", +  nbytes); +  break; +  case STRING_ALLOC_BA: // old string was short +  s = xalloc(nbytes); +  a->flags &= ~STRING_ALLOC_MASK; +  a->flags |= STRING_ALLOC_MALLOC; +  memcpy(s, a->str, obytes); +  ba_free(&string_allocator, a->str); +  break; +  case STRING_ALLOC_MALLOC|1: +  s = ba_alloc(&string_allocator); +  a->flags &= ~STRING_ALLOC_MASK; +  a->flags |= STRING_ALLOC_BA; +  memcpy(s, a->str, nbytes); +  free(a->str); +  break; +  case STRING_ALLOC_BA|1: // both are short +  goto done; +  case STRING_ALLOC_STATIC: +  s = xalloc(nbytes); +  a->flags &= ~STRING_ALLOC_MASK; +  a->flags |= STRING_ALLOC_MALLOC; +  memcpy(s, a->str, obytes); +  break; +  case STRING_ALLOC_STATIC|1: +  s = ba_alloc(&string_allocator); +  a->flags &= ~STRING_ALLOC_MASK; +  a->flags |= STRING_ALLOC_BA; +  memcpy(s, a->str, obytes); +  break; + #ifdef PIKE_DEBUG +  default: +  Pike_fatal("encountered string with unknown allocation type %d\n", +  a->flags & STRING_ALLOC_MASK); + #endif    }    -  r->len=size; -  low_set_index(r,size,0); /* Enforce \0 termination */ -  return r; +  a->str = s; + done: +  a->len=size; +  low_set_index(a,size,0); +  +  return a;   }      /* Returns an unlinked string ready for end_shared_string */   static struct pike_string *realloc_shared_string(struct pike_string *a,    ptrdiff_t size)   { -  struct pike_string *r; -  if(a->refs==1) +  if(string_may_modify(a))    {    unlink_pike_string(a);    return realloc_unlinked_string(a, size);    }else{ -  r=begin_wide_shared_string(size,a->size_shift); -  memcpy(r->str, a->str, a->len<<a->size_shift); -  r->flags |= a->flags & ~15; +  struct pike_string *r=begin_wide_shared_string(size,a->size_shift); +  MEMCPY(r->str, a->str, a->len<<a->size_shift); +  r->flags |= a->flags & STRING_CHECKED_MASK;    r->min = a->min;    r->max = a->max;    free_string(a);
1636:       r=begin_wide_shared_string(size,shift);    pike_string_cpy(MKPCHARP_STR(r),a); -  r->flags |= (a->flags & ~15); +  r->flags |= (a->flags & STRING_CHECKED_MASK);    r->min = a->min;    r->max = a->max;    free_string(a);
1764:          /* We now know that the string has the right character size */ -  if(a->refs==1) +  if(string_may_modify(a))    {    /* One ref - destructive mode */       unlink_pike_string(a);    low_set_index(a, index, c);    CLEAR_STRING_CHECKED(a); -  if((((unsigned int)index) >= hash_prefix_len) && (index < a->len-8)) +  +  if((((unsigned int)index) >= hash_prefix_len) && (index < a->len-8) )    {    struct pike_string *old;    /* Doesn't change hash value - sneak it in there */
2138:   #endif /* DO_PIKE_CLEANUP */   }    - static INLINE size_t memory_in_string (const struct pike_string *s) - { -  if (s->flags & STRING_IS_SHORT ) -  return sizeof (struct short_pike_string0); -  else -  return sizeof (struct pike_string_hdr) + ((s->len + 1) << s->size_shift); + void count_string_types() { +  unsigned INT32 e; +  size_t num_static = 0, num_short = 0; +  +  for (e = 0; e < htable_size; e++) { +  struct pike_string * s; +  +  for (s = base_table[e]; s; s = s->next) +  switch (s->flags & STRING_ALLOC_MASK) { +  case STRING_ALLOC_BA: +  num_short ++; +  break; +  case STRING_ALLOC_STATIC: +  num_static ++; +  break;    } -  +  }    - void count_memory_in_short_pike_strings(size_t *num, size_t *size) - { -  ba_count_all(&string_allocator, num, size); +  push_static_text("num_short_pike_strings"); +  push_ulongest(num_short); +  push_static_text("num_static_pike_strings"); +  push_ulongest(num_static);   }    - void count_memory_in_strings(size_t *num, size_t *size) + size_t count_memory_in_string(const struct pike_string * s) { +  size_t size = sizeof(struct pike_string); +  +  switch (s->flags & STRING_ALLOC_MASK) { +  case STRING_ALLOC_BA: +  size += sizeof(struct pike_string); +  break; +  case STRING_ALLOC_MALLOC: +  size += PIKE_ALIGNTO(((s->len + 1) << s->size_shift), 4); +  break; +  } +  +  return size; + } +  + void count_memory_in_strings(size_t *num, size_t *_size)   {    unsigned INT32 e; -  size_t num_=0, size_=0; -  if(!base_table) -  { -  *num=*size=0; -  return; +  size_t size = 0; +  *num = num_strings; +  +  size+=htable_size * sizeof(struct pike_string *); +  +  for (e = 0; e < htable_size; e++) { +  struct pike_string * s; +  +  for (s = base_table[e]; s; s = s->next) { +  size += count_memory_in_string(s);    } -  size_+=htable_size * sizeof(struct pike_string *); -  for(e=0;e<htable_size;e++) -  { -  struct pike_string *p; -  for(p=base_table[e];p;p=p->next) -  { -  num_++; -  size_ += memory_in_string (p); +     } -  +  +  *_size = size;   } - #ifdef PIKE_DEBUG -  if(num_strings != num_) -  Pike_fatal("Num strings is wrong! %d!=%d.\n",num_strings, num_); - #endif -  num[0]=num_; -  size[0]=size_; - } +       PMOD_EXPORT void visit_string (struct pike_string *s, int action, void *extra)   {
2190:    break;   #endif    case VISIT_COUNT_BYTES: -  mc_counted_bytes += memory_in_string (s); +  mc_counted_bytes += count_memory_in_string (s);    break;    }    visit_leave(s, T_STRING, extra);
2260:    to->known_shift = from->known_shift;   }    + /* str becomes invalid if successful (i.e. nonzero returned), +  * otherwise nothing happens. */ + PMOD_EXPORT int init_string_builder_with_string (struct string_builder *s, +  struct pike_string *str) + { +  if (string_may_modify(str)) { +  /* Unlink the string and use it as buffer directly. */ +  unlink_pike_string (str); +  str->flags = STRING_NOT_SHARED; +  s->s = str; +  s->malloced = str->len; +  s->known_shift = str->size_shift; +  return 1; +  } +  return 0; + } +    PMOD_EXPORT void string_build_mkspace(struct string_builder *s,    ptrdiff_t chars, int mag)   /* Doesn't touch or sanity check s->known_shift. */