1

I am new to C and want to know if how I am handling something is correct. I am creating a module for http://nginx.com/, and I am creating a status page for my module.

Now the status page will consist of some basic HTML & tables. Here is some of my code to create this.

// Get size
size =
    sizeof("<table>") +
    sizeof("<tr><td align=\"right\">enabled:</td><td>YES</td></tr>") +
    sizeof("<tr><td align=\"right\">activated:</td><td>YES</td></tr>") +
    sizeof("<tr><td align=\"right\">connections/lt:</td><td>") + NGX_ATOMIC_T_LEN + sizeof(" / ") + NGX_ATOMIC_T_LEN + sizeof("</td></tr>") +
    sizeof("<tr><td align=\"right\">remain on: xxxx-xx-xx xx:xx:xx GMT</td><td></td></tr>") +
    sizeof("</table>");

// Start buffer
b = ngx_create_temp_buf(r->pool, size);
if (b == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

// Start chain
out.buf = b;
out.next = NULL;

// Finish buffer
b->last = ngx_sprintf(b->last, "<table>");
b->last = ngx_sprintf(b->last, "<tr><td align=\"right\">enabled:</td><td>%s</td></tr>", alcf->enabled ? "YES" : "NO");
b->last = ngx_sprintf(b->last, "<tr><td align=\"right\">activated:</td><td>%s</td></tr>", alcf->activated ? "YES" : "NO");
b->last = ngx_sprintf(b->last, "<tr><td align=\"right\">connections/lt:</td><td>%uA / %uA</td></tr>", ac, alcf->connections_activate);
b->last = ngx_sprintf(b->last, "<tr><td align=\"right\">remain on:</td><td>");
b->last = !alcf->activatedEndTime ? ngx_sprintf(b->last,"") : ngx_http_cookie_time(b->last, alcf->activatedEndTime);
b->last = ngx_sprintf(b->last, "</td></tr>");
b->last = ngx_sprintf(b->last, "<table>");

Is this the only effective way of doing this, I feel like it would be wrong to have to write the HTML code twice, once to get the size to inflate the buffer, and one to actually store in the buffer. Would there be any other solution to this. I am trying to keep it as memory efficient as possible.

arosolino
  • 1,059
  • 8
  • 10

2 Answers2

3

Remember: sizeof is a compile-time operator, so you aren't using any extra memory with your sizeof statement.

So yes, this is as efficient as can be, assuming your compiler has even a small amount of optimization.

Community
  • 1
  • 1
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201
1

I think you have an off-by-one error - you forgot to keep room for the terminating \0.

Your usage of sizeof works, but forces you to repeat each part of the HTML. One day you'll update the HTML in the sprintf lines, and forget to update the copy in the sizeof lines, which would lead to a memory overrun.

A better way would be to keep the strings once, and use strlen to get the sizes.
Here's an example of the idea. It's far from perfect, but it saves most of the repetition.

struct html_part {
   const char *text;
   size_t extra_len;
};
struct html_part html_parts[] = {
    { "<table>", 0 }
    { "<tr><td align=\"right\">enabled:</td><td>%s</td></tr>", 3-2 } // YES=3, %s=2
    ...
};
// Calculate the space needed
len = 0;
for (i=0;i<sizeof(html_parts)/sizeof(html_parts[0]);i++) {
    len += strlen(html_parts[i].text) + html_parts[i].extra_len;
}
len++;  // For the terminating null
...
// Print the data
b->last = ngx_sprintf(b->last, html_parts[0]);
b->last = ngx_sprintf(b->last, html_parts[1], alcf->enabled ? "YES" : "NO");
ugoren
  • 16,023
  • 3
  • 35
  • 65