/* * Copyright 2016 WebAssembly Community Group participants * * Licensed 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 #include #include #include "gtest/gtest.h" #include "wabt/literal.h" #define FOREACH_UINT32_MULTIPLIER 1 #define FOREACH_UINT32(bits) \ uint32_t last_bits = 0; \ uint32_t bits = shard; \ int last_top_byte = -1; \ for (; bits >= last_bits; \ last_bits = bits, bits += num_threads_ * FOREACH_UINT32_MULTIPLIER) #define LOG_COMPLETION(bits) \ if (shard == 0) { \ int top_byte = bits >> 24; \ if (top_byte != last_top_byte) { \ printf("value: 0x%08x (%d%%)\r", bits, \ static_cast(static_cast(bits) * 100 / UINT32_MAX)); \ fflush(stdout); \ last_top_byte = top_byte; \ } \ } #define LOG_DONE() \ if (shard == 0) { \ printf("done.\n"); \ fflush(stdout); \ } using namespace wabt; template T bit_cast(F value) { T result; memcpy(&result, &value, sizeof(result)); return result; } static bool is_infinity_or_nan(uint32_t float_bits) { return ((float_bits >> 23) & 0xff) == 0xff; } static bool is_infinity_or_nan(uint64_t double_bits) { return ((double_bits >> 52) & 0x7ff) == 0x7ff; } class ThreadedTest : public ::testing::Test { protected: static constexpr int kDefaultNumThreads = 2; virtual void SetUp() { num_threads_ = std::thread::hardware_concurrency(); if (num_threads_ == 0) num_threads_ = kDefaultNumThreads; } virtual void RunShard(int shard) = 0; void RunThreads() { std::vector threads; for (int i = 0; i < num_threads_; ++i) { threads.emplace_back(&ThreadedTest::RunShard, this, i); } for (std::thread& thread : threads) { thread.join(); } } int num_threads_; }; /* floats */ class AllFloatsParseTest : public ThreadedTest { protected: virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(bits) { LOG_COMPLETION(bits); if (is_infinity_or_nan(bits)) continue; float value = bit_cast(bits); int len = snprintf(buffer, sizeof(buffer), "%a", value); uint32_t me; ASSERT_EQ(Result::Ok, ParseFloat(LiteralType::Hexfloat, buffer, buffer + len, &me)); ASSERT_EQ(me, bits); } LOG_DONE(); } }; TEST_F(AllFloatsParseTest, Run) { RunThreads(); } class AllFloatsWriteTest : public ThreadedTest { protected: virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(bits) { LOG_COMPLETION(bits); if (is_infinity_or_nan(bits)) continue; WriteFloatHex(buffer, sizeof(buffer), bits); char* endptr; float them_float = strtof(buffer, &endptr); uint32_t them_bits = bit_cast(them_float); ASSERT_EQ(bits, them_bits); } LOG_DONE(); } }; TEST_F(AllFloatsWriteTest, Run) { RunThreads(); } class AllFloatsRoundtripTest : public ThreadedTest { protected: static LiteralType ClassifyFloat(uint32_t float_bits) { if (is_infinity_or_nan(float_bits)) { if (float_bits & 0x7fffff) { return LiteralType::Nan; } else { return LiteralType::Infinity; } } else { return LiteralType::Hexfloat; } } virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(bits) { LOG_COMPLETION(bits); WriteFloatHex(buffer, sizeof(buffer), bits); int len = strlen(buffer); uint32_t new_bits; ASSERT_EQ(Result::Ok, ParseFloat(ClassifyFloat(bits), buffer, buffer + len, &new_bits)); ASSERT_EQ(new_bits, bits); } LOG_DONE(); } }; TEST_F(AllFloatsRoundtripTest, Run) { RunThreads(); } /* doubles */ class ManyDoublesParseTest : public ThreadedTest { protected: virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(halfbits) { LOG_COMPLETION(halfbits); uint64_t bits = (static_cast(halfbits) << 32) | halfbits; if (is_infinity_or_nan(bits)) continue; double value = bit_cast(bits); int len = snprintf(buffer, sizeof(buffer), "%a", value); uint64_t me; ASSERT_EQ(Result::Ok, ParseDouble(LiteralType::Hexfloat, buffer, buffer + len, &me)); ASSERT_EQ(me, bits); } LOG_DONE(); } }; TEST_F(ManyDoublesParseTest, Run) { RunThreads(); } class ManyDoublesWriteTest : public ThreadedTest { protected: virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(halfbits) { LOG_COMPLETION(halfbits); uint64_t bits = (static_cast(halfbits) << 32) | halfbits; if (is_infinity_or_nan(bits)) continue; WriteDoubleHex(buffer, sizeof(buffer), bits); char* endptr; double them_double = strtod(buffer, &endptr); uint64_t them_bits = bit_cast(them_double); ASSERT_EQ(bits, them_bits); } LOG_DONE(); } }; TEST_F(ManyDoublesWriteTest, Run) { RunThreads(); } class ManyDoublesRoundtripTest : public ThreadedTest { protected: static LiteralType ClassifyDouble(uint64_t double_bits) { if (is_infinity_or_nan(double_bits)) { if (double_bits & 0xfffffffffffffULL) { return LiteralType::Nan; } else { return LiteralType::Infinity; } } else { return LiteralType::Hexfloat; } } virtual void RunShard(int shard) { char buffer[100]; FOREACH_UINT32(halfbits) { LOG_COMPLETION(halfbits); uint64_t bits = (static_cast(halfbits) << 32) | halfbits; WriteDoubleHex(buffer, sizeof(buffer), bits); int len = strlen(buffer); uint64_t new_bits; ASSERT_EQ(Result::Ok, ParseDouble(ClassifyDouble(bits), buffer, buffer + len, &new_bits)); ASSERT_EQ(new_bits, bits); } LOG_DONE(); } }; TEST_F(ManyDoublesRoundtripTest, Run) { RunThreads(); }