diff --git a/src/lib/egcpool.c b/src/lib/egcpool.c index facc5d32b..033ea9fb5 100644 --- a/src/lib/egcpool.c +++ b/src/lib/egcpool.c @@ -3,15 +3,7 @@ #define POOL_MINIMUM_ALLOC BUFSIZ -int egcpool_grow(egcpool* pool, size_t len, bool force){ - const size_t poolfree = pool->poolsize - pool->poolused; - // proactively get more space if we have less than 10% free. this doesn't - // guarantee that we'll have enough space to insert the string -- we could - // theoretically have every 10th byte free, and be unable to write even a - // two-byte egc -- so we might have to allocate after an expensive search :/. - if(poolfree >= len && poolfree * 10 > pool->poolsize && !force){ - return 0; - } +int egcpool_grow(egcpool* pool, size_t len){ size_t newsize = pool->poolsize * 2; if(newsize < POOL_MINIMUM_ALLOC){ newsize = POOL_MINIMUM_ALLOC; diff --git a/src/lib/egcpool.h b/src/lib/egcpool.h index a6a34e28f..1d993066b 100644 --- a/src/lib/egcpool.h +++ b/src/lib/egcpool.h @@ -27,7 +27,7 @@ egcpool_init(egcpool* p){ memset(p, 0, sizeof(*p)); } -int egcpool_grow(egcpool* pool, size_t len, bool force); +int egcpool_grow(egcpool* pool, size_t len); // FIXME needs to loop on wcwidth() == 0 static inline size_t @@ -40,6 +40,20 @@ utf8_gce_len(const char* gcluster){ return r; } +// if we're inserting a EGC of |len| bytes, ought we proactively realloc? +static inline bool +egcpool_alloc_justified(const egcpool* pool, size_t len){ + const size_t poolfree = pool->poolsize - pool->poolused; + // proactively get more space if we have less than 10% free. this doesn't + // guarantee that we'll have enough space to insert the string -- we could + // theoretically have every 10th byte free, and be unable to write even a + // two-byte egc -- so we might have to allocate after an expensive search :/. + if(poolfree >= len && poolfree * 10 > pool->poolsize){ + return false; + } + return true; +} + // stash away the provided UTF8, NUL-terminated grapheme cluster. the cluster // should not be less than 2 bytes (such a cluster should be directly stored in // the cell). returns -1 on error, and otherwise a non-negative 24-bit offset. @@ -55,9 +69,20 @@ egcpool_stash(egcpool* pool, const char* egc, size_t* ulen){ // to have too little space. once we've done a search, we do force the grow. // we should thus never have more than two iterations of this loop. bool searched = false; + // we might have to realloc our underlying pool. it is possible that this EGC + // is actually *in* that pool, in which case our pointer will be invalidated. + // to be safe, duplicate prior to a realloc, and free along all paths. + char* duplicated = NULL; do{ - if(egcpool_grow(pool, len, false)){ - return -1; + if(egcpool_alloc_justified(pool, len) || searched){ + if(!duplicated){ + duplicated = strdup(egc); + } + if(egcpool_grow(pool, len)){ + free(duplicated); + return -1; + } + egc = duplicated; } // we now look for a place to lay out this egc. we need |len| zeroes in a // row. starting at pool->poolwrite, look for such a range of unused @@ -95,12 +120,14 @@ egcpool_stash(egcpool* pool, const char* egc, size_t* ulen){ pool->poolwrite = len - fchunk; } pool->poolused += len; + free(duplicated); return curpos; } curpos += len - need; // do we always hit pool->poolwrite properly? } }while(curpos != pool->poolwrite); }while( (searched = !searched) ); + free(duplicated); return -1; // should never get here }