diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/ftl/Android.bp | 1 | ||||
| -rw-r--r-- | libs/ftl/hash_test.cpp | 40 | ||||
| -rw-r--r-- | libs/ui/DisplayIdentification.cpp | 67 | ||||
| -rw-r--r-- | libs/ui/include/ui/DisplayIdentification.h | 3 | ||||
| -rw-r--r-- | libs/ui/tests/DisplayIdentification_test.cpp | 11 |
5 files changed, 52 insertions, 70 deletions
diff --git a/libs/ftl/Android.bp b/libs/ftl/Android.bp index 32b2b68b4d..4b41152c12 100644 --- a/libs/ftl/Android.bp +++ b/libs/ftl/Android.bp @@ -24,6 +24,7 @@ cc_test { "flags_test.cpp", "function_test.cpp", "future_test.cpp", + "hash_test.cpp", "match_test.cpp", "mixins_test.cpp", "non_null_test.cpp", diff --git a/libs/ftl/hash_test.cpp b/libs/ftl/hash_test.cpp new file mode 100644 index 0000000000..9c7b8c225e --- /dev/null +++ b/libs/ftl/hash_test.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2024 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 <ftl/hash.h> +#include <gtest/gtest.h> + +#include <numeric> +#include <string> + +namespace android::test { + +TEST(Hash, StableHash) { + EXPECT_EQ(11160318154034397263ull, (ftl::stable_hash({}))); + + std::string string(64, '?'); + std::iota(string.begin(), string.end(), 'A'); + + // Maximum length is 64 characters. + EXPECT_FALSE(ftl::stable_hash(string + '\n')); + + EXPECT_EQ(6278090252846864564ull, ftl::stable_hash(std::string_view(string).substr(0, 8))); + EXPECT_EQ(1883356980931444616ull, ftl::stable_hash(std::string_view(string).substr(0, 16))); + EXPECT_EQ(8073093283835059304ull, ftl::stable_hash(std::string_view(string).substr(0, 32))); + EXPECT_EQ(18197365392429149980ull, ftl::stable_hash(string)); +} + +} // namespace android::test diff --git a/libs/ui/DisplayIdentification.cpp b/libs/ui/DisplayIdentification.cpp index 82e5427317..0908ae85a8 100644 --- a/libs/ui/DisplayIdentification.cpp +++ b/libs/ui/DisplayIdentification.cpp @@ -23,67 +23,13 @@ #include <optional> #include <span> +#include <ftl/hash.h> #include <log/log.h> - #include <ui/DisplayIdentification.h> namespace android { namespace { -template <class T> -inline T load(const void* p) { - static_assert(std::is_integral<T>::value, "T must be integral"); - - T r; - std::memcpy(&r, p, sizeof(r)); - return r; -} - -uint64_t rotateByAtLeast1(uint64_t val, uint8_t shift) { - return (val >> shift) | (val << (64 - shift)); -} - -uint64_t shiftMix(uint64_t val) { - return val ^ (val >> 47); -} - -__attribute__((no_sanitize("unsigned-integer-overflow"))) -uint64_t hash64Len16(uint64_t u, uint64_t v) { - constexpr uint64_t kMul = 0x9ddfea08eb382d69; - uint64_t a = (u ^ v) * kMul; - a ^= (a >> 47); - uint64_t b = (v ^ a) * kMul; - b ^= (b >> 47); - b *= kMul; - return b; -} - -__attribute__((no_sanitize("unsigned-integer-overflow"))) -uint64_t hash64Len0To16(const char* s, uint64_t len) { - constexpr uint64_t k2 = 0x9ae16a3b2f90404f; - constexpr uint64_t k3 = 0xc949d7c7509e6557; - - if (len > 8) { - const uint64_t a = load<uint64_t>(s); - const uint64_t b = load<uint64_t>(s + len - 8); - return hash64Len16(a, rotateByAtLeast1(b + len, static_cast<uint8_t>(len))) ^ b; - } - if (len >= 4) { - const uint32_t a = load<uint32_t>(s); - const uint32_t b = load<uint32_t>(s + len - 4); - return hash64Len16(len + (a << 3), b); - } - if (len > 0) { - const unsigned char a = static_cast<unsigned char>(s[0]); - const unsigned char b = static_cast<unsigned char>(s[len >> 1]); - const unsigned char c = static_cast<unsigned char>(s[len - 1]); - const uint32_t y = static_cast<uint32_t>(a) + (static_cast<uint32_t>(b) << 8); - const uint32_t z = static_cast<uint32_t>(len) + (static_cast<uint32_t>(c) << 2); - return shiftMix(y * k2 ^ z * k3) * k2; - } - return k2; -} - using byte_view = std::span<const uint8_t>; constexpr size_t kEdidBlockSize = 128; @@ -320,7 +266,7 @@ std::optional<Edid> parseEdid(const DisplayIdentificationData& edid) { // Hash model string instead of using product code or (integer) serial number, since the latter // have been observed to change on some displays with multiple inputs. Use a stable hash instead // of std::hash which is only required to be same within a single execution of a program. - const uint32_t modelHash = static_cast<uint32_t>(cityHash64Len0To16(modelString)); + const uint32_t modelHash = static_cast<uint32_t>(*ftl::stable_hash(modelString)); // Parse extension blocks. std::optional<Cea861ExtensionBlock> cea861Block; @@ -394,13 +340,4 @@ PhysicalDisplayId getVirtualDisplayId(uint32_t id) { return PhysicalDisplayId::fromEdid(0, kVirtualEdidManufacturerId, id); } -uint64_t cityHash64Len0To16(std::string_view sv) { - auto len = sv.length(); - if (len > 16) { - ALOGE("%s called with length %zu. Only hashing the first 16 chars", __FUNCTION__, len); - len = 16; - } - return hash64Len0To16(sv.data(), len); -} - } // namespace android diff --git a/libs/ui/include/ui/DisplayIdentification.h b/libs/ui/include/ui/DisplayIdentification.h index fc9c0f491b..8bc2017b55 100644 --- a/libs/ui/include/ui/DisplayIdentification.h +++ b/libs/ui/include/ui/DisplayIdentification.h @@ -80,7 +80,4 @@ std::optional<DisplayIdentificationInfo> parseDisplayIdentificationData( PhysicalDisplayId getVirtualDisplayId(uint32_t id); -// CityHash64 implementation that only hashes at most the first 16 characters of the given string. -uint64_t cityHash64Len0To16(std::string_view sv); - } // namespace android diff --git a/libs/ui/tests/DisplayIdentification_test.cpp b/libs/ui/tests/DisplayIdentification_test.cpp index 736979a7c5..721b46688e 100644 --- a/libs/ui/tests/DisplayIdentification_test.cpp +++ b/libs/ui/tests/DisplayIdentification_test.cpp @@ -21,9 +21,9 @@ #include <functional> #include <string_view> +#include <ftl/hash.h> #include <gmock/gmock.h> #include <gtest/gtest.h> - #include <ui/DisplayIdentification.h> using ::testing::ElementsAre; @@ -135,7 +135,7 @@ DisplayIdentificationData asDisplayIdentificationData(const unsigned char (&byte } uint32_t hash(const char* str) { - return static_cast<uint32_t>(cityHash64Len0To16(str)); + return static_cast<uint32_t>(*ftl::stable_hash(str)); } } // namespace @@ -188,6 +188,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_STREQ("SEC", edid->pnpId.data()); // ASCII text should be used as fallback if display name and serial number are missing. EXPECT_EQ(hash("121AT11-801"), edid->modelHash); + EXPECT_EQ(hash("121AT11-801"), 626564263); EXPECT_TRUE(edid->displayName.empty()); EXPECT_EQ(12610, edid->productId); EXPECT_EQ(21, edid->manufactureOrModelYear); @@ -199,6 +200,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0x22f0u, edid->manufacturerId); EXPECT_STREQ("HWP", edid->pnpId.data()); EXPECT_EQ(hash("HP ZR30w"), edid->modelHash); + EXPECT_EQ(hash("HP ZR30w"), 918492362); EXPECT_EQ("HP ZR30w", edid->displayName); EXPECT_EQ(10348, edid->productId); EXPECT_EQ(22, edid->manufactureOrModelYear); @@ -210,6 +212,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(0x4c2du, edid->manufacturerId); EXPECT_STREQ("SAM", edid->pnpId.data()); EXPECT_EQ(hash("SAMSUNG"), edid->modelHash); + EXPECT_EQ(hash("SAMSUNG"), 1201368132); EXPECT_EQ("SAMSUNG", edid->displayName); EXPECT_EQ(2302, edid->productId); EXPECT_EQ(21, edid->manufactureOrModelYear); @@ -227,6 +230,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(13481, edid->manufacturerId); EXPECT_STREQ("MEI", edid->pnpId.data()); EXPECT_EQ(hash("Panasonic-TV"), edid->modelHash); + EXPECT_EQ(hash("Panasonic-TV"), 3876373262); EXPECT_EQ("Panasonic-TV", edid->displayName); EXPECT_EQ(41622, edid->productId); EXPECT_EQ(29, edid->manufactureOrModelYear); @@ -244,6 +248,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(8355, edid->manufacturerId); EXPECT_STREQ("HEC", edid->pnpId.data()); EXPECT_EQ(hash("Hisense"), edid->modelHash); + EXPECT_EQ(hash("Hisense"), 2859844809); EXPECT_EQ("Hisense", edid->displayName); EXPECT_EQ(0, edid->productId); EXPECT_EQ(29, edid->manufactureOrModelYear); @@ -261,6 +266,7 @@ TEST(DisplayIdentificationTest, parseEdid) { EXPECT_EQ(3724, edid->manufacturerId); EXPECT_STREQ("CTL", edid->pnpId.data()); EXPECT_EQ(hash("LP2361"), edid->modelHash); + EXPECT_EQ(hash("LP2361"), 1523181158); EXPECT_EQ("LP2361", edid->displayName); EXPECT_EQ(9373, edid->productId); EXPECT_EQ(23, edid->manufactureOrModelYear); @@ -281,6 +287,7 @@ TEST(DisplayIdentificationTest, parseInvalidEdid) { // Serial number should be used as fallback if display name is invalid. const auto modelHash = hash("CN4202137Q"); EXPECT_EQ(modelHash, edid->modelHash); + EXPECT_EQ(modelHash, 3582951527); EXPECT_TRUE(edid->displayName.empty()); // Parsing should succeed even if EDID is truncated. |