/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * 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 "common_components/heap/space/large_space.h"
#include "heap/allocator/region_manager.h"
#include "common_components/common_runtime/hooks.h"
#include "common_components/heap/collector/collector.h"
#include "common_components/heap/collector/marking_collector.h"
#if defined(COMMON_SANITIZER_SUPPORT)
#include "common_components/base/asan_interface.h"
#endif

namespace common {
void LargeSpace::AssembleGarbageCandidates()
{
    largeRegionList_.MergeRegionList(recentLargeRegionList_, RegionDesc::RegionType::LARGE_REGION);
}

void LargeSpace::CollectFixTasks(FixHeapTaskList& taskList)
{
    if (Heap::GetHeap().GetGCReason() == GC_REASON_YOUNG) {
        FixHeapWorker::CollectFixHeapTasks(taskList, largeRegionList_, FIX_OLD_REGION);
        FixHeapWorker::CollectFixHeapTasks(taskList, recentLargeRegionList_, FIX_RECENT_OLD_REGION);
    } else {
        FixHeapWorker::CollectFixHeapTasks(taskList, largeRegionList_, FIX_REGION);
        FixHeapWorker::CollectFixHeapTasks(taskList, recentLargeRegionList_, FIX_RECENT_REGION);
    }
}

size_t LargeSpace::CollectLargeGarbage()
{
    OHOS_HITRACE(HITRACE_LEVEL_COMMERCIAL, "CMCGC::CollectLargeGarbage", "");
    size_t garbageSize = 0;
#ifndef CMC_LCOV_EXCL
    MarkingCollector& collector = reinterpret_cast<MarkingCollector&>(Heap::GetHeap().GetCollector());
    RegionDesc* region = largeRegionList_.GetHeadRegion();
    while (region != nullptr) {
        HeapAddress addr = region->GetRegionStart();
        BaseObject *obj = reinterpret_cast<BaseObject *>(addr);

        if (region->IsJitFortAwaitInstallFlag()) {
                region = region->GetNextRegion();
                continue;
        }
        if (!collector.IsSurvivedObject(obj) && !region->IsNewObjectSinceMarking(obj)) {
            DLOG(REGION, "reclaim large region %p@0x%zx+%zu type %u", region, region->GetRegionStart(),
                 region->GetRegionAllocatedSize(), region->GetRegionType());

            RegionDesc* del = region;
            region = region->GetNextRegion();
            largeRegionList_.DeleteRegion(del);
            if (IsMachineCodeObject(reinterpret_cast<HeapAddress>(obj))) {
                JitFortUnProt(del->GetRegionBaseSize(), reinterpret_cast<void*>(del->GetRegionBaseFast()));
            }
            if (del->GetRegionSize() > RegionDesc::LARGE_OBJECT_RELEASE_THRESHOLD) {
                garbageSize += regionManager_.ReleaseRegion(del);
            } else {
                garbageSize += regionManager_.CollectRegion(del);
            }
        } else {
            DLOG(REGION, "clear mark-bit for large region %p@0x%zx+%zu type %u", region, region->GetRegionStart(),
                 region->GetRegionAllocatedSize(), region->GetRegionType());
            region = region->GetNextRegion();
        }
    }

    region = recentLargeRegionList_.GetHeadRegion();
    while (region != nullptr) {
        region = region->GetNextRegion();
    }
#endif
    return garbageSize;
}

uintptr_t LargeSpace::Alloc(size_t size, bool allowGC)
{
    size_t alignedSize = AlignUp<size_t>(size + RegionDesc::UNIT_HEADER_SIZE, RegionDesc::UNIT_SIZE);
    size_t regionCount = alignedSize / RegionDesc::UNIT_SIZE;
    RegionDesc* region = regionManager_.TakeRegion(regionCount, RegionDesc::UnitRole::LARGE_SIZED_UNITS,
                                                   false, allowGC);
    if (region == nullptr) {
        return 0;
    }
    InitRegionPhaseLine(region);
    DLOG(REGION, "alloc large region @0x%zx+%zu type %u", region->GetRegionStart(),
         region->GetRegionSize(), region->GetRegionType());
    uintptr_t addr = region->Alloc(size);
    ASSERT(addr > 0);
    recentLargeRegionList_.PrependRegion(region, RegionDesc::RegionType::RECENT_LARGE_REGION);
    return addr;
}

} // namespace common