/* * Copyright (C) 2014 The Android Open Source Project * * 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 "gtest/gtest.h" #include "memcmp16.h" class RandGen { public: explicit RandGen(uint32_t seed) : val_(seed) {} uint32_t next() { val_ = val_ * 48271 % 2147483647 + 13; return val_; } uint32_t val_; }; class MemCmp16Test : public testing::Test { }; // A simple implementation to compare against. // Note: this version is equivalent to the generic one used when no optimized version is available. int32_t memcmp16_compare(const uint16_t* s0, const uint16_t* s1, size_t count) { for (size_t i = 0; i < count; i++) { if (s0[i] != s1[i]) { return static_cast(s0[i]) - static_cast(s1[i]); } } return 0; } static constexpr size_t kMemCmp16Rounds = 100000; static void CheckSeparate(size_t max_length, size_t min_length) { RandGen r(0x1234); size_t range_of_tests = 7; // All four (weighted) tests active in the beginning. for (size_t round = 0; round < kMemCmp16Rounds; ++round) { size_t type = r.next() % range_of_tests; size_t count1, count2; uint16_t *s1, *s2; // Use raw pointers to simplify using clobbered addresses switch (type) { case 0: // random, non-zero lengths of both strings case 1: case 2: case 3: count1 = (r.next() % max_length) + min_length; count2 = (r.next() % max_length) + min_length; break; case 4: // random non-zero length of first, second is zero count1 = (r.next() % max_length) + min_length; count2 = 0U; break; case 5: // random non-zero length of second, first is zero count1 = 0U; count2 = (r.next() % max_length) + min_length; break; case 6: // both zero-length count1 = 0U; count2 = 0U; range_of_tests = 6; // Don't do zero-zero again. break; default: ASSERT_TRUE(false) << "Should not get here."; continue; } if (count1 > 0U) { s1 = new uint16_t[count1]; } else { // Leave a random pointer, should not be touched. s1 = reinterpret_cast(0xebad1001); } if (count2 > 0U) { s2 = new uint16_t[count2]; } else { // Leave a random pointer, should not be touched. s2 = reinterpret_cast(0xebad2002); } size_t min = count1 < count2 ? count1 : count2; bool fill_same = r.next() % 1 == 1; if (fill_same) { for (size_t i = 0; i < min; ++i) { s1[i] = static_cast(r.next() & 0xFFFF); s2[i] = s1[i]; } for (size_t i = min; i < count1; ++i) { s1[i] = static_cast(r.next() & 0xFFFF); } for (size_t i = min; i < count2; ++i) { s2[i] = static_cast(r.next() & 0xFFFF); } } else { for (size_t i = 0; i < count1; ++i) { s1[i] = static_cast(r.next() & 0xFFFF); } for (size_t i = 0; i < count2; ++i) { s2[i] = static_cast(r.next() & 0xFFFF); } } uint16_t* s1_pot_unaligned = s1; uint16_t* s2_pot_unaligned = s2; size_t c1_mod = count1; size_t c2_mod = count2; if (!fill_same) { // Don't waste a good "long" test. if (count1 > 1 && r.next() % 10 == 0) { c1_mod--; s1_pot_unaligned++; } if (count2 > 1 && r.next() % 10 == 0) { c2_mod--; s2_pot_unaligned++; } } size_t mod_min = c1_mod < c2_mod ? c1_mod : c2_mod; int32_t expected = memcmp16_compare(s1_pot_unaligned, s2_pot_unaligned, mod_min); int32_t computed = art::testing::MemCmp16Testing(s1_pot_unaligned, s2_pot_unaligned, mod_min); ASSERT_EQ(expected, computed) << "Run " << round << ", c1=" << count1 << " c2=" << count2; if (count1 > 0U) { delete[] s1; } if (count2 > 0U) { delete[] s2; } } } TEST_F(MemCmp16Test, RandomSeparateShort) { CheckSeparate(5U, 1U); } TEST_F(MemCmp16Test, RandomSeparateLong) { CheckSeparate(64U, 32U); } // TODO: What's a good test for overlapping memory. Is it important? // TEST_F(MemCmp16Test, RandomOverlay) { // // }