2015-08-18
2015-08-18 16:55:08 by Per Hedbor <ph@opera.com>
-
ced1916864a918a6f2abf86739bb5ad038f0b9c8
(71 lines)
(+53/-18)
[
Show
| Annotate
]
Branch: per/string_split_merge
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. */