| /* |
| * 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 "memcmp16.h" |
| |
| #include "gtest/gtest.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<int32_t>(s0[i]) - static_cast<int32_t>(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<uint16_t*>(0xebad1001); |
| } |
| |
| if (count2 > 0U) { |
| s2 = new uint16_t[count2]; |
| } else { |
| // Leave a random pointer, should not be touched. |
| s2 = reinterpret_cast<uint16_t*>(0xebad2002); |
| } |
| |
| size_t min = count1 < count2 ? count1 : count2; |
| bool fill_same = r.next() % 2 == 1; |
| |
| if (fill_same) { |
| for (size_t i = 0; i < min; ++i) { |
| s1[i] = static_cast<uint16_t>(r.next() & 0xFFFF); |
| s2[i] = s1[i]; |
| } |
| for (size_t i = min; i < count1; ++i) { |
| s1[i] = static_cast<uint16_t>(r.next() & 0xFFFF); |
| } |
| for (size_t i = min; i < count2; ++i) { |
| s2[i] = static_cast<uint16_t>(r.next() & 0xFFFF); |
| } |
| } else { |
| for (size_t i = 0; i < count1; ++i) { |
| s1[i] = static_cast<uint16_t>(r.next() & 0xFFFF); |
| } |
| for (size_t i = 0; i < count2; ++i) { |
| s2[i] = static_cast<uint16_t>(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) { |
| // |
| // } |