| /* |
| * Copyright (c) 2018-2021 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "pas_config.h" |
| |
| #if LIBPAS_ENABLED |
| |
| #include "pas_large_map.h" |
| |
| #include "pas_large_heap.h" |
| #include "pas_large_utility_free_heap.h" |
| |
| pas_large_map_hashtable pas_large_map_hashtable_instance = PAS_HASHTABLE_INITIALIZER; |
| pas_large_map_hashtable_in_flux_stash pas_large_map_hashtable_instance_in_flux_stash; |
| pas_small_large_map_hashtable pas_small_large_map_hashtable_instance = PAS_HASHTABLE_INITIALIZER; |
| pas_small_large_map_hashtable_in_flux_stash pas_small_large_map_hashtable_instance_in_flux_stash; |
| pas_tiny_large_map_hashtable pas_tiny_large_map_hashtable_instance = PAS_HASHTABLE_INITIALIZER; |
| pas_tiny_large_map_hashtable_in_flux_stash pas_tiny_large_map_hashtable_instance_in_flux_stash; |
| pas_tiny_large_map_second_level_hashtable_in_flux_stash pas_tiny_large_map_second_level_hashtable_in_flux_stash_instance; |
| |
| pas_large_map_entry pas_large_map_find(uintptr_t begin) |
| { |
| uintptr_t tiny_base; |
| pas_first_level_tiny_large_map_entry* first_level_tiny_entry; |
| pas_small_large_map_entry* small_entry; |
| |
| pas_heap_lock_assert_held(); |
| |
| tiny_base = pas_tiny_large_map_entry_base(begin); |
| first_level_tiny_entry = pas_tiny_large_map_hashtable_find( |
| &pas_tiny_large_map_hashtable_instance, tiny_base); |
| if (first_level_tiny_entry) { |
| pas_tiny_large_map_entry* tiny_entry; |
| PAS_ASSERT(first_level_tiny_entry->base == tiny_base); |
| tiny_entry = pas_tiny_large_map_second_level_hashtable_find( |
| first_level_tiny_entry->hashtable, begin - tiny_base); |
| if (tiny_entry) { |
| PAS_ASSERT(pas_tiny_large_map_entry_begin(*tiny_entry, tiny_base) == begin); |
| return pas_tiny_large_map_entry_get_entry(*tiny_entry, tiny_base); |
| } |
| } |
| |
| small_entry = pas_small_large_map_hashtable_find( |
| &pas_small_large_map_hashtable_instance, begin); |
| if (small_entry) |
| return pas_small_large_map_entry_get_entry(*small_entry); |
| |
| return pas_large_map_hashtable_get(&pas_large_map_hashtable_instance, begin); |
| } |
| |
| void pas_large_map_add(pas_large_map_entry entry) |
| { |
| static const bool verbose = false; |
| |
| pas_heap_lock_assert_held(); |
| |
| if (verbose) |
| pas_log("adding %p...%p, heap = %p.\n", (void*)entry.begin, (void*)entry.end, entry.heap); |
| |
| if (pas_tiny_large_map_entry_can_create(entry)) { |
| pas_tiny_large_map_hashtable_add_result add_result; |
| uintptr_t tiny_base; |
| pas_tiny_large_map_entry tiny_entry; |
| |
| if (verbose) |
| pas_log("can be tiny.\n"); |
| |
| tiny_base = pas_tiny_large_map_entry_base(entry.begin); |
| |
| tiny_entry = pas_tiny_large_map_entry_create(entry); |
| |
| if (verbose) |
| pas_log("adding base = %p.\n", tiny_base); |
| |
| add_result = pas_tiny_large_map_hashtable_add( |
| &pas_tiny_large_map_hashtable_instance, tiny_base, |
| &pas_tiny_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config); |
| |
| if (add_result.is_new_entry) { |
| if (verbose) |
| pas_log("allocating table.\n"); |
| pas_tiny_large_map_hashtable_instance_in_flux_stash.in_flux_entry = add_result.entry; |
| pas_compiler_fence(); |
| add_result.entry->base = tiny_base; |
| add_result.entry->hashtable = |
| pas_utility_heap_allocate(sizeof(pas_tiny_large_map_second_level_hashtable), |
| "pas_tiny_large_map_second_level_hashtable"); |
| pas_tiny_large_map_second_level_hashtable_construct(add_result.entry->hashtable); |
| pas_compiler_fence(); |
| pas_tiny_large_map_hashtable_instance_in_flux_stash.in_flux_entry = NULL; |
| } |
| |
| pas_tiny_large_map_second_level_hashtable_add_new( |
| add_result.entry->hashtable, tiny_entry, |
| &pas_tiny_large_map_second_level_hashtable_in_flux_stash_instance, |
| &pas_large_utility_free_heap_allocation_config); |
| return; |
| } |
| |
| if (pas_small_large_map_entry_can_create(entry)) { |
| pas_small_large_map_entry small_entry; |
| |
| if (verbose) |
| pas_log("can be small.\n"); |
| |
| small_entry = pas_small_large_map_entry_create(entry); |
| |
| pas_small_large_map_hashtable_add_new( |
| &pas_small_large_map_hashtable_instance, small_entry, |
| &pas_small_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config); |
| return; |
| } |
| |
| if (verbose) |
| pas_log("gotta be large.\n"); |
| |
| pas_large_map_hashtable_add_new( |
| &pas_large_map_hashtable_instance, entry, |
| &pas_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config); |
| } |
| |
| pas_large_map_entry pas_large_map_take(uintptr_t begin) |
| { |
| static const bool verbose = false; |
| |
| uintptr_t tiny_base; |
| pas_first_level_tiny_large_map_entry* first_level_tiny_entry; |
| pas_small_large_map_entry small_entry; |
| |
| pas_heap_lock_assert_held(); |
| |
| if (verbose) |
| pas_log("taking begin = %p.\n", (void*)begin); |
| |
| tiny_base = pas_tiny_large_map_entry_base(begin); |
| if (verbose) |
| pas_log("tiny_base = %p.\n", tiny_base); |
| first_level_tiny_entry = pas_tiny_large_map_hashtable_find( |
| &pas_tiny_large_map_hashtable_instance, tiny_base); |
| if (first_level_tiny_entry) { |
| pas_tiny_large_map_second_level_hashtable* second_hashtable; |
| pas_tiny_large_map_entry tiny_entry; |
| if (verbose) |
| pas_log("found first level tiny.\n"); |
| PAS_ASSERT(first_level_tiny_entry->base == tiny_base); |
| second_hashtable = first_level_tiny_entry->hashtable; |
| if (pas_tiny_large_map_second_level_hashtable_take_and_return_if_taken( |
| second_hashtable, begin - tiny_base, &tiny_entry, |
| &pas_tiny_large_map_second_level_hashtable_in_flux_stash_instance, |
| &pas_large_utility_free_heap_allocation_config)) { |
| if (verbose) |
| pas_log("found second level tiny.\n"); |
| PAS_ASSERT(pas_tiny_large_map_entry_begin(tiny_entry, tiny_base) == begin); |
| if (!pas_tiny_large_map_second_level_hashtable_size(second_hashtable)) { |
| pas_tiny_large_map_hashtable_delete( |
| &pas_tiny_large_map_hashtable_instance, tiny_base, |
| &pas_tiny_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config); |
| pas_tiny_large_map_second_level_hashtable_destruct( |
| second_hashtable, &pas_large_utility_free_heap_allocation_config); |
| pas_utility_heap_deallocate(second_hashtable); |
| } |
| return pas_tiny_large_map_entry_get_entry(tiny_entry, tiny_base); |
| } |
| } |
| |
| if (pas_small_large_map_hashtable_take_and_return_if_taken( |
| &pas_small_large_map_hashtable_instance, begin, &small_entry, |
| &pas_small_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config)) |
| return pas_small_large_map_entry_get_entry(small_entry); |
| |
| return pas_large_map_hashtable_take( |
| &pas_large_map_hashtable_instance, begin, |
| &pas_large_map_hashtable_instance_in_flux_stash, |
| &pas_large_utility_free_heap_allocation_config); |
| } |
| |
| bool pas_large_map_for_each_entry(pas_large_map_for_each_entry_callback callback, |
| void* arg) |
| { |
| size_t index; |
| |
| for (index = pas_large_map_hashtable_entry_index_end(&pas_large_map_hashtable_instance); |
| index--;) { |
| pas_large_map_entry entry; |
| entry = *pas_large_map_hashtable_entry_at_index(&pas_large_map_hashtable_instance, |
| index); |
| if (pas_large_map_entry_is_empty_or_deleted(entry)) |
| continue; |
| if (!callback(entry, arg)) |
| return false; |
| } |
| |
| for (index = pas_small_large_map_hashtable_entry_index_end( |
| &pas_small_large_map_hashtable_instance); |
| index--;) { |
| pas_small_large_map_entry entry; |
| entry = *pas_small_large_map_hashtable_entry_at_index( |
| &pas_small_large_map_hashtable_instance, |
| index); |
| if (pas_small_large_map_entry_is_empty_or_deleted(entry)) |
| continue; |
| if (!callback(pas_small_large_map_entry_get_entry(entry), arg)) |
| return false; |
| } |
| |
| for (index = pas_tiny_large_map_hashtable_entry_index_end( |
| &pas_tiny_large_map_hashtable_instance); |
| index--;) { |
| pas_first_level_tiny_large_map_entry entry; |
| size_t second_index; |
| entry = *pas_tiny_large_map_hashtable_entry_at_index(&pas_tiny_large_map_hashtable_instance, |
| index); |
| if (pas_first_level_tiny_large_map_entry_is_empty_or_deleted(entry)) |
| continue; |
| for (second_index = pas_tiny_large_map_second_level_hashtable_entry_index_end( |
| entry.hashtable); |
| second_index--;) { |
| pas_tiny_large_map_entry second_entry; |
| second_entry = *pas_tiny_large_map_second_level_hashtable_entry_at_index(entry.hashtable, |
| second_index); |
| if (pas_tiny_large_map_entry_is_empty_or_deleted(second_entry)) |
| continue; |
| if (!callback(pas_tiny_large_map_entry_get_entry(second_entry, entry.base), arg)) |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| #endif /* LIBPAS_ENABLED */ |