1
0
mirror of https://github.com/postgres/postgres.git synced 2025-12-19 17:02:53 +03:00

Convert src/tools/testint128.c into a test module.

This creates a new test module src/test/modules/test_int128 and moves
src/tools/testint128.c into it so that it can be built using the
normal build system, allowing the 128-bit integer arithmetic functions
in src/include/common/int128.h to be tested automatically. For now,
the tests are skipped on platforms that don't have native int128
support.

While at it, fix the test128 union in the test code: the "hl" member
of test128 was incorrectly defined to be a union instead of a struct,
which meant that the tests were only ever setting and checking half of
each 128-bit integer value.

Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Discussion: https://postgr.es/m/CAEZATCWgBMc9ZwKMYqQpaQz2X6gaamYRB+RnMsUNcdMcL2Mj_w@mail.gmail.com
This commit is contained in:
Dean Rasheed
2025-08-06 09:41:11 +01:00
parent 225ebfe30a
commit 8c7445a008
8 changed files with 125 additions and 18 deletions

View File

@@ -25,6 +25,7 @@ SUBDIRS = \
test_escape \
test_extensions \
test_ginpostinglist \
test_int128 \
test_integerset \
test_json_parser \
test_lfind \

View File

@@ -24,6 +24,7 @@ subdir('test_dsm_registry')
subdir('test_escape')
subdir('test_extensions')
subdir('test_ginpostinglist')
subdir('test_int128')
subdir('test_integerset')
subdir('test_json_parser')
subdir('test_lfind')

View File

@@ -0,0 +1,2 @@
/tmp_check/
/test_int128

View File

@@ -0,0 +1,23 @@
# src/test/modules/test_int128/Makefile
PGFILEDESC = "test_int128 - test 128-bit integer arithmetic"
PROGRAM = test_int128
OBJS = $(WIN32RES) test_int128.o
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS_INTERNAL += $(libpq_pgport)
NO_INSTALL = 1
TAP_TESTS = 1
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = src/test/modules/test_int128
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

View File

@@ -0,0 +1,33 @@
# Copyright (c) 2025, PostgreSQL Global Development Group
test_int128_sources = files(
'test_int128.c',
)
if host_system == 'windows'
test_int128_sources += rc_bin_gen.process(win32ver_rc, extra_args: [
'--NAME', 'test_int128',
'--FILEDESC', 'test int128 program',])
endif
test_int128 = executable('test_int128',
test_int128_sources,
dependencies: [frontend_code, libpq],
kwargs: default_bin_args + {
'install': false,
},
)
testprep_targets += test_int128
tests += {
'name': 'test_int128',
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
'tests': [
't/001_test_int128.pl',
],
'deps': [test_int128],
},
}

View File

@@ -0,0 +1,27 @@
# Copyright (c) 2025, PostgreSQL Global Development Group
# Test 128-bit integer arithmetic code in int128.h
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Utils;
use Test::More;
# Run the test program with 1M iterations
my $exe = "test_int128";
my $size = 1_000_000;
note "testing executable $exe";
my ($stdout, $stderr) = run_command([ $exe, $size ]);
SKIP:
{
skip "no native int128 type", 2 if $stdout =~ /skipping tests/;
is($stdout, "", "test_int128: no stdout");
is($stderr, "", "test_int128: no stderr");
}
done_testing();

View File

@@ -0,0 +1,190 @@
/*-------------------------------------------------------------------------
*
* test_int128.c
* Testbed for roll-our-own 128-bit integer arithmetic.
*
* This is a standalone test program that compares the behavior of an
* implementation in int128.h to an (assumed correct) int128 native type.
*
* Copyright (c) 2017-2025, PostgreSQL Global Development Group
*
*
* IDENTIFICATION
* src/test/modules/test_int128/test_int128.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <time.h>
/* Require a native int128 type */
#ifdef HAVE_INT128
/*
* By default, we test the non-native implementation in int128.h; but
* by predefining USE_NATIVE_INT128 to 1, you can test the native
* implementation, just to be sure.
*/
#ifndef USE_NATIVE_INT128
#define USE_NATIVE_INT128 0
#endif
#include "common/int128.h"
#include "common/pg_prng.h"
/*
* We assume the parts of this union are laid out compatibly.
*/
typedef union
{
int128 i128;
INT128 I128;
struct
{
#ifdef WORDS_BIGENDIAN
int64 hi;
uint64 lo;
#else
uint64 lo;
int64 hi;
#endif
} hl;
} test128;
#define INT128_HEX_FORMAT "%016" PRIx64 "%016" PRIx64
/*
* Control version of comparator.
*/
static inline int
my_int128_compare(int128 x, int128 y)
{
if (x < y)
return -1;
if (x > y)
return 1;
return 0;
}
/*
* Main program.
*
* Generates a lot of random numbers and tests the implementation for each.
* The results should be reproducible, since we use a fixed PRNG seed.
*
* You can give a loop count if you don't like the default 1B iterations.
*/
int
main(int argc, char **argv)
{
long count;
pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
if (argc >= 2)
count = strtol(argv[1], NULL, 0);
else
count = 1000000000;
while (count-- > 0)
{
int64 x = pg_prng_uint64(&pg_global_prng_state);
int64 y = pg_prng_uint64(&pg_global_prng_state);
int64 z = pg_prng_uint64(&pg_global_prng_state);
test128 t1;
test128 t2;
/* check unsigned addition */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 += (int128) (uint64) z;
int128_add_uint64(&t2.I128, (uint64) z);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " + unsigned " PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check signed addition */
t1.hl.hi = x;
t1.hl.lo = y;
t2 = t1;
t1.i128 += (int128) z;
int128_add_int64(&t2.I128, z);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(INT128_HEX_FORMAT " + signed " PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check multiplication */
t1.i128 = (int128) x * (int128) y;
t2.hl.hi = t2.hl.lo = 0;
int128_add_int64_mul_int64(&t2.I128, x, y);
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf(PRIx64 " * " PRIx64 "\n", x, y);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check comparison */
t1.hl.hi = x;
t1.hl.lo = y;
t2.hl.hi = z;
t2.hl.lo = pg_prng_uint64(&pg_global_prng_state);
if (my_int128_compare(t1.i128, t2.i128) !=
int128_compare(t1.I128, t2.I128))
{
printf("comparison failure: %d vs %d\n",
my_int128_compare(t1.i128, t2.i128),
int128_compare(t1.I128, t2.I128));
printf("arg1 = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
/* check case with identical hi parts; above will hardly ever hit it */
t2.hl.hi = x;
if (my_int128_compare(t1.i128, t2.i128) !=
int128_compare(t1.I128, t2.I128))
{
printf("comparison failure: %d vs %d\n",
my_int128_compare(t1.i128, t2.i128),
int128_compare(t1.I128, t2.I128));
printf("arg1 = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
}
return 0;
}
#else /* ! HAVE_INT128 */
/*
* For now, do nothing if we don't have a native int128 type.
*/
int
main(int argc, char **argv)
{
printf("skipping tests: no native int128 type\n");
return 0;
}
#endif