| /* | 
 |  * Copyright (C) 2018 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. | 
 |  */ | 
 |  | 
 | #ifndef ART_RUNTIME_INTERN_TABLE_INL_H_ | 
 | #define ART_RUNTIME_INTERN_TABLE_INL_H_ | 
 |  | 
 | #include "intern_table.h" | 
 |  | 
 | #include "dex/utf.h" | 
 | #include "gc/space/image_space.h" | 
 | #include "gc_root-inl.h" | 
 | #include "image.h" | 
 | #include "mirror/string-inl.h" | 
 | #include "thread-current-inl.h" | 
 |  | 
 | namespace art { | 
 |  | 
 | ALWAYS_INLINE | 
 | inline uint32_t InternTable::Utf8String::Hash(uint32_t utf16_length, const char* utf8_data) { | 
 |   DCHECK_EQ(utf16_length, CountModifiedUtf8Chars(utf8_data)); | 
 |   if (LIKELY(utf8_data[utf16_length] == 0)) { | 
 |     int32_t hash = ComputeUtf16Hash(utf8_data, utf16_length); | 
 |     DCHECK_EQ(hash, ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length)); | 
 |     return hash; | 
 |   } else { | 
 |     return ComputeUtf16HashFromModifiedUtf8(utf8_data, utf16_length); | 
 |   } | 
 | } | 
 |  | 
 | ALWAYS_INLINE | 
 | inline size_t InternTable::StringHash::operator()(const GcRoot<mirror::String>& root) const { | 
 |   if (kIsDebugBuild) { | 
 |     Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); | 
 |   } | 
 |   ObjPtr<mirror::String> s = root.Read<kWithoutReadBarrier>(); | 
 |   int32_t hash = s->GetStoredHashCode(); | 
 |   DCHECK_EQ(hash, s->ComputeHashCode()); | 
 |   // An additional cast to prevent undesired sign extension. | 
 |   return static_cast<uint32_t>(hash); | 
 | } | 
 |  | 
 | ALWAYS_INLINE | 
 | inline bool InternTable::StringEquals::operator()(const GcRoot<mirror::String>& a, | 
 |                                                   const GcRoot<mirror::String>& b) const { | 
 |   if (kIsDebugBuild) { | 
 |     Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); | 
 |   } | 
 |   return a.Read<kWithoutReadBarrier>()->Equals(b.Read<kWithoutReadBarrier>()); | 
 | } | 
 |  | 
 | ALWAYS_INLINE | 
 | inline bool InternTable::StringEquals::operator()(const GcRoot<mirror::String>& a, | 
 |                                                   const Utf8String& b) const { | 
 |   if (kIsDebugBuild) { | 
 |     Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); | 
 |   } | 
 |   ObjPtr<mirror::String> a_string = a.Read<kWithoutReadBarrier>(); | 
 |   uint32_t a_length = static_cast<uint32_t>(a_string->GetLength()); | 
 |   if (a_length != b.GetUtf16Length()) { | 
 |     return false; | 
 |   } | 
 |   DCHECK_GE(strlen(b.GetUtf8Data()), a_length); | 
 |   if (a_string->IsCompressed()) { | 
 |     // Modified UTF-8 single byte character range is 0x01 .. 0x7f. | 
 |     // The string compression occurs on regular ASCII with same exact range, | 
 |     // not on extended ASCII which is up to 0xff. | 
 |     return b.GetUtf8Data()[a_length] == 0 && | 
 |            memcmp(b.GetUtf8Data(), a_string->GetValueCompressed(), a_length * sizeof(uint8_t)) == 0; | 
 |   } else if (mirror::kUseStringCompression && b.GetUtf8Data()[a_length] == 0) { | 
 |     // ASCII string `b` cannot equal non-ASCII `a_string`. | 
 |     return false; | 
 |   } else { | 
 |     const uint16_t* a_value = a_string->GetValue(); | 
 |     return CompareModifiedUtf8ToUtf16AsCodePointValues(b.GetUtf8Data(), a_value, a_length) == 0; | 
 |   } | 
 | } | 
 |  | 
 | template <typename Visitor> | 
 | inline void InternTable::AddImageStringsToTable(gc::space::ImageSpace* image_space, | 
 |                                                 const Visitor& visitor) { | 
 |   DCHECK(image_space != nullptr); | 
 |   // Only add if we have the interned strings section. | 
 |   const ImageHeader& header = image_space->GetImageHeader(); | 
 |   const ImageSection& section = header.GetInternedStringsSection(); | 
 |   if (section.Size() > 0) { | 
 |     AddTableFromMemory(image_space->Begin() + section.Offset(), visitor, !header.IsAppImage()); | 
 |   } | 
 | } | 
 |  | 
 | template <typename Visitor> | 
 | inline size_t InternTable::AddTableFromMemory(const uint8_t* ptr, | 
 |                                               const Visitor& visitor, | 
 |                                               bool is_boot_image) { | 
 |   size_t read_count = 0; | 
 |   UnorderedSet set(ptr, /*make copy*/false, &read_count); | 
 |   { | 
 |     // Hold the lock while calling the visitor to prevent possible race | 
 |     // conditions with another thread adding intern strings. | 
 |     MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); | 
 |     // Visit the unordered set, may remove elements. | 
 |     visitor(set); | 
 |     if (!set.empty()) { | 
 |       strong_interns_.AddInternStrings(std::move(set), is_boot_image); | 
 |     } | 
 |   } | 
 |   return read_count; | 
 | } | 
 |  | 
 | inline void InternTable::Table::AddInternStrings(UnorderedSet&& intern_strings, | 
 |                                                  bool is_boot_image) { | 
 |   if (kIsDebugBuild) { | 
 |     // Avoid doing read barriers since the space might not yet be added to the heap. | 
 |     // See b/117803941 | 
 |     for (GcRoot<mirror::String>& string : intern_strings) { | 
 |       ObjPtr<mirror::String> s = string.Read<kWithoutReadBarrier>(); | 
 |       uint32_t hash = static_cast<uint32_t>(s->GetStoredHashCode()); | 
 |       CHECK_EQ(hash, static_cast<uint32_t>(s->ComputeHashCode())); | 
 |       CHECK(Find(s, hash) == nullptr) | 
 |           << "Already found " << string.Read<kWithoutReadBarrier>()->ToModifiedUtf8() | 
 |           << " in the intern table"; | 
 |     } | 
 |   } | 
 |  | 
 |   // Insert before the last (unfrozen) table since we add new interns into the back. | 
 |   // Keep the order of previous frozen tables unchanged, so that we can can remember | 
 |   // the number of searched frozen tables and not search them again. | 
 |   DCHECK(!tables_.empty()); | 
 |   tables_.insert(tables_.end() - 1, InternalTable(std::move(intern_strings), is_boot_image)); | 
 | } | 
 |  | 
 | template <typename Visitor> | 
 | inline void InternTable::VisitInterns(const Visitor& visitor, | 
 |                                       bool visit_boot_images, | 
 |                                       bool visit_non_boot_images) { | 
 |   auto visit_tables = [&](dchecked_vector<Table::InternalTable>& tables) | 
 |       NO_THREAD_SAFETY_ANALYSIS { | 
 |     for (Table::InternalTable& table : tables) { | 
 |       // Determine if we want to visit the table based on the flags. | 
 |       const bool visit = table.IsBootImage() ? visit_boot_images : visit_non_boot_images; | 
 |       if (visit) { | 
 |         for (auto& intern : table.set_) { | 
 |           visitor(intern); | 
 |         } | 
 |       } | 
 |     } | 
 |   }; | 
 |   visit_tables(strong_interns_.tables_); | 
 |   visit_tables(weak_interns_.tables_); | 
 | } | 
 |  | 
 | inline size_t InternTable::CountInterns(bool visit_boot_images, bool visit_non_boot_images) const { | 
 |   size_t ret = 0u; | 
 |   auto visit_tables = [&](const dchecked_vector<Table::InternalTable>& tables) | 
 |       NO_THREAD_SAFETY_ANALYSIS { | 
 |     for (const Table::InternalTable& table : tables) { | 
 |       // Determine if we want to visit the table based on the flags. | 
 |       const bool visit = table.IsBootImage() ? visit_boot_images : visit_non_boot_images; | 
 |       if (visit) { | 
 |         ret += table.set_.size(); | 
 |       } | 
 |     } | 
 |   }; | 
 |   visit_tables(strong_interns_.tables_); | 
 |   visit_tables(weak_interns_.tables_); | 
 |   return ret; | 
 | } | 
 |  | 
 | }  // namespace art | 
 |  | 
 | #endif  // ART_RUNTIME_INTERN_TABLE_INL_H_ |