From 7f55decf8f136f4f0682ecbbfd5e559710daa407 Mon Sep 17 00:00:00 2001 From: Mikko Mononen Date: Sat, 3 May 2014 11:56:26 +0300 Subject: [PATCH] More error checking on allocations - changed remainder of allocations to follow same 1.5x patters - do not leak memory when realloc() fails - added error handling in case allocations fail --- src/nanovg.c | 33 +++++++++++++---------- src/nanovg_gl.h | 70 ++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/src/nanovg.c b/src/nanovg.c index da42bab..3c56c6a 100644 --- a/src/nanovg.c +++ b/src/nanovg.c @@ -857,11 +857,11 @@ static void nvg__appendCommands(struct NVGcontext* ctx, float* vals, int nvals) int i; if (ctx->ncommands+nvals > ctx->ccommands) { - if (ctx->ccommands == 0) ctx->ccommands = 8; - while (ctx->ccommands < ctx->ncommands+nvals) - ctx->ccommands *= 2; - ctx->commands = (float*)realloc(ctx->commands, ctx->ccommands*sizeof(float)); - if (ctx->commands == NULL) return; + float* commands; + ctx->ccommands = ctx->ncommands+nvals + ctx->ccommands/2; + commands = (float*)realloc(ctx->commands, ctx->ccommands*sizeof(float)); + if (commands == NULL) return; + ctx->commands = commands; } // transform commands @@ -922,9 +922,11 @@ static void nvg__addPath(struct NVGcontext* ctx) { struct NVGpath* path; if (ctx->cache->npaths+1 > ctx->cache->cpaths) { + struct NVGpath* paths; ctx->cache->cpaths = ctx->cache->npaths+1 + ctx->cache->cpaths/2; - ctx->cache->paths = (struct NVGpath*)realloc(ctx->cache->paths, sizeof(struct NVGpath)*ctx->cache->cpaths); - if (ctx->cache->paths == NULL) return; + paths = (struct NVGpath*)realloc(ctx->cache->paths, sizeof(struct NVGpath)*ctx->cache->cpaths); + if (paths == NULL) return; + ctx->cache->paths = paths; } path = &ctx->cache->paths[ctx->cache->npaths]; memset(path, 0, sizeof(*path)); @@ -956,9 +958,11 @@ static void nvg__addPoint(struct NVGcontext* ctx, float x, float y, int flags) } if (ctx->cache->npoints+1 > ctx->cache->cpoints) { + struct NVGpoint* points; ctx->cache->cpoints = ctx->cache->npoints+1 + ctx->cache->cpoints/2; - ctx->cache->points = (struct NVGpoint*)realloc(ctx->cache->points, sizeof(struct NVGpoint)*ctx->cache->cpoints); - if (ctx->cache->points == NULL) return; + points = (struct NVGpoint*)realloc(ctx->cache->points, sizeof(struct NVGpoint)*ctx->cache->cpoints); + if (points == NULL) return; + ctx->cache->points = points; } pt = &ctx->cache->points[ctx->cache->npoints]; @@ -995,12 +999,13 @@ static float nvg__getAverageScale(float *t) static struct NVGvertex* nvg__allocTempVerts(struct NVGcontext* ctx, int nverts) { if (nverts > ctx->cache->cverts) { - if (ctx->cache->cverts == 0) ctx->cache->cverts = 8; - while (ctx->cache->cverts < nverts) - ctx->cache->cverts *= 2; - ctx->cache->verts = (struct NVGvertex*)realloc(ctx->cache->verts, sizeof(struct NVGvertex)*ctx->cache->cverts); - if (ctx->cache->verts == NULL) return NULL; + struct NVGvertex* verts; + ctx->cache->cverts = (nverts + 0xff) & ~0xff; // Round up to prevent allocations when things change just slightly. + verts = (struct NVGvertex*)realloc(ctx->cache->verts, sizeof(struct NVGvertex)*ctx->cache->cverts); + if (verts == NULL) return NULL; + ctx->cache->verts = verts; } + return ctx->cache->verts; } diff --git a/src/nanovg_gl.h b/src/nanovg_gl.h index 4dfb11f..c72e64d 100644 --- a/src/nanovg_gl.h +++ b/src/nanovg_gl.h @@ -128,6 +128,7 @@ struct GLNVGtexture { }; enum GLNVGcallType { + GLNVG_NONE = 0, GLNVG_FILL, GLNVG_CONVEXFILL, GLNVG_STROKE, @@ -199,6 +200,7 @@ struct GLNVGcontext { int nuniforms; }; +static int glnvg__maxi(int a, int b) { return a > b ? a : b; } static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) { @@ -213,9 +215,11 @@ static struct GLNVGtexture* glnvg__allocTexture(struct GLNVGcontext* gl) } if (tex == NULL) { if (gl->ntextures+1 > gl->ctextures) { - gl->ctextures = (gl->ctextures == 0) ? 2 : gl->ctextures*2; - gl->textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); - if (gl->textures == NULL) return NULL; + struct GLNVGtexture* textures; + gl->ctextures = glnvg__maxi(gl->ntextures+1, 4) + gl->ctextures/2; // 1.5x Overallocate + textures = (struct GLNVGtexture*)realloc(gl->textures, sizeof(struct GLNVGtexture)*gl->ctextures); + if (textures == NULL) return NULL; + gl->textures = textures; } tex = &gl->textures[gl->ntextures++]; } @@ -577,6 +581,7 @@ static int glnvg__renderCreateTexture(void* uptr, int type, int w, int h, const struct GLNVGtexture* tex = glnvg__allocTexture(gl); if (tex == NULL) return 0; + glGenTextures(1, &tex->tex); tex->width = w; tex->height = h; @@ -978,14 +983,15 @@ static int glnvg__maxVertCount(const struct NVGpath* paths, int npaths) return count; } -static int glnvg__maxi(int a, int b) { return a > b ? a : b; } - static struct GLNVGcall* glnvg__allocCall(struct GLNVGcontext* gl) { struct GLNVGcall* ret = NULL; if (gl->ncalls+1 > gl->ccalls) { + struct GLNVGcall* calls; gl->ccalls = glnvg__maxi(gl->ncalls+1, 128) + gl->ccalls; // 1.5x Overallocate - gl->calls = (struct GLNVGcall*)realloc(gl->calls, sizeof(struct GLNVGcall) * gl->ccalls); + calls = (struct GLNVGcall*)realloc(gl->calls, sizeof(struct GLNVGcall) * gl->ccalls); + if (calls == NULL) return NULL; + gl->calls = calls; } ret = &gl->calls[gl->ncalls++]; memset(ret, 0, sizeof(struct GLNVGcall)); @@ -996,8 +1002,11 @@ static int glnvg__allocPaths(struct GLNVGcontext* gl, int n) { int ret = 0; if (gl->npaths+n > gl->cpaths) { + struct GLNVGpath* paths; gl->cpaths = glnvg__maxi(gl->npaths + n, 128) + gl->cpaths; // 1.5x Overallocate - gl->paths = (struct GLNVGpath*)realloc(gl->paths, sizeof(struct GLNVGpath) * gl->cpaths); + paths = (struct GLNVGpath*)realloc(gl->paths, sizeof(struct GLNVGpath) * gl->cpaths); + if (paths == NULL) return -1; + gl->paths = paths; } ret = gl->npaths; gl->npaths += n; @@ -1008,8 +1017,11 @@ static int glnvg__allocVerts(struct GLNVGcontext* gl, int n) { int ret = 0; if (gl->nverts+n > gl->cverts) { + struct NVGvertex* verts; gl->cverts = glnvg__maxi(gl->nverts + n, 4096) + gl->cverts/2; // 1.5x Overallocate - gl->verts = (struct NVGvertex*)realloc(gl->verts, sizeof(struct NVGvertex) * gl->cverts); + verts = (struct NVGvertex*)realloc(gl->verts, sizeof(struct NVGvertex) * gl->cverts); + if (verts == NULL) return -1; + gl->verts = verts; } ret = gl->nverts; gl->nverts += n; @@ -1020,8 +1032,11 @@ static int glnvg__allocFragUniforms(struct GLNVGcontext* gl, int n) { int ret = 0, structSize = gl->fragSize; if (gl->nuniforms+n > gl->cuniforms) { + unsigned char* uniforms; gl->cuniforms = glnvg__maxi(gl->nuniforms+n, 128) + gl->cuniforms/2; // 1.5x Overallocate - gl->uniforms = (unsigned char*)realloc(gl->uniforms, gl->cuniforms * structSize); + uniforms = (unsigned char*)realloc(gl->uniforms, gl->cuniforms * structSize); + if (uniforms == NULL) return -1; + gl->uniforms = uniforms; } ret = gl->nuniforms * structSize; gl->nuniforms += n; @@ -1050,8 +1065,11 @@ static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscis struct GLNVGfragUniforms* frag; int i, maxverts, offset; + if (call == NULL) return; + call->type = GLNVG_FILL; call->pathOffset = glnvg__allocPaths(gl, npaths); + if (call->pathOffset == -1) goto error; call->pathCount = npaths; call->image = paint->image; @@ -1061,6 +1079,7 @@ static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscis // Allocate vertices for all the paths. maxverts = glnvg__maxVertCount(paths, npaths) + 6; offset = glnvg__allocVerts(gl, maxverts); + if (offset == -1) goto error; for (i = 0; i < npaths; i++) { struct GLNVGpath* copy = &gl->paths[call->pathOffset + i]; @@ -1095,6 +1114,7 @@ static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscis // Setup uniforms for draw calls if (call->type == GLNVG_FILL) { call->uniformOffset = glnvg__allocFragUniforms(gl, 2); + if (call->uniformOffset == -1) goto error; // Simple shader for stencil frag = nvg__fragUniformPtr(gl, call->uniformOffset); memset(frag, 0, sizeof(*frag)); @@ -1103,9 +1123,17 @@ static void glnvg__renderFill(void* uptr, struct NVGpaint* paint, struct NVGscis glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset + gl->fragSize), paint, scissor, fringe, fringe); } else { call->uniformOffset = glnvg__allocFragUniforms(gl, 1); + if (call->uniformOffset == -1) goto error; // Fill shader glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, fringe, fringe); } + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gl->ncalls > 0) gl->ncalls--; } static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, float fringe, @@ -1115,14 +1143,18 @@ static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGsc struct GLNVGcall* call = glnvg__allocCall(gl); int i, maxverts, offset; + if (call == NULL) return; + call->type = GLNVG_STROKE; call->pathOffset = glnvg__allocPaths(gl, npaths); + if (call->pathOffset == -1) goto error; call->pathCount = npaths; call->image = paint->image; // Allocate vertices for all the paths. maxverts = glnvg__maxVertCount(paths, npaths); offset = glnvg__allocVerts(gl, maxverts); + if (offset == -1) goto error; for (i = 0; i < npaths; i++) { struct GLNVGpath* copy = &gl->paths[call->pathOffset + i]; @@ -1138,7 +1170,15 @@ static void glnvg__renderStroke(void* uptr, struct NVGpaint* paint, struct NVGsc // Fill shader call->uniformOffset = glnvg__allocFragUniforms(gl, 1); + if (call->uniformOffset == -1) goto error; glnvg__convertPaint(gl, nvg__fragUniformPtr(gl, call->uniformOffset), paint, scissor, strokeWidth, fringe); + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gl->ncalls > 0) gl->ncalls--; } static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NVGscissor* scissor, @@ -1148,19 +1188,31 @@ static void glnvg__renderTriangles(void* uptr, struct NVGpaint* paint, struct NV struct GLNVGcall* call = glnvg__allocCall(gl); struct GLNVGfragUniforms* frag; + if (call == NULL) return; + call->type = GLNVG_TRIANGLES; call->image = paint->image; // Allocate vertices for all the paths. call->triangleOffset = glnvg__allocVerts(gl, nverts); + if (call->triangleOffset == -1) goto error; call->triangleCount = nverts; + memcpy(&gl->verts[call->triangleOffset], verts, sizeof(struct NVGvertex) * nverts); // Fill shader call->uniformOffset = glnvg__allocFragUniforms(gl, 1); + if (call->uniformOffset == -1) goto error; frag = nvg__fragUniformPtr(gl, call->uniformOffset); glnvg__convertPaint(gl, frag, paint, scissor, 1.0f, 1.0f); frag->type = NSVG_SHADER_IMG; + + return; + +error: + // We get here if call alloc was ok, but something else is not. + // Roll back the last call to prevent drawing it. + if (gl->ncalls > 0) gl->ncalls--; } static void glnvg__renderDelete(void* uptr)