/* * 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 { inline std::size_t InternTable::StringHash::operator()(const GcRoot& root) const { if (kIsDebugBuild) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } // An additional cast to prevent undesired sign extension. return static_cast( static_cast(root.Read()->GetHashCode())); } inline bool InternTable::StringEquals::operator()(const GcRoot& a, const GcRoot& b) const { if (kIsDebugBuild) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } return a.Read()->Equals(b.Read()); } inline bool InternTable::StringEquals::operator()(const GcRoot& a, const Utf8String& b) const { if (kIsDebugBuild) { Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); } ObjPtr a_string = a.Read(); uint32_t a_length = static_cast(a_string->GetLength()); if (a_length != b.GetUtf16Length()) { return false; } if (a_string->IsCompressed()) { size_t b_byte_count = strlen(b.GetUtf8Data()); size_t b_utf8_length = CountModifiedUtf8Chars(b.GetUtf8Data(), b_byte_count); // 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 up to 0xff const bool is_b_regular_ascii = (b_byte_count == b_utf8_length); if (is_b_regular_ascii) { return memcmp(b.GetUtf8Data(), a_string->GetValueCompressed(), a_length * sizeof(uint8_t)) == 0; } else { return false; } } else { const uint16_t* a_value = a_string->GetValue(); return CompareModifiedUtf8ToUtf16AsCodePointValues(b.GetUtf8Data(), a_value, a_length) == 0; } } template 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 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) { static constexpr bool kCheckDuplicates = kIsDebugBuild; if (kCheckDuplicates) { // Avoid doing read barriers since the space might not yet be added to the heap. // See b/117803941 for (GcRoot& string : intern_strings) { CHECK(Find(string.Read()) == nullptr) << "Already found " << string.Read()->ToModifiedUtf8() << " in the intern table"; } } // Insert at the front since we add new interns into the back. tables_.insert(tables_.begin(), InternalTable(std::move(intern_strings), is_boot_image)); } template inline void InternTable::VisitInterns(const Visitor& visitor, bool visit_boot_images, bool visit_non_boot_images) { auto visit_tables = [&](std::vector& 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 = (visit_boot_images && table.IsBootImage()) || (visit_non_boot_images && !table.IsBootImage()); 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 std::vector& 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 = (visit_boot_images && table.IsBootImage()) || (visit_non_boot_images && !table.IsBootImage()); 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_