Browse Source

print_number: Make locale independent

This first prints the number into a temporary buffer and then copies the
number to the output.

A positive side effect is that cJSON no longer reserves more space for
the number in the output than is necessary.
Max Bruckner 8 years ago
parent
commit
c08f7e1d29
2 changed files with 51 additions and 46 deletions
  1. 46 35
      cJSON.c
  2. 5 11
      tests/print_number.c

+ 46 - 35
cJSON.c

@@ -381,34 +381,24 @@ static void update_offset(printbuffer * const buffer)
 }
 
 /* Removes trailing zeroes from the end of a printed number */
-static cJSON_bool trim_trailing_zeroes(printbuffer * const buffer)
+static int trim_trailing_zeroes(const unsigned char * const number, int length, const unsigned char decimal_point)
 {
-    size_t offset = 0;
-    unsigned char *content = NULL;
-
-    if ((buffer == NULL) || (buffer->buffer == NULL) || (buffer->offset < 1))
+    if ((number == NULL) || (length <= 0))
     {
-        return false;
+        return -1;
     }
 
-    offset = buffer->offset - 1;
-    content = buffer->buffer;
-
-    while ((offset > 0) && (content[offset] == '0'))
+    while ((length > 0) && (number[length - 1] == '0'))
     {
-        offset--;
+        length--;
     }
-    if ((offset > 0) && (content[offset] == '.'))
+    if ((length > 0) && (number[length - 1] == decimal_point))
     {
-        offset--;
+        /* remove trailing decimal_point */
+        length--;
     }
 
-    offset++;
-    content[offset] = '\0';
-
-    buffer->offset = offset;
-
-    return true;
+    return length;
 }
 
 /* Render the number nicely from the given item into a string. */
@@ -417,54 +407,75 @@ static cJSON_bool print_number(const cJSON * const item, printbuffer * const out
     unsigned char *output_pointer = NULL;
     double d = item->valuedouble;
     int length = 0;
+    size_t i = 0;
     cJSON_bool trim_zeroes = true; /* should zeroes at the end be removed? */
+    unsigned char number_buffer[64]; /* temporary buffer to print the number into */
+    unsigned char decimal_point = get_decimal_point();
 
     if (output_buffer == NULL)
     {
         return false;
     }
 
-    /* This is a nice tradeoff. */
-    output_pointer = ensure(output_buffer, 64, hooks);
-    if (output_pointer == NULL)
-    {
-        return false;
-    }
-
     /* This checks for NaN and Infinity */
     if ((d * 0) != 0)
     {
-        length = sprintf((char*)output_pointer, "null");
+        length = sprintf((char*)number_buffer, "null");
     }
     else if ((fabs(floor(d) - d) <= DBL_EPSILON) && (fabs(d) < 1.0e60))
     {
         /* integer */
-        length = sprintf((char*)output_pointer, "%.0f", d);
+        length = sprintf((char*)number_buffer, "%.0f", d);
         trim_zeroes = false; /* don't remove zeroes for "big integers" */
     }
     else if ((fabs(d) < 1.0e-6) || (fabs(d) > 1.0e9))
     {
-        length = sprintf((char*)output_pointer, "%e", d);
+        length = sprintf((char*)number_buffer, "%e", d);
         trim_zeroes = false; /* don't remove zeroes in engineering notation */
     }
     else
     {
-        length = sprintf((char*)output_pointer, "%f", d);
+        length = sprintf((char*)number_buffer, "%f", d);
     }
 
-    /* sprintf failed */
-    if (length < 0)
+    /* sprintf failed or buffer overrun occured */
+    if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
     {
         return false;
     }
 
-    output_buffer->offset += (size_t)length;
-
     if (trim_zeroes)
     {
-        return trim_trailing_zeroes(output_buffer);
+        length = trim_trailing_zeroes(number_buffer, length, decimal_point);
+        if (length <= 0)
+        {
+            return false;
+        }
+    }
+
+    /* reserve appropriate space in the output */
+    output_pointer = ensure(output_buffer, (size_t)length, hooks);
+    if (output_pointer == NULL)
+    {
+        return false;
     }
 
+    /* copy the printed number to the output and replace locale
+     * dependent decimal point with '.' */
+    for (i = 0; i < ((size_t)length); i++)
+    {
+        if (number_buffer[i] == decimal_point)
+        {
+            output_pointer[i] = '.';
+            continue;
+        }
+
+        output_pointer[i] = number_buffer[i];
+    }
+    output_pointer[i] = '\0';
+
+    output_buffer->offset += (size_t)length;
+
     return true;
 }
 

+ 5 - 11
tests/print_number.c

@@ -89,17 +89,11 @@ static void print_number_should_print_non_number(void)
 
 static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
 {
-    printbuffer buffer;
-    unsigned char number[100];
-    buffer.length = sizeof(number);
-    buffer.buffer = number;
-
-    strcpy((char*)number, "10.00");
-    buffer.offset = sizeof("10.00") - 1;
-    TEST_ASSERT_TRUE(trim_trailing_zeroes(&buffer));
-    TEST_ASSERT_EQUAL_UINT8('\0', buffer.buffer[buffer.offset]);
-    TEST_ASSERT_EQUAL_STRING("10", number);
-    TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
+    TEST_ASSERT_EQUAL_INT(2, trim_trailing_zeroes((const unsigned char*)"10.00", (int)(sizeof("10.00") - 1), '.'));
+    TEST_ASSERT_EQUAL_INT(0, trim_trailing_zeroes((const unsigned char*)".00", (int)(sizeof(".00") - 1), '.'));
+    TEST_ASSERT_EQUAL_INT(0, trim_trailing_zeroes((const unsigned char*)"00", (int)(sizeof("00") - 1), '.'));
+    TEST_ASSERT_EQUAL_INT(-1, trim_trailing_zeroes(NULL, 10, '.'));
+    TEST_ASSERT_EQUAL_INT(-1, trim_trailing_zeroes((const unsigned char*)"", 0, '.'));
 }
 
 int main(void)