From 226a3de55d4ec45c3c2dc462d4cf7f2cb1c2e6d6 Mon Sep 17 00:00:00 2001 From: Sergey Lyubka Date: Wed, 6 Nov 2024 06:14:38 +0000 Subject: [PATCH] Fix dbl printing --- mongoose.c | 22 +++++-- src/fmt.c | 22 +++++-- test/unit_test.c | 159 +++++++++++++++++++++++++++++++++-------------- 3 files changed, 145 insertions(+), 58 deletions(-) diff --git a/mongoose.c b/mongoose.c index 33a5231e..bfc8fcc9 100644 --- a/mongoose.c +++ b/mongoose.c @@ -550,28 +550,36 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { // Round saved = d; - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; + if (tz) { + mul = 1.0; + while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; + } else { + mul = 0.1; + } + while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0; for (i = 0, t = mul * 5; i < width; i++) t /= 10.0; + d += t; + // Calculate exponent, and 'mul' for scientific representation mul = 1.0; while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++; while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; // printf(" --> %g %d %g %g\n", saved, e, t, mul); - if (e >= width && width > 1) { + if (tz && e >= width && width > 1) { n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); n += addexp(buf + s + n, e, '+'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (e <= -width && width > 1) { + } else if (tz && e <= -width && width > 1) { n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); n += addexp(buf + s + n, -e, '-'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); } else { + int targ_width = width; for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { int ch = (int) (d / t); if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0'); @@ -583,15 +591,17 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0; if (s + n < (int) sizeof(buf)) buf[n + s++] = '.'; // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); - for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) { + if (!tz && n > 0) targ_width = width + n; + for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < targ_width; i++) { int ch = (int) (d / t); buf[s + n++] = (char) (ch + '0'); d -= ch * t; t /= 10.0; } } + while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes - if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot + if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot n += s; if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; buf[n] = '\0'; diff --git a/src/fmt.c b/src/fmt.c index f2209f1a..e16f7f46 100644 --- a/src/fmt.c +++ b/src/fmt.c @@ -48,28 +48,36 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { // Round saved = d; - mul = 1.0; - while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; + if (tz) { + mul = 1.0; + while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0; + } else { + mul = 0.1; + } + while (d <= 1.0 && d / mul <= 1.0) mul /= 10.0; for (i = 0, t = mul * 5; i < width; i++) t /= 10.0; + d += t; + // Calculate exponent, and 'mul' for scientific representation mul = 1.0; while (d >= 10.0 && d / mul >= 10.0) mul *= 10.0, e++; while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; // printf(" --> %g %d %g %g\n", saved, e, t, mul); - if (e >= width && width > 1) { + if (tz && e >= width && width > 1) { n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); n += addexp(buf + s + n, e, '+'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (e <= -width && width > 1) { + } else if (tz && e <= -width && width > 1) { n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); n += addexp(buf + s + n, -e, '-'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); } else { + int targ_width = width; for (i = 0, t = mul; t >= 1.0 && s + n < (int) sizeof(buf); i++) { int ch = (int) (d / t); if (n > 0 || ch > 0) buf[s + n++] = (char) (ch + '0'); @@ -81,15 +89,17 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { while (t >= 1.0 && n + s < (int) sizeof(buf)) buf[n++] = '0', t /= 10.0; if (s + n < (int) sizeof(buf)) buf[n + s++] = '.'; // printf(" 1--> [%g] -> [%.*s]\n", saved, s + n, buf); - for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < width; i++) { + if (!tz && n > 0) targ_width = width + n; + for (i = 0, t = 0.1; s + n < (int) sizeof(buf) && n < targ_width; i++) { int ch = (int) (d / t); buf[s + n++] = (char) (ch + '0'); d -= ch * t; t /= 10.0; } } + while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes - if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot + if (tz && n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot n += s; if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; buf[n] = '\0'; diff --git a/test/unit_test.c b/test/unit_test.c index 11216943..79e0d0f4 100644 --- a/test/unit_test.c +++ b/test/unit_test.c @@ -882,15 +882,15 @@ static void fprcb(struct mg_connection *c, int ev, void *ev_data) { struct mg_http_message *hm = (struct mg_http_message *) ev_data; int code = atoi(hm->uri.buf); if (code == 200) { - snprintf(fd->buf + fd->len, FETCH_BUF_SIZE - (unsigned int) fd->len, "%.*s", (int) hm->message.len, - hm->message.buf); + snprintf(fd->buf + fd->len, FETCH_BUF_SIZE - (unsigned int) fd->len, + "%.*s", (int) hm->message.len, hm->message.buf); fd->len += (int) hm->message.len; ++fd->reqs; - if(fd->reqs == 2) { + if (fd->reqs == 2) { fd->closed = 1; c->is_closing = 1; } - } else { // allow testing for other codes and catching wrong responses + } else { // allow testing for other codes and catching wrong responses MG_INFO(("reqs: %d, code: %d", fd->reqs, code)); fd->reqs += code; } @@ -900,8 +900,8 @@ static void fprcb(struct mg_connection *c, int ev, void *ev_data) { } } -static int fpr(struct mg_mgr *mgr, char *buf, const char *url, - const char *fmt, ...) { +static int fpr(struct mg_mgr *mgr, char *buf, const char *url, const char *fmt, + ...) { struct fpr_data fd = {buf, 0, 0, 0}; int i; struct mg_connection *c = NULL; @@ -941,12 +941,17 @@ static void test_http_server(void) { ASSERT(fetch(&mgr, buf, url, "GET /a.txt HTTP/1.0\nA B\n\n") == 0); // Pipelined requests - ASSERT(fpr(&mgr, buf, url, "GET /foo/bar HTTP/1.1\n\nGET /foo/foobar HTTP/1.1\n\n") == 2); + ASSERT(fpr(&mgr, buf, url, + "GET /foo/bar HTTP/1.1\n\nGET /foo/foobar HTTP/1.1\n\n") == 2); // Pipelined requests with file requests other than the last one (see #2796) - ASSERT(fpr(&mgr, buf, url, "GET /a.txt HTTP/1.1\n\nGET /a.txt HTTP/1.1\n\n") == 2); - ASSERT(fpr(&mgr, buf, url, "HEAD /a.txt HTTP/1.1\n\nGET /a.txt HTTP/1.1\n\n") == 2); + ASSERT(fpr(&mgr, buf, url, + "GET /a.txt HTTP/1.1\n\nGET /a.txt HTTP/1.1\n\n") == 2); + ASSERT(fpr(&mgr, buf, url, + "HEAD /a.txt HTTP/1.1\n\nGET /a.txt HTTP/1.1\n\n") == 2); // Connection: close - ASSERT(fpr(&mgr, buf, url, "GET /foo/bar HTTP/1.1\nConnection: close\n\nGET /foo/foobar HTTP/1.1\n\n") == 1); + ASSERT(fpr(&mgr, buf, url, + "GET /foo/bar HTTP/1.1\nConnection: close\n\nGET /foo/foobar " + "HTTP/1.1\n\n") == 1); ASSERT(cmpbody(buf, "uri: bar") == 0); // Responses with missing reason phrase must also work @@ -1661,7 +1666,9 @@ static void test_http_parse(void) { ASSERT(mg_http_parse(s, strlen(s), &req) == (int) strlen(s)); ASSERT((v = mg_http_get_header(&req, "e")) != NULL); ASSERT(vcmp(*v, "5")); - ASSERT((v = mg_http_get_header(&req, "h")) == NULL); + ASSERT((v = mg_http_get_header(&req, "g")) != NULL); + ASSERT(vcmp(*v, "7")); + ASSERT((v = mg_http_get_header(&req, "h")) == NULL); // MG_MAX_HTTP_HEADERS } { @@ -2108,6 +2115,16 @@ static void test_str(void) { if (!is_windows) ASSERT(strcmp(t1, t2) == 0); \ } while (0) +#define TESTDOUBLE_NOHOSTCHECK(fmt_, num_, res_) \ + do { \ + char t1[40] = "", t2[40] = ""; \ + const char *N = #num_; \ + mg_snprintf(t1, sizeof(t1), fmt_, num_); \ + snprintf(t2, sizeof(t2), fmt_, num_); \ + printf("[%s,%s] : [%s] [%s] [%s]\n", fmt_, N, res_, t2, t1); \ + ASSERT(strcmp(t1, res_) == 0); \ + } while (0) + TESTDOUBLE("%g", 0.0, "0"); TESTDOUBLE("%g", 0.123, "0.123"); TESTDOUBLE("%g", 0.00123, "0.00123"); @@ -2153,11 +2170,28 @@ static void test_str(void) { TESTDOUBLE("%.*f", DBLWIDTH(1, 0.14), "0.1"); TESTDOUBLE("%.*f", DBLWIDTH(1, 0.19), "0.2"); TESTDOUBLE("%.*f", DBLWIDTH(1, 0.16), "0.2"); - //TESTDOUBLE("%.1f", 0.15, "0.1"); - //TESTDOUBLE("%.3f", 123.123456789, "123.123"); - //TESTDOUBLE("%.4f", 123.123456789, "123.1235"); - //TESTDOUBLE("%.5f", 123.1234567, "123.12346"); - //TESTDOUBLE("%.*f", DBLWIDTH(1, 0.15), "0.1"); + TESTDOUBLE_NOHOSTCHECK("%.1f", 0.15, "0.2"); + TESTDOUBLE("%.3f", 123.123456789, "123.123"); + TESTDOUBLE("%.4f", 123.123456789, "123.1235"); + TESTDOUBLE("%.5f", 123.1234567, "123.12346"); + TESTDOUBLE_NOHOSTCHECK("%.*f", DBLWIDTH(1, 0.15), "0.2"); + + TESTDOUBLE("%.1f", 1.5, "1.5"); + + TESTDOUBLE("%.3f", 500.0, "500.000"); + TESTDOUBLE("%.1f", 1.15, "1.1"); + TESTDOUBLE("%.1f", 0.155, "0.2"); + TESTDOUBLE("%.1f", 1.155, "1.2"); + TESTDOUBLE("%.1f", 0.155, "0.2"); + TESTDOUBLE("%.2f", 0.015, "0.01"); + TESTDOUBLE("%.2f", 0.0015, "0.00"); + TESTDOUBLE("%.1f", 0.155, "0.2"); + + TESTDOUBLE("%.3f", 13.12505, "13.125"); + TESTDOUBLE("%.3f", 15.1255, "15.126"); + TESTDOUBLE("%.3f", 19.1255, "19.125"); + TESTDOUBLE("%.4f", 100.15, "100.1500"); + TESTDOUBLE("%.2f", 5.55, "5.55"); #ifndef _WIN32 TESTDOUBLE("%g", (double) INFINITY, "inf"); @@ -2381,7 +2415,8 @@ static void test_util(void) { ASSERT(mg_url_decode("&&&a=%", 6, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("a=%1", 4, buf, sizeof(buf), 0) < 0); ASSERT(mg_url_decode("a=%12", 5, buf, sizeof(buf), 0) == 3 && buf[2] == 0x12); - ASSERT(mg_url_decode("a=%123", 6, buf, sizeof(buf), 0) == 4 && buf[2] == 0x12 && buf[3] == '3'); + ASSERT(mg_url_decode("a=%123", 6, buf, sizeof(buf), 0) == 4 && + buf[2] == 0x12 && buf[3] == '3'); memset(a.ip, 0xaa, sizeof(a.ip)); ASSERT(mg_aton(mg_str("::1%1"), &a) == true); @@ -2439,64 +2474,94 @@ static void test_util(void) { { uint64_t val, max = (uint64_t) -1; ASSERT(mg_str_to_num(mg_str("0"), 10, &val, sizeof(uint64_t)) && val == 0); - ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint64_t)) && val == 123); + ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint64_t)) && + val == 123); ASSERT(mg_str_to_num(mg_str(" 123"), 10, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str("123 "), 10, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str(""), 10, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str(" 123x"), 10, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str("-"), 10, &val, sizeof(uint64_t)) == false); mg_snprintf(buf, sizeof(buf), "%llu", max); - ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint64_t)) && val == max); + ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint64_t)) && + val == max); ASSERT(mg_str_to_num(mg_str("0"), 2, &val, sizeof(uint64_t)) && val == 0); ASSERT(mg_str_to_num(mg_str("1"), 2, &val, sizeof(uint64_t)) && val == 1); ASSERT(mg_str_to_num(mg_str("0123"), 2, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str("123"), 2, &val, sizeof(uint64_t)) == false); - ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint64_t)) && val == 123); - ASSERT(mg_str_to_num(mg_str("1111111111111111111111111111111111111111111111111111111111111111"), 2, &val, sizeof(uint64_t)) && val == max); + ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint64_t)) && + val == 123); + ASSERT(mg_str_to_num(mg_str("1111111111111111111111111111111111111111111111" + "111111111111111111"), + 2, &val, sizeof(uint64_t)) && + val == max); ASSERT(mg_str_to_num(mg_str("0"), 16, &val, sizeof(uint64_t)) && val == 0); - ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint64_t)) && val == 0x123); - ASSERT(mg_str_to_num(mg_str("def"), 16, &val, sizeof(uint64_t)) && val == 0xdef); + ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint64_t)) && + val == 0x123); + ASSERT(mg_str_to_num(mg_str("def"), 16, &val, sizeof(uint64_t)) && + val == 0xdef); ASSERT(mg_str_to_num(mg_str("defg"), 16, &val, sizeof(uint64_t)) == false); mg_snprintf(buf, sizeof(buf), "%llx", max); - ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint64_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("0x123"), 0, &val, sizeof(uint64_t)) && val == 0x123); + ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint64_t)) && + val == max); + ASSERT(mg_str_to_num(mg_str("0x123"), 0, &val, sizeof(uint64_t)) && + val == 0x123); ASSERT(mg_str_to_num(mg_str("0b123"), 0, &val, sizeof(uint64_t)) == false); ASSERT(mg_str_to_num(mg_str("0c123"), 0, &val, sizeof(uint64_t)) == false); - ASSERT(mg_str_to_num(mg_str("0b101"), 0, &val, sizeof(uint64_t)) && val == 5); - ASSERT(mg_str_to_num(mg_str("0123"), 0, &val, sizeof(uint64_t)) && val == 123); + ASSERT(mg_str_to_num(mg_str("0b101"), 0, &val, sizeof(uint64_t)) && + val == 5); + ASSERT(mg_str_to_num(mg_str("0123"), 0, &val, sizeof(uint64_t)) && + val == 123); } { uint32_t val, max = (uint32_t) -1; - ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint32_t)) && val == 123); + ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint32_t)) && + val == 123); mg_snprintf(buf, sizeof(buf), "%lu", (unsigned long) max); ASSERT(strcmp(buf, "4294967295") == 0); - ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint32_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint32_t)) && val == 123); - ASSERT(mg_str_to_num(mg_str("11111111111111111111111111111111"), 2, &val, sizeof(uint32_t)) && val == max); + ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint32_t)) && + val == max); + ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint32_t)) && + val == 123); + ASSERT(mg_str_to_num(mg_str("11111111111111111111111111111111"), 2, &val, + sizeof(uint32_t)) && + val == max); ASSERT(mg_str_to_num(mg_str("0"), 16, &val, sizeof(uint32_t)) && val == 0); - ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint32_t)) && val == 0x123); + ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint32_t)) && + val == 0x123); mg_snprintf(buf, sizeof(buf), "%lx", max); - ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint32_t)) && val == max); + ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint32_t)) && + val == max); } { uint16_t val, max = (uint16_t) -1; - ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint16_t)) && val == 123); + ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint16_t)) && + val == 123); mg_snprintf(buf, sizeof(buf), "%u", max); - ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint16_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint16_t)) && val == 123); - ASSERT(mg_str_to_num(mg_str("1111111111111111"), 2, &val, sizeof(uint16_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint16_t)) && val == 0x123); + ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint16_t)) && + val == max); + ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint16_t)) && + val == 123); + ASSERT( + mg_str_to_num(mg_str("1111111111111111"), 2, &val, sizeof(uint16_t)) && + val == max); + ASSERT(mg_str_to_num(mg_str("123"), 16, &val, sizeof(uint16_t)) && + val == 0x123); mg_snprintf(buf, sizeof(buf), "%x", max); - ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint16_t)) && val == max); + ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint16_t)) && + val == max); } { uint8_t val, max = (uint8_t) -1; - ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint8_t)) && val == 123); + ASSERT(mg_str_to_num(mg_str("123"), 10, &val, sizeof(uint8_t)) && + val == 123); mg_snprintf(buf, sizeof(buf), "%u", max); ASSERT(mg_str_to_num(mg_str(buf), 10, &val, sizeof(uint8_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint8_t)) && val == 123); - ASSERT(mg_str_to_num(mg_str("11111111"), 2, &val, sizeof(uint8_t)) && val == max); - ASSERT(mg_str_to_num(mg_str("12"), 16, &val, sizeof(uint8_t)) && val == 0x12); + ASSERT(mg_str_to_num(mg_str("01111011"), 2, &val, sizeof(uint8_t)) && + val == 123); + ASSERT(mg_str_to_num(mg_str("11111111"), 2, &val, sizeof(uint8_t)) && + val == max); + ASSERT(mg_str_to_num(mg_str("12"), 16, &val, sizeof(uint8_t)) && + val == 0x12); mg_snprintf(buf, sizeof(buf), "%x", max); ASSERT(mg_str_to_num(mg_str(buf), 16, &val, sizeof(uint8_t)) && val == max); } @@ -3098,12 +3163,14 @@ static void test_json(void) { ASSERT(mg_json_get(json, "$.c", &n) == 19 && n == 1); { - char to[4], expect[4] = {0,0,0,0}; + char to[4], expect[4] = {0, 0, 0, 0}; memset(to, 0, sizeof(to)); - ASSERT(mg_json_unescape(mg_str("\\u0000"), to, 4) && memcmp(to, expect, 4) == 0); + ASSERT(mg_json_unescape(mg_str("\\u0000"), to, 4) && + memcmp(to, expect, 4) == 0); to[0] = 0; expect[0] = (char) 0xff; - ASSERT(mg_json_unescape(mg_str("\\u00ff"), to, 4) && memcmp(to, expect, 4) == 0); + ASSERT(mg_json_unescape(mg_str("\\u00ff"), to, 4) && + memcmp(to, expect, 4) == 0); ASSERT(!mg_json_unescape(mg_str("\\u0100"), to, 4)); ASSERT(!mg_json_unescape(mg_str("\\u1000"), to, 4)); }