Fix a number of thinkos in human-readable file size formatting.
* subversion/svn/filesize.c
(format_size): Be smarter about predicting floating-point rounding to
decide whether to show decimal places or not.
(get_base2_unit_file_size): Fix human-readable size order calculation.
(get_base10_unit_file_size): Likewise.
* build.conf (filesize-test): New test suite.
(private-includes): Add subversion/svn/filesize.c for filesize-test.
* subversion/tests/client/filesize-test.c: New.
Found by: Tobias Bading <tbading{_AT_}web.de>
Patch by: me
git-svn-id: https://svn.apache.org/repos/asf/subversion/trunk@1878909 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/build.conf b/build.conf
index c8a2305..f489ab2 100644
--- a/build.conf
+++ b/build.conf
@@ -53,6 +53,7 @@
subversion/libsvn_subr/utf8proc/utf8proc_internal.h
subversion/libsvn_subr/utf8proc/utf8proc.c
subversion/libsvn_subr/utf8proc/utf8proc_data.c
+ subversion/svn/filesize.c
private-built-includes =
subversion/svn_private_config.h
subversion/libsvn_fs_fs/rep-cache-db.h
@@ -1339,6 +1340,18 @@
msvc-force-static = yes
# ----------------------------------------------------------------------------
+# Tests for the client's internal functions
+
+[filesize-test]
+description = Test the storage of tree conflict data
+type = exe
+path = subversion/tests/client
+sources = filesize-test.c
+install = test
+libs = libsvn_client libsvn_test libsvn_wc libsvn_subr apriconv apr
+msvc-force-static = yes
+
+# ----------------------------------------------------------------------------
# These are not unit tests at all, they are small programs that exercise
# parts of the libsvn_delta API from the command line. They are stuck here
# because of some historical association with the test-suite, but should
diff --git a/subversion/svn/filesize.c b/subversion/svn/filesize.c
index ba1c356..c6e2bc4 100644
--- a/subversion/svn/filesize.c
+++ b/subversion/svn/filesize.c
@@ -88,15 +88,24 @@
+ 1 nul terminator
---
= 5 characters of space needed in the buffer. */
- char buffer[8];
+ char buffer[64];
- assert(absolute_human_readable_size < 1000.0);
+ assert(absolute_human_readable_size < 1000);
- /* When the adjusted size has only one significant digit left of the
- decimal point, show tenths of a unit, too. */
- sprintf(buffer, "%.*f",
- absolute_human_readable_size < 10.0 ? 1 : 0,
- human_readable_size);
+ /* When the adjusted size has only one significant digit left of
+ the decimal point, show tenths of a unit, too. Except when
+ the absolute size is actually a single-digit number, because
+ files can't have fractional byte sizes. */
+ if (absolute_human_readable_size >= 10)
+ sprintf(buffer, "%.0f", human_readable_size);
+ else
+ {
+ double integral;
+ const double frac = modf(absolute_human_readable_size, &integral);
+ const int decimals = (index > 0 && (integral < 9 || frac <= .949999999));
+ sprintf(buffer, "%.*f", decimals, human_readable_size);
+ }
+
return apr_pstrcat(result_pool, buffer, suffix, SVN_VA_NULL);
}
@@ -138,8 +147,9 @@
assert(index < order_size - 1);
++index;
}
+
human_readable_size = (index == 0 ? (double)size
- : (size >> 3 * index) / 128.0 / index);
+ : (size >> (10 * index - 10)) / 1024.0);
return format_size(human_readable_size,
long_units, order, index, result_pool);
@@ -160,7 +170,7 @@
{APR_INT64_C( 999999999999), " TB", "T"}, /* tera */
{APR_INT64_C( 999999999999999), " EB", "E"}, /* exa */
{APR_INT64_C(999999999999999999), " PB", "P"} /* peta */
- /* 18446744073709551615 is the maximum value. */
+ /* 9223372036854775807 is the maximum value. */
};
static const apr_size_t order_size = sizeof(order) / sizeof(order[0]);
@@ -170,20 +180,29 @@
/* Adjust the size to the given order of magnitude.
- This is division by (order[index].mask + 1), which is the base-1000
- magnitude of the size. For large file sizes, we split the operation
- into an integer and a floating-point division, so that we don't
+ This is division by (order[index].mask + 1), which is the
+ base-1000 magnitude of the size. We split the operation into an
+ integer and a floating-point division, so that we don't
overflow the mantissa. */
if (index == 0)
human_readable_size = (double)size;
- else if (index <= 3)
- human_readable_size = (double)size / (order[index].mask + 1);
else
{
- /* [ Keep integer division here! ] */
- const double divisor = (double)((order[index].mask + 1) / 1000000);
- human_readable_size = (size / 1000000) / divisor;
- /* [ And here! ] */
+ const svn_filesize_t divisor = (order[index - 1].mask + 1);
+ /* [Keep integer arithmetic here!] */
+ human_readable_size = (size / divisor) / 1000.0;
+ }
+
+ /* Adjust index and number for rounding. */
+ if (human_readable_size >= 999.5)
+ {
+ /* This assertion should never fail, because we only have one
+ decimal digit in the petabyte range and so the number of
+ petabytes can't be large enough to cause the program flow
+ to enter this conditional block. */
+ assert(index < order_size - 1);
+ human_readable_size /= 1000.0;
+ ++index;
}
return format_size(human_readable_size,
diff --git a/subversion/tests/client/filesize-test.c b/subversion/tests/client/filesize-test.c
new file mode 100644
index 0000000..1e6a977
--- /dev/null
+++ b/subversion/tests/client/filesize-test.c
@@ -0,0 +1,131 @@
+/* filesize-test.c --- tests for svn_cl__format_file_size
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include "../../svn/filesize.c"
+
+#include "../svn_test.h"
+
+typedef struct test_data_t
+{
+ svn_filesize_t size;
+ const char* result;
+} test_data_t;
+
+
+static svn_error_t *
+test_base2_file_size(apr_pool_t *pool)
+{
+ static const test_data_t data[] =
+ {
+ {APR_INT64_C( 1), "1 B"},
+ {APR_INT64_C( 9), "9 B"},
+ {APR_INT64_C( 13), "13 B"},
+ {APR_INT64_C( 999), "999 B"},
+ {APR_INT64_C( 1000), "1.0 KiB"},
+ {APR_INT64_C( 1024), "1.0 KiB"},
+ {APR_INT64_C( 3000), "2.9 KiB"},
+ {APR_INT64_C( 1000000), "977 KiB"},
+ {APR_INT64_C( 1048576), "1.0 MiB"},
+ {APR_INT64_C( 1000000000), "954 MiB"},
+ {APR_INT64_C( 1000000000000), "931 GiB"},
+ {APR_INT64_C( 1000000000000000), "909 TiB"},
+ {APR_INT64_C(1000000000000000000), "888 EiB"},
+ {APR_INT64_C(9223372036854775807), "8.0 PiB"},
+ };
+ static const apr_size_t data_size = sizeof(data) / sizeof(data[0]);
+
+ apr_size_t index;
+ for (index = 0; index < data_size; ++index)
+ {
+ const char *result;
+ SVN_ERR(svn_cl__format_file_size(&result, data[index].size,
+ SVN_CL__SIZE_UNIT_BASE_2,
+ TRUE, pool));
+ SVN_TEST_STRING_ASSERT(result, data[index].result);
+ /* fprintf(stderr, "%s\t%" APR_INT64_T_FMT "\n", result, data[index].size); */
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+test_base10_file_size(apr_pool_t *pool)
+{
+ static const test_data_t data[] =
+ {
+ {APR_INT64_C( 1), "1 B"},
+ {APR_INT64_C( 9), "9 B"},
+ {APR_INT64_C( 13), "13 B"},
+ {APR_INT64_C( 999), "999 B"},
+ {APR_INT64_C( 1000), "1.0 kB"},
+ {APR_INT64_C( 3000), "3.0 kB"},
+ {APR_INT64_C( 999499), "999 kB"},
+ {APR_INT64_C( 999501), "1.0 MB"},
+ {APR_INT64_C( 1000000), "1.0 MB"},
+ {APR_INT64_C( 9900000), "9.9 MB"},
+ {APR_INT64_C( 9950001), "10 MB"},
+ {APR_INT64_C( 99400001), "99 MB"},
+ {APR_INT64_C( 99500001), "100 MB"},
+ {APR_INT64_C( 999444444), "999 MB"},
+ {APR_INT64_C( 999999999), "1.0 GB"},
+ {APR_INT64_C( 1000000000), "1.0 GB"},
+ {APR_INT64_C( 1100000000), "1.1 GB"},
+ {APR_INT64_C( 1000000000000), "1.0 TB"},
+ {APR_INT64_C( 1000000000000000), "1.0 EB"},
+ {APR_INT64_C( 999000000000000000), "999 EB"},
+ {APR_INT64_C( 999500000000000000), "1.0 PB"},
+ {APR_INT64_C(1000000000000000000), "1.0 PB"},
+ {APR_INT64_C(1090000000000000000), "1.1 PB"},
+ {APR_INT64_C(9223372036854775807), "9.2 PB"},
+ };
+ static const apr_size_t data_size = sizeof(data) / sizeof(data[0]);
+
+ apr_size_t index;
+ for (index = 0; index < data_size; ++index)
+ {
+ const char *result;
+ SVN_ERR(svn_cl__format_file_size(&result, data[index].size,
+ SVN_CL__SIZE_UNIT_BASE_10,
+ TRUE, pool));
+ SVN_TEST_STRING_ASSERT(result, data[index].result);
+ /* fprintf(stderr, "%s\t%" APR_INT64_T_FMT "\n", result, data[index].size); */
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* The test table. */
+
+static int max_threads = 3;
+
+static struct svn_test_descriptor_t test_funcs[] =
+ {
+ SVN_TEST_NULL,
+ SVN_TEST_PASS2(test_base2_file_size,
+ "base-2 human-friendly file size"),
+ SVN_TEST_PASS2(test_base10_file_size,
+ "base-10 human-friendly file size"),
+ SVN_TEST_NULL
+ };
+
+SVN_TEST_MAIN