////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// THIS IS AUTO-GENERATED CODE.  PLEASE DO NOT EDIT (File bug reports against tools).
///
/// Auto-generated by: Parameter Parser V5.5.1 (2411131018)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file  parameterfilesymboltable.cpp
/// @brief Auto-generated Chromatix parameter file
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "parameterfilesymboltable.h"
#include "parametersetmanager.h"

ParameterFileSymbolTable::ParameterFileSymbolTable(
    ParameterSetManager* const setManager,
    const VersionUnion&        version)
{
    m_count         = 0;
    m_dtCount       = 0;
    m_dupCount      = 0;
    m_dtDupCount    = 0;
    m_entryLookup   = nullptr;
    m_dtEntryLookup = nullptr;
    m_dupEntries    = nullptr;
    m_dtDupEntries  = nullptr;
    m_entries       = nullptr;
    m_dtEntries     = nullptr;
    SetManager      = setManager;
    Version.Value   = version.Value;
    DataTableVersion.Version.Major  = ParameterSetManager::DATA_TABLE_MAJOR;
    DataTableVersion.Version.Minor  = ParameterSetManager::DATA_TABLE_MINOR;
    DataTableVersion.Version.Patch  = ParameterSetManager::DATA_TABLE_PATCH;
    AlignedEndVersion.Version.Major = ParameterSetManager::ALIGNED_END_MAJOR;
    AlignedEndVersion.Version.Minor = ParameterSetManager::ALIGNED_END_MINOR;
    AlignedEndVersion.Version.Patch = ParameterSetManager::ALIGNED_END_PATCH;
    LinkVersion.Version.Major       = ParameterSetManager::LINK_MAJOR;
    LinkVersion.Version.Minor       = ParameterSetManager::LINK_MINOR;
    LinkVersion.Version.Patch       = ParameterSetManager::LINK_PATCH;
    ModeFlagsVersion.Version.Major  = ParameterSetManager::MODE_FLAGS_MAJOR;
    ModeFlagsVersion.Version.Minor  = ParameterSetManager::MODE_FLAGS_MINOR;
    ModeFlagsVersion.Version.Patch  = ParameterSetManager::MODE_FLAGS_PATCH;
    File              = nullptr;
    FileLength        = 0;
    SymbolTableOffset = 0;
    SymbolTableSize   = 0;
    DataTableOffset   = 0;
    DataTableSize     = 0;
    DataOffset        = 0;
    DataSize          = 0;
    Data              = nullptr;
    ModeTable         = 0;
    Alignment         = 1;
}

ParameterFileSymbolTable::~ParameterFileSymbolTable()
{
    if (m_dtEntries != nullptr && m_dtEntries != m_entries)
    {
        PARAMETER_DELETE[] m_dtEntries;
    }

    if (m_entries != nullptr)
    {
        PARAMETER_DELETE[] m_entries;
    }

    if (m_dupEntries != nullptr)
    {
        for (UINT32 i = 0; i < m_dupCount; i++)
        {
            while (m_dupEntries[i].Count() > 0)
            {
                const UINT32 index = m_dupEntries[i].Count() - 1;
                PARAMETER_DELETE m_dupEntries[i][index];
                m_dupEntries[i].RemoveAt(index);
            }
        }
        PARAMETER_DELETE[] m_dupEntries;
    }

    if (m_dtDupEntries != nullptr)
    {
        for (UINT32 i = 0; i < m_dtDupCount; i++)
        {
            while (m_dtDupEntries[i].Count() > 0)
            {
                UINT32 const index = m_dtDupEntries[i].Count() - 1;
                PARAMETER_DELETE m_dtDupEntries[i][index];
                m_dtDupEntries[i].RemoveAt(index);
            }
        }
        PARAMETER_DELETE[] m_dtDupEntries;
    }

    if (m_dtEntryLookup != nullptr && m_dtEntryLookup != m_entryLookup)
    {
        PARAMETER_DELETE[] m_dtEntryLookup;
    }

    if (m_entryLookup != nullptr)
    {
        PARAMETER_DELETE[] m_entryLookup;
    }

    for (UINT32 i = 0; i < m_modules.Count(); i++)
    {
        PARAMETER_DELETE m_modules[i];
    }

    if (m_dupInfoMap.size() > 0)
    {
        PARAMETER_LOG_ERROR(SetManager, "%d DupInfo object were not released\n", m_dupInfoMap.size());
    }

    PARAMETER_DELETE File;
}

bool ParameterFileSymbolTable::ReadTable(
    BYTE* const                    buffer,
    UINT32 const                   length,
    UINT32* const                  pos,
    UINT32                         count,
    ParameterFileSymbolTableEntry* entries,
    EntryData* const               entryLookup,
    UINT32&                        dupCount,
    PList<DupObjInfo*>*&           dupEntries,
    UINT32 const                   dataOffset,
    IModeTable* const              modeTable,
    const bool                     isModule,
    const BYTE                     alignment)
{
    UINT32* duplicates = PARAMETER_NEW UINT32[count + 1];
    bool    valid      = true;

    if (duplicates != nullptr)
    {
        PARAMETER_MEMSET(duplicates, 0, sizeof(UINT32) * (count + 1));
        PARAMETER_INITIALIZE(entries[0]); // entry 0 is unused, so clear it

        for (UINT32 i = 1; valid && i < count + 1; i++)
        {
            UINT32 id = 0;
            UINT32 idPos = *pos;
            valid = ParameterUtils::Read(&id, buffer, length, &idPos, alignment, SetManager);
            if (valid && id > 0 && id < count + 1)
            {
                entryLookup[id].Position = *pos;
                entries[id].Init(this);
                valid = entries[id].Load(buffer, nullptr, nullptr, length, pos, dataOffset, modeTable, isModule, alignment);
                if (valid && duplicates != nullptr)
                {
                    const UINT32 dup = entries[id].DuplicateID;
                    duplicates[dup]++;           // Count the duplicates
                    entryLookup[id].Dup = dup; // Make duplicate entries point to self/duplicate
                }
            }
            else if (!valid)
            {
                PARAMETER_LOG_ERROR(SetManager, "Failed to read entry ID from Symbol table\n");
            }
            else
            {
                PARAMETER_LOG_ERROR(SetManager, "Symbol table entry ID %d out of range 1 - %d\n", id, count);
            }
        }

        // Do not use entry 0 in m_dupEntries as that value is used to represent no duplicates
        dupCount = 1;
        // Build the total number of m_dupEntries needed, change duplicates to point to the m_dupEntries index,
        // or zero if not a duplicate
        for (UINT32 i = 1; valid && duplicates != nullptr && i < count + 1; i++)
        {
            if (duplicates[i] > 1)
            {
                duplicates[i] = dupCount++;
            }
            else
            {
                duplicates[i] = 0;
            }
        }

        // After re-building the duplicates array, update entryLookup to point to the m_dupEntries index, 
        // or zero if no duplicate
        for (UINT32 i = 1; valid && duplicates != nullptr && i < count + 1; i++)
        {
            if (entryLookup[i].Dup > 0)
            {
                entryLookup[i].Dup = duplicates[entryLookup[i].Dup];
            }
        }

        PARAMETER_DELETE[] duplicates;
        duplicates = nullptr;

        // If there are duplicates, build m_dupEntries
        if (dupCount > 1)
        {
            dupEntries = PARAMETER_NEW PList<DupObjInfo*>[dupCount];
        }

        for (UINT32 i = 1; valid && i < count + 1; i++)
        {
            if (entries[i].IsModule())
            {
                m_modules.Add(entries[i].Module);
                entries[i].Module = nullptr;
            }
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(SetManager, "Failed to allocate space for duplicates variable\n");
    }

    return valid;
}

bool ParameterFileSymbolTable::Load(
    BYTE* const                buffer,
    UINT32 const               bufLength,
    const CHAR* const          fileName,
    ParameterBinaryFile* const file,
    UINT32 const               fileLength,
    UINT32                     tableSize,
    UINT32                     tableOffset,
    UINT32                     dataTableSize,
    UINT32 const               dataTableOffset,
    UINT32 const               dataSize,
    UINT32 const               dataOffset,
    IModeTable* const          modeTable,
    BYTE const                 alignment)
{
    UINT32 entrySize = GetSymbolEntrySize(alignment);
    m_count = tableSize / entrySize;

    UINT32 expected = m_count * entrySize;
    bool   valid    = tableSize == expected;

    if (!valid)
    {
        PARAMETER_LOG_ERROR(SetManager, "Invalid symbol table size: %d, expected: %d\n", tableSize, expected);
    }

    if (valid && dataTableSize > 0 && dataTableOffset > 0)
    {
        entrySize = GetDataTableEntrySize();
        if (entrySize > 0)
        {
            m_dtCount = dataTableSize / entrySize;

            expected = m_dtCount * entrySize;
            valid    = dataTableSize == expected;

            if (!valid)
            {
                PARAMETER_LOG_ERROR(SetManager, "Invalid data table size: %d, expected: %d\n", dataTableSize, expected);
            }
        }
        else
        {
            PARAMETER_LOG_ERROR(SetManager, "Data table entry size is zero\n");
        }
    }

    // Validate symbol table size
    if (valid)
    {
        m_entries     = PARAMETER_NEW ParameterFileSymbolTableEntry[m_count + 1];
        m_entryLookup = PARAMETER_NEW EntryData[m_count + 1];

        if (dataTableSize > 0 && dataTableOffset > 0)
        {
            m_dtEntries     = PARAMETER_NEW ParameterFileSymbolTableEntry[m_dtCount + 1];
            m_dtEntryLookup = PARAMETER_NEW EntryData[m_dtCount + 1];
        }
        else
        {
            m_dtEntries     = m_entries;
            m_dtEntryLookup = m_entryLookup;
            m_dtCount       = m_count;
        }

        // m_entryLookup will ultimately point to the entry index in the m_dupEntries, or 0 if there is no duplicate.
        // Start by building m_dupEntries to point to itself or the duplicate entry.
        // Then check if there are enough duplicates to warrant an entry in m_dupEntries (uses the duplicates variable).
        // Then rebuild the m_entryLookup to point to the entry index in m_dupEntries or zero if not valid

        valid = m_entries != nullptr && m_entryLookup != nullptr && m_dtEntries != nullptr && m_dtEntryLookup != nullptr;

        if (!valid)
        {
            PARAMETER_LOG_ERROR(SetManager, "Failed to allocate entry/lookup tables\n");
        }

        FileName   = fileName;
        File       = file;
        m_fileRef  = file != nullptr ? 1 : 0;
        FileLength = fileLength;
        ModeTable  = modeTable;

        if (valid)
        {
            UINT8* readBuffer = buffer;
            UINT32 readLength = bufLength;
            UINT32 readPos    = tableOffset;
            if (file != nullptr)
            {
                readLength = tableSize;
                readBuffer = file->Read(tableOffset, tableSize);
                readPos    = 0;
                valid      = readBuffer != nullptr;
                if (!valid)
                {
                    PARAMETER_LOG_ERROR(SetManager, "Failed to read symbol table buffer (offset: %d, size: %d) from binary file\n", tableOffset, tableSize);
                }
            }

            if (valid)
            {
                valid = ReadTable(readBuffer, readLength, &readPos, m_count, m_entries, m_entryLookup, m_dupCount,
                    m_dupEntries, dataOffset, modeTable, true, alignment);
                if (!valid)
                {
                    PARAMETER_LOG_ERROR(SetManager, "Failed to read symbol table from binary file\n");
                }
            }

            if (file != nullptr)
            {
                PARAMETER_DELETE[] readBuffer;
                readBuffer = nullptr;
            }

            if (valid && dataTableSize > 0 && dataTableOffset > 0)
            {
                readPos = dataTableOffset;

                if (file != nullptr)
                {
                    readLength = dataTableSize;
                    readBuffer = file->Read(dataTableOffset, dataTableSize);
                    readPos    = 0;
                    valid      = readBuffer != nullptr;
                    if (!valid)
                    {
                        PARAMETER_LOG_ERROR(SetManager, "Failed to read data table buffer (offset: %d, size: %d) from binary file\n", tableOffset, tableSize);
                    }
                }

                if (valid)
                {
                    valid = ReadTable(readBuffer, readLength, &readPos, m_dtCount, m_dtEntries, m_dtEntryLookup, m_dtDupCount,
                        m_dtDupEntries, dataOffset, modeTable, false, alignment);
                    if (!valid)
                    {
                        PARAMETER_LOG_ERROR(SetManager, "Failed to read data table from binary file\n");
                    }
                }

                if (file != nullptr)
                {
                    PARAMETER_DELETE[] readBuffer;
                    readBuffer = nullptr;
                }
            }
        }
    }

    if (valid)
    {
        SymbolTableOffset = tableOffset;
        SymbolTableSize   = tableSize;
        DataTableOffset   = dataTableOffset;
        DataTableSize     = dataTableSize;
        DataOffset        = dataOffset;
        DataSize          = dataSize;
        if (file == nullptr)
        {
            Data = &buffer[dataOffset];
        }
    }

    return valid;
}

bool ParameterFileSymbolTable::Find(
    ParameterFileSymbolTableEntry& entry,
    UINT32 id,
    bool const isModule)
{
    bool       result      = false;
    UINT32     count       = isModule ? m_count       : m_dtCount;
    const EntryData* const entryLookup = isModule ? m_entryLookup : m_dtEntryLookup;
    const ParameterFileSymbolTableEntry* const entries = isModule ? m_entries : m_dtEntries;

    if (SetManager != nullptr && id > 0 && id < count + 1)
    {
        if (entries != nullptr)
        {
            entry = entries[id];
            result = true;
        }
        else if (FileName != nullptr)
        {
            const ParameterMutexLock rlock {SetManager->ReloadMutex, true, true}; // Shared LOCK

            FileReference(1);

            if (File != nullptr)
            {
                UINT32 pos = entryLookup[id].Position;
                entry.Init(this);
                result = entry.Load(Data, FileName, File, FileLength, &pos, DataOffset, ModeTable, isModule, Alignment);
            }

            FileReference(-1);
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(SetManager, "Find: Symbol table entry ID %d out of range 1 - %d\n", id, count);
    }

    return result;
}

bool ParameterFileSymbolTable::ReadPointerEntry(
    ParameterFileSymbolTableEntry& pointerEntry,
    ParameterFileSymbolTableEntry* const entry,
    BYTE const alignment)
{
    UINT32 id = 0;

    if (entry->Read(&id, alignment))
    {
        return Find(pointerEntry, id, false);
    }
    return false;
}

DupObjInfo* ParameterFileSymbolTable::FindDupObject(
    void* const obj)
{
    DupObjInfo* result = nullptr;

    // m_dupInfoMutex Mutex is locked where this function is called
    if (m_dupInfoMap.find(obj) != m_dupInfoMap.end())
    {
        result = m_dupInfoMap[obj];
    }

    return result;
}

DupObjInfo* ParameterFileSymbolTable::GetDupObject(
    bool const        isModule,
    UINT32 const      id,
    UINT32 const      count,
    const CHAR* const file,
    UINT32      const line)
{
    DupObjInfo*               result      = nullptr;
    const UINT32              dupCount    = isModule ? m_dupCount    : m_dtDupCount;
    const UINT32              lookupCnt   = isModule ? m_count       : m_dtCount;
    PList<DupObjInfo*>* const dupEntries  = isModule ? m_dupEntries  : m_dtDupEntries;
    const EntryData* const    entryLookup = isModule ? m_entryLookup : m_dtEntryLookup;

    if (dupEntries != nullptr && entryLookup != nullptr && id <= lookupCnt && entryLookup[id].Dup > 0 &&
        entryLookup[id].Dup < dupCount)
    {
        const ParameterMutexLock lock{ m_dupInfoMutex, true, true }; // Shared LOCK
        const PList<DupObjInfo*>& dupObject = dupEntries[entryLookup[id].Dup];
        for (UINT32 i = 0; result == nullptr && i < dupObject.Count(); i++)
        {
            DupObjInfo* const obj = dupObject[i];
            if (obj != nullptr && obj->Line == line && PARAMETER_STRCMP(obj->File, file) == 0)
            {
                if (obj->Count == count)
                {
                    result = obj;
                    if (obj->Object != nullptr)
                    {
                        result->RefCount++;
                    }
                }
                else
                {
                    PARAMETER_LOG_DEBUG(SetManager, "GetDupObject found duplicate objects with differing counts: IsModule: %d, ID: %u, Count: %u, Found: %u, Loc: %s:%d\n",
                        isModule, id, count, obj->Count, file, line);
                }
            }
        }
    }
    else if (entryLookup != nullptr && id <= lookupCnt)
    {
        // entryLookup[id].Dup == 0 is not an error, ignore
        if (entryLookup[id].Dup > 0)
        {
            PARAMETER_LOG_ERROR(SetManager, "GetDupObject called with invalid values, This: %p, IsModule: %d, ID: %u, DupCount: %u, LookupCnt: %u, DupEntries: %p, EntryLookup: %p, EntryLookup[id].Dup: %u, Loc: %s:%d\n",
                this, isModule, id, dupCount, lookupCnt, dupEntries, entryLookup, entryLookup[id].Dup, file, line);
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(SetManager, "GetDupObject called with invalid values, This: %p, IsModule: %d, ID: %u, DupCount: %u, LookupCnt: %u, DupEntries: %p, EntryLookup: %p, Loc: %s:%d\n",
            this, isModule, id, dupCount, lookupCnt, dupEntries, entryLookup, file, line);
    }
    return result;
}

DupObjInfo* ParameterFileSymbolTable::SetDupObject(
    bool const        isModule,
    UINT32 const      id,
    void* const       obj,
    UINT32 const      count,
    const CHAR* const file,
    UINT32 const      line)
{
    DupObjInfo*               result      = nullptr;
    const UINT32              dupCount    = isModule ? m_dupCount    : m_dtDupCount;
    const UINT32              lookupCnt   = isModule ? m_count       : m_dtCount;
    PList<DupObjInfo*>* const dupEntries  = isModule ? m_dupEntries  : m_dtDupEntries;
    const EntryData* const    entryLookup = isModule ? m_entryLookup : m_dtEntryLookup;

    if (obj != nullptr && dupEntries != nullptr && entryLookup != nullptr && id <= lookupCnt &&
        entryLookup[id].Dup > 0 && entryLookup[id].Dup < dupCount)
    {
        const ParameterMutexLock lock{ m_dupInfoMutex, false, true }; // Unique LOCK
        result = GetDupObject(isModule, id, count, file, line);
        if (result == nullptr)
        {
            PList<DupObjInfo*>& dupObject = dupEntries[entryLookup[id].Dup];
            result = PARAMETER_NEW DupObjInfo(count, file, line);
            result->Object = obj;
            result->RefCount++;
            m_dupInfoMap[obj] = result;
            dupObject.Add(result);
        }
        else if (result->Object == nullptr)
        {
            // entryLookup[id].Dup == 0 is not an error, ignore
            if (result->RefCount > 0)
            {
                PARAMETER_LOG_ERROR(SetManager, "SetDuplicateObject called on existing object with nullptr Object and RefCount %d:"
                    " ID: %d, DupID: %d, Loc: %s:%d, ignored\n", result->RefCount, id, entryLookup[id].Dup, file, line);
            }
            result->Object = obj;
            result->RefCount++;
            m_dupInfoMap[obj] = result;
        }
        else
        {
            result = nullptr;
        }
    }
    else if (entryLookup != nullptr && id <= lookupCnt)
    {
        // entryLookup[id].Dup == 0 is not an error, ignore
        if (entryLookup[id].Dup > 0)
        {
            PARAMETER_LOG_ERROR(SetManager, "SetDupObject called with invalid values, This: %p, IsModule: %d, ID: %u, Obj: %p, DupCount: %u, LookupCnt: %u, DupEntries: %p, EntryLookup: %p, EntryLookup[id].Dup: %u, Loc: %s:%d\n",
                this, isModule, id, obj, dupCount, lookupCnt, dupEntries, entryLookup, entryLookup[id].Dup, file, line);
        }
    }
    else
    {
        PARAMETER_LOG_ERROR(SetManager, "SetDupObject called with invalid values, This: %p, IsModule: %d, ID: %u, Obj: %p, DupCount: %u, LookupCnt: %u, DupEntries: %p, EntryLookup: %p, Loc: %s:%d\n",
            this, isModule, id, obj, dupCount, lookupCnt, dupEntries, entryLookup, file, line);
    }

    return result;
}

bool ParameterFileSymbolTable::ReleaseDupObject(
    void* const obj)
{
    bool deleteObject = obj != nullptr;

    if (obj != nullptr)
    {
        const ParameterMutexLock lock{ m_dupInfoMutex, false, true }; // Unique LOCK
        DupObjInfo* const dupInfo = FindDupObject(obj);

        if (dupInfo != nullptr)
        {
            deleteObject = DupObjInfo::Release(dupInfo);
            if (deleteObject)
            {
                m_dupInfoMap.erase(obj);
            }
        }
    }

    return deleteObject;
}

UINT32 ParameterFileSymbolTable::GetSymbolEntrySize(
    BYTE const alignment)
{
    UINT32 result = ParameterFileSymbolTableEntry::SYMBOL_ENTRY_SIZE;
    if (Version <= AlignedEndVersion)
    {
        result = ParameterFileSymbolTableEntry::SYMBOL_ENTRY_ALIGNED_SIZE;
        if (alignment == 1)
        {
            if (Version < AlignedEndVersion)
            {
                result = ParameterFileSymbolTableEntry::SYMBOL_ENTRY_SIZE_LEGACY;
            }
            else
            {
                result = ParameterFileSymbolTableEntry::SYMBOL_ENTRY_UNALIGNED_SIZE;
            }
        }
    }

    return result;
}

UINT32 ParameterFileSymbolTable::GetDataTableEntrySize()
{
    UINT32 result = ParameterFileSymbolTableEntry::DATA_TABLE_ENTRY_SIZE;
    if (Version < DataTableVersion)
    {
        result = 0;
    }

    return result;
}

UINT32 ParameterFileSymbolTable::GetModeEntrySize(
    BYTE const alignment)
{
    UINT32 result = ParameterFileSymbolTableEntry::MODE_ENTRY_SIZE;
    if (alignment == 1)
    {
        if (Version < AlignedEndVersion)
        {
            result = ParameterFileSymbolTableEntry::MODE_ENTRY_SIZE_LEGACY;
        }
        else if (Version >= ModeFlagsVersion)
        {
            result = ParameterFileSymbolTableEntry::MODE_ENTRY_FLAGS_SIZE;
        }
        else
        {
            result = ParameterFileSymbolTableEntry::MODE_ENTRY_UNALIGNED_SIZE;
        }
    }
    else if (Version < LinkVersion)
    {
        result = ParameterFileSymbolTableEntry::MODE_ENTRY_SIZE_NOLINK;
    }

    return result;
}

void ParameterFileSymbolTable::FreeEntryData()
{
    if (m_dtEntries != m_entries)
    {
        PARAMETER_DELETE[] m_dtEntries;
    }

    PARAMETER_DELETE[] m_entries;
    m_entries   = nullptr;
    m_dtEntries = nullptr;
}

void ParameterFileSymbolTable::FreeLookupData()
{
    if (m_dtEntryLookup != m_entryLookup)
    {
        PARAMETER_DELETE[] m_dtEntryLookup;
    }

    PARAMETER_DELETE[] m_entryLookup;
    m_entryLookup   = nullptr;
    m_dtEntryLookup = nullptr;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// FileReference
///
/// @brief Adjusts the reference count, and opens or closes the file as needed
/// @param adjust - how much to adjust the reference count
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void ParameterFileSymbolTable::FileReference(INT32 adjust)
{
    if (File != nullptr)
    {
        (void)File->Reference(adjust);
    }
    else
    {
        PARAMETER_LOG_DEBUG(SetManager, "FileReference called with null File object, Adjust: %d\n", adjust);
    }
}
