ncplane_resize_internal: release lost cells in the general case #2426

This commit is contained in:
nick black 2021-12-14 01:16:05 -05:00
parent 902fe7ee4b
commit 7a99d00719
No known key found for this signature in database
GPG Key ID: 5F43400C21CBFACC

View File

@ -841,20 +841,29 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
int keptarea = keepleny * keeplenx; int keptarea = keepleny * keeplenx;
int newarea = ylen * xlen; int newarea = ylen * xlen;
size_t fbsize = sizeof(nccell) * newarea; size_t fbsize = sizeof(nccell) * newarea;
// an important optimization is the case where nothing needs to be moved,
// true when either the keptarea is 0 or the old and new x dimensions are
// equal, and we're keeping the full x dimension, and any material we're
// keeping starts at the top. in this case, we try to realloc() and avoid
// any copying whatsoever (we otherwise incur at least one copy due to
// always using a new area). set realloced high in this event, so we don't
// free anything.
nccell* fb; nccell* fb;
bool realloced = false; // there are two cases worth optimizing:
if(keptarea == 0 || (cols == xlen && cols == keeplenx && keepy == 0)){ //
// * nothing is kept. we malloc() a new cellmatrix, dump the EGCpool in
// toto, and zero out the matrix. no copies, one memset.
// * old and new x dimensions match, and we're keeping the full width.
// we release any cells we're about to lose, realloc() the cellmatrix,
// and zero out any new cells. so long as the realloc() doesn't move
// us, there are no copies, one memset, one iteration (since this is
// most often due to autogrowth by a single line, the likelihood that
// we remain where we are is pretty high).
// * otherwise, we malloc() a new cellmatrix, zero out any new cells,
// copy over any reused cells, and release any lost cells. one
// gigantic iteration.
// we might realloc instead of mallocing, in which case we NULL out
// |preserved|. it must otherwise be free()d at the end.
nccell* preserved = n->fb;
if(cols == xlen && cols == keeplenx && keepy){
// we need release the cells that we're losing, lest we leak EGCpool // we need release the cells that we're losing, lest we leak EGCpool
// memory. unfortunately, this means we mutate the plane on the error // memory. unfortunately, this means we mutate the plane on the error case.
// case...any solution would involve copying them out first. // any solution would involve copying them out first. we only do this if
if(n->leny > ylen){ // we're keeping some, as we otherwise drop the EGCpool in toto.
if(keptarea && n->leny > ylen){
for(unsigned y = ylen ; y < n->leny ; ++y){ for(unsigned y = ylen ; y < n->leny ; ++y){
for(unsigned x = 0 ; x < n->lenx ; ++x){ for(unsigned x = 0 ; x < n->lenx ; ++x){
nccell_release(n, ncplane_cell_ref_yx(n, y, x)); nccell_release(n, ncplane_cell_ref_yx(n, y, x));
@ -864,7 +873,7 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
if((fb = realloc(n->fb, fbsize)) == NULL){ if((fb = realloc(n->fb, fbsize)) == NULL){
return -1; return -1;
} }
realloced = true; preserved = NULL;
}else{ }else{
if((fb = malloc(fbsize)) == NULL){ if((fb = malloc(fbsize)) == NULL){
return -1; return -1;
@ -875,7 +884,7 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
// FIXME first, free any disposed auxiliary vectors! // FIXME first, free any disposed auxiliary vectors!
tament* tmptam = realloc(n->tam, sizeof(*tmptam) * newarea); tament* tmptam = realloc(n->tam, sizeof(*tmptam) * newarea);
if(tmptam == NULL){ if(tmptam == NULL){
if(!realloced){ if(preserved){
free(fb); free(fb);
} }
return -1; return -1;
@ -892,12 +901,10 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
if(n->x >= xlen){ if(n->x >= xlen){
n->x = xlen - 1; n->x = xlen - 1;
} }
nccell* preserved = n->fb;
pthread_mutex_lock(&nc->stats.lock); pthread_mutex_lock(&nc->stats.lock);
ncplane_notcurses(n)->stats.s.fbbytes -= sizeof(*preserved) * (rows * cols); ncplane_notcurses(n)->stats.s.fbbytes -= sizeof(*fb) * (rows * cols);
ncplane_notcurses(n)->stats.s.fbbytes += fbsize; ncplane_notcurses(n)->stats.s.fbbytes += fbsize;
pthread_mutex_unlock(&nc->stats.lock); pthread_mutex_unlock(&nc->stats.lock);
n->fb = fb;
const int oldabsy = n->absy; const int oldabsy = n->absy;
// go ahead and move. we can no longer fail at this point. but don't yet // go ahead and move. we can no longer fail at this point. but don't yet
// resize, because n->len[xy] are used in fbcellidx() in the loop below. we // resize, because n->len[xy] are used in fbcellidx() in the loop below. we
@ -910,7 +917,7 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
// and keep it. perhaps we ought compact it? // and keep it. perhaps we ought compact it?
memset(fb, 0, sizeof(*fb) * newarea); memset(fb, 0, sizeof(*fb) * newarea);
egcpool_dump(&n->pool); egcpool_dump(&n->pool);
}else if(realloced){ }else if(!preserved){
// the x dimensions are equal, and we're keeping across the width. only the // the x dimensions are equal, and we're keeping across the width. only the
// y dimension changed. if we grew, we need zero out the new cells (if we // y dimension changed. if we grew, we need zero out the new cells (if we
// shrunk, we already released the old cells prior to the realloc). // shrunk, we already released the old cells prior to the realloc).
@ -946,17 +953,18 @@ int ncplane_resize_internal(ncplane* n, int keepy, int keepx,
memcpy(fb + copyoff, preserved + sourceidx, sizeof(*fb) * keeplenx); memcpy(fb + copyoff, preserved + sourceidx, sizeof(*fb) * keeplenx);
copyoff += keeplenx; copyoff += keeplenx;
copied += keeplenx; copied += keeplenx;
if(xlen > copied){ unsigned perline = xlen - copied;
memset(fb + copyoff, 0, sizeof(*fb) * (xlen - copied)); for(unsigned x = copyoff ; x < n->lenx ; ++x){
nccell_release(n, ncplane_cell_ref_yx(n, sourceoffy, x));
} }
memset(fb + copyoff, 0, sizeof(*fb) * perline);
} }
} }
} }
n->fb = fb;
n->lenx = xlen; n->lenx = xlen;
n->leny = ylen; n->leny = ylen;
if(!realloced){ free(preserved);
free(preserved);
}
return resize_callbacks_children(n); return resize_callbacks_children(n);
} }