jdk-24/test/hotspot/gtest/utilities/test_unsigned5.cpp
Xue-Lei Andrew Fan 38cfc59172 8299378: sprintf is deprecated in Xcode 14
Reviewed-by: kbarrett, dholmes
2023-01-03 22:44:14 +00:00

305 lines
10 KiB
C++

/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#include "precompiled.hpp"
#include "memory/allocation.hpp"
#include "runtime/os.hpp"
#include "utilities/unsigned5.hpp"
#include "unittest.hpp"
TEST_VM(unsigned5, max_encoded_in_length) {
int maxlen = UNSIGNED5::MAX_LENGTH;
EXPECT_EQ(maxlen, 5);
for (int i = 0; i <= 190; i++) {
uint32_t interesting = i;
EXPECT_EQ(UNSIGNED5::encoded_length(interesting), 1);
EXPECT_EQ(UNSIGNED5::encoded_length(~interesting), maxlen);
}
for (int len = 1; len <= maxlen; len++) {
uint32_t interesting = UNSIGNED5::max_encoded_in_length(len);
EXPECT_EQ(UNSIGNED5::encoded_length(interesting-1), len);
EXPECT_EQ(UNSIGNED5::encoded_length(interesting), len);
if (len < 5) {
EXPECT_EQ(UNSIGNED5::encoded_length(interesting+1), len+1);
EXPECT_EQ(UNSIGNED5::encoded_length(interesting*2), len+1);
}
const int offset = -123;
const int good_limit = offset + len;
const int bad_limit = good_limit - 1;
EXPECT_TRUE(UNSIGNED5::fits_in_limit(interesting, offset, good_limit));
EXPECT_TRUE(!UNSIGNED5::fits_in_limit(interesting, offset, bad_limit));
}
}
// Call FN on a nice list of "interesting" uint32_t values to encode/decode.
// For each length in [1..5], the maximum encodable value of that
// length is "interesting", as are one more and one less than that
// value. For each nybble (aligned 4-bit field) of a uint32_t, each
// possible value (in [0..15]) stored in that nybble is "interesting".
// Also "interesting" are some other values created by perturbing
// lower bits of that nybble-bearing number, by subtracting a power
// of -7 (up to -7^7). That makes just over 1000 distinct numbers.
//
// Calls to this function are repeatable, so you can call it to pack
// an output array, and then call it again to read an input array
// verifying that the retrieved values match the stored ones.
template<typename FN>
inline int enumerate_cases(FN fn) {
// boundary values around the maximum encoded in each byte-length
for (int len = 1; len <= 5; len++) {
uint32_t interesting = UNSIGNED5::max_encoded_in_length(len);
int res = fn(interesting-1);
if (res) return res;
res = fn(interesting);
if (res) return res;
if (interesting < (uint32_t)-1) {
res = fn(interesting+1);
if (res) return res;
}
}
// for each nybble, for each value in the nybble
for (uint32_t npos = 0; npos < 32; npos += 4) {
for (uint32_t nval = 0; nval <= 15; nval++) {
uint32_t interesting = nval << npos;
int res = fn(interesting);
if (res) return res;
// mix in some crazy-looking values: powers of -7 to -7^7
for (int pon7 = 1; pon7 < 1000000; pon7 *= -7) {
uint32_t interesting2 = interesting - pon7;
res = fn(interesting2);
if (res) return res;
}
}
}
return 0;
}
TEST_VM(unsigned5, transcode_single) {
const int limit = UNSIGNED5::MAX_LENGTH;
u_char buffer[limit + 1];
auto each_case = [&](uint32_t value) -> uint32_t {
//printf("case %08X len=%d\n", value, UNSIGNED5::encoded_length(value));
int offset = 0;
UNSIGNED5::write_uint(value, buffer, offset, limit);
int length = offset;
EXPECT_TRUE(length <= UNSIGNED5::MAX_LENGTH);
EXPECT_EQ(length, UNSIGNED5::encoded_length(value)) << "for value=" << value;
buffer[length] = 0;
offset = 0;
uint32_t check = UNSIGNED5::read_uint(buffer, offset, limit);
EXPECT_EQ(offset, length) << "for value=" << value;
EXPECT_EQ(value, check);
return 0;
};
auto z = enumerate_cases(each_case);
EXPECT_TRUE(!z);
}
static int count_cases() {
int case_count = 0;
auto inc_case_count = [&](uint32_t){ ++case_count; return 0; };
enumerate_cases(inc_case_count);
return case_count;
}
TEST_VM(unsigned5, transcode_multiple) {
int case_count = count_cases();
const int limit = 200;
ASSERT_TRUE(limit < case_count*UNSIGNED5::MAX_LENGTH);
u_char buffer[limit + 1];
//printf("%d cases total\n", case_count); //1166 cases total
for (int sublimit = limit - 20; sublimit < limit; sublimit++) {
int offset = 0;
int count = 0;
// write each number into an array
auto write_case = [&](uint32_t value) -> uint32_t {
if (!UNSIGNED5::fits_in_limit(value, offset, sublimit))
return value|1;
UNSIGNED5::write_uint(value, buffer, offset, sublimit);
count++;
return 0;
};
auto done = enumerate_cases(write_case);
EXPECT_TRUE(done) << "must have hit the sublimit";
EXPECT_TRUE(count < case_count);
int length = offset;
EXPECT_TRUE(length <= sublimit && length + UNSIGNED5::MAX_LENGTH > sublimit)
<< "length=" << length << " sublimit=" << sublimit;
for (int i = length; i <= sublimit; i++) {
buffer[i] = 0;
}
if (sublimit == limit-1) {
UNSIGNED5::print_count(case_count + 1, &buffer[0], sublimit);
}
//printf("encoded %d values in %d bytes: [[%s]]\n", count, length, buffer);
// now read it all back
offset = 0;
int count2 = 0;
auto read_back_case = [&](uint32_t value) -> uint32_t {
int clen = UNSIGNED5::check_length(buffer, offset, sublimit);
if (clen == 0) return value|1;
EXPECT_EQ(clen, UNSIGNED5::encoded_length(value));
int begin = offset;
uint32_t check = UNSIGNED5::read_uint(buffer, offset, sublimit);
EXPECT_EQ(offset, begin + clen);
EXPECT_EQ(value, check);
count2++;
return 0;
};
auto done2 = enumerate_cases(read_back_case);
EXPECT_EQ(done, done2);
EXPECT_EQ(count, count2);
EXPECT_EQ(offset, length);
}
}
inline void init_ints(int len, int* ints) {
for (int i = 0; i < len; i++) {
ints[i] = (i * ((i&2) ? i : 1001)) ^ -(i & 1);
}
}
struct MyReaderHelper {
uint8_t operator()(char* a, int i) const { return a[i]; }
};
using MyReader = UNSIGNED5::Reader<char*, int, MyReaderHelper>;
TEST_VM(unsigned5, reader) {
const int LEN = 100;
int ints[LEN];
init_ints(LEN, ints);
int i;
UNSIGNED5::Sizer<> szr;
for (i = 0; i < LEN; i++) {
szr.accept_uint(ints[i]);
}
//printf("count=%d, size=%d\n", szr.count(), szr.position());
char buf[LEN * UNSIGNED5::MAX_LENGTH + 1];
int buflen;
{
int pos = 0;
for (int i = 0; i < LEN; i++) {
UNSIGNED5::write_uint(ints[i], buf, pos, 0);
}
EXPECT_TRUE(pos+1 < (int)sizeof(buf)) << pos;
buflen = pos;
buf[buflen] = 0;
}
EXPECT_EQ(szr.position(), buflen);
MyReader r1(buf);
i = 0;
while (r1.has_next()) {
int x = r1.next_uint();
int y = ints[i++];
ASSERT_EQ(x, y) << i;
}
ASSERT_EQ(i, LEN);
MyReader r2(buf, buflen / 2);
i = 0;
while (r2.has_next()) {
int x = r2.next_uint();
int y = ints[i++];
ASSERT_EQ(x, y) << i;
}
ASSERT_TRUE(i < LEN);
// copy from reader to writer
UNSIGNED5::Reader<char*,int> r3(buf);
int array_limit = 1;
char* array = new char[array_limit + 1];
auto array_grow = [&](int){
array[array_limit] = 0;
auto oal = array_limit;
array_limit += 10;
//printf("growing array from %d to %d\n", oal, array_limit);
auto na = new char[array_limit + 1];
strcpy(na, array);
array = na;
};
UNSIGNED5::Writer<char*,int> w3(array, array_limit);
while (r3.has_next()) {
w3.accept_grow(r3.next_uint(), array_grow);
}
w3.end_byte(); // we always allocated one more than the limit!
std::string buf_s(buf, buflen);
std::string arr_s(array, strlen(array));
ASSERT_EQ(buf_s, arr_s);
// try printing:
{
char stbuf[1000];
stringStream st(stbuf, sizeof(stbuf)-1);
UNSIGNED5::Reader<char*,int> printer(buf);
printer.print_on(&st, 4, "(", ")");
std::string st_s(st.base(), st.size());
char buf2[sizeof(stbuf)];
os::snprintf_checked(buf2, sizeof(buf2), "(%d %d %d %d)", ints[0], ints[1], ints[2], ints[3]);
std::string exp_s(buf2, strlen(buf2));
ASSERT_EQ(exp_s, st_s);
}
}
// Here is some object code to look at if we want to do a manual
// study. One could find the build file named test_unsigned5.o.cmdline
// and hand-edit the command line to produce assembly code in
// test_unsigned5.s.
//
// Or, given the two empty "fence functions", one could do a
// quick scan like this:
//
// $ objdump -D $(find build/*release -name test_unsigned5.o) \
// | sed -n /start_code_quality/,/end_code_quality/p \
// | egrep -B10 bswap # or grep -B20 cfi_endproc
void start_code_quality_unsigned5() { }
uint32_t code_quality_max_encoded_in_length(int i) {
return UNSIGNED5::max_encoded_in_length(i); // should compile like 5-switch
}
int code_quality_encoded_length(uint32_t x) {
return UNSIGNED5::encoded_length(x); // should compile to 4-way comparison
}
int code_quality_check_length(char* a) {
return UNSIGNED5::check_length(a, 0); // should compile with fast-path
}
int code_quality_read_int(char* a) {
int i = 0;
return UNSIGNED5::read_uint(a, i, 0); // should compile with fast-path
}
int code_quality_int_reader(char* a) {
MyReader r1(a);
if (!r1.has_next()) return -1;
return r1.next_uint();
}
int code_quality_int_sizer(int* a, int n) {
UNSIGNED5::Sizer<> s;
for (int i = 0; i < n; i++) s.accept_uint(a[i]);
return s.position();
}
void end_code_quality_unsigned5() { }