/*===================== begin_copyright_notice ==================================

Copyright (c) 2017 Intel Corporation

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


======================= end_copyright_notice ==================================*/

#include <cstring>
#include <iomanip>
# if GED_VALIDATION_API
# include <algorithm>
# endif
#include "common/ged_string_utils.h"
#include "xcoder/ged_ins.h"

using std::setw;
using std::setfill;
using std::hex;
using std::memset;
#if GED_VALIDATION_API
using std::sort;
using std::end;
using std::next;
using std::cout;
using std::endl;
using std::dec;
using std::left;
using std::stringstream;
#endif

#ifdef _WIN32
# define MEMCPY(D,S,N) memcpy_s(D,N,S,N)
#else
using std::memcpy;
# define MEMCPY(D,S,N) memcpy(D,S,N)
#endif


/*************************************************************************************************
 * class GEDIns static data members
 *************************************************************************************************/

const int64_t GEDIns::signExtendTable[] =
{
    (int64_t)0xffffffffffffffff, // 0
    (int64_t)0xfffffffffffffffe, // 1
    (int64_t)0xfffffffffffffffc, // 2
    (int64_t)0xfffffffffffffff8, // 3
    (int64_t)0xfffffffffffffff0, // 4
    (int64_t)0xffffffffffffffe0, // 5
    (int64_t)0xffffffffffffffc0, // 6
    (int64_t)0xffffffffffffff80, // 7
    (int64_t)0xffffffffffffff00, // 8
    (int64_t)0xfffffffffffffe00, // 9
    (int64_t)0xfffffffffffffc00, // 10
    (int64_t)0xfffffffffffff800, // 11
    (int64_t)0xfffffffffffff000, // 12
    (int64_t)0xffffffffffffe000, // 13
    (int64_t)0xffffffffffffc000, // 14
    (int64_t)0xffffffffffff8000, // 15
    (int64_t)0xffffffffffff0000, // 16
    (int64_t)0xfffffffffffe0000, // 17
    (int64_t)0xfffffffffffc0000, // 18
    (int64_t)0xfffffffffff80000, // 19
    (int64_t)0xfffffffffff00000, // 20
    (int64_t)0xffffffffffe00000, // 21
    (int64_t)0xffffffffffc00000, // 22
    (int64_t)0xffffffffff800000, // 23
    (int64_t)0xffffffffff000000, // 24
    (int64_t)0xfffffffffe000000, // 25
    (int64_t)0xfffffffffc000000, // 26
    (int64_t)0xfffffffff8000000, // 27
    (int64_t)0xfffffffff0000000, // 28
    (int64_t)0xffffffffe0000000, // 29
    (int64_t)0xffffffffc0000000, // 30
    (int64_t)0xffffffff80000000, // 31
    (int64_t)0xffffffff00000000, // 32
    (int64_t)0xfffffffe00000000, // 33
    (int64_t)0xfffffffc00000000, // 34
    (int64_t)0xfffffff800000000, // 35
    (int64_t)0xfffffff000000000, // 36
    (int64_t)0xffffffe000000000, // 37
    (int64_t)0xffffffc000000000, // 38
    (int64_t)0xffffff8000000000, // 39
    (int64_t)0xffffff0000000000, // 40
    (int64_t)0xfffffe0000000000, // 41
    (int64_t)0xfffffc0000000000, // 42
    (int64_t)0xfffff80000000000, // 43
    (int64_t)0xfffff00000000000, // 44
    (int64_t)0xffffe00000000000, // 45
    (int64_t)0xffffc00000000000, // 46
    (int64_t)0xffff800000000000, // 47
    (int64_t)0xffff000000000000, // 48
    (int64_t)0xfffe000000000000, // 49
    (int64_t)0xfffc000000000000, // 50
    (int64_t)0xfff8000000000000, // 51
    (int64_t)0xfff0000000000000, // 52
    (int64_t)0xffe0000000000000, // 53
    (int64_t)0xffc0000000000000, // 54
    (int64_t)0xff80000000000000, // 55
    (int64_t)0xff00000000000000, // 56
    (int64_t)0xfe00000000000000, // 57
    (int64_t)0xfc00000000000000, // 58
    (int64_t)0xf800000000000000, // 59
    (int64_t)0xf000000000000000, // 60
    (int64_t)0xe000000000000000, // 61
    (int64_t)0xc000000000000000, // 62
    (int64_t)0x8000000000000000, // 63
};


/*************************************************************************************************
 * class GEDIns API functions
 *************************************************************************************************/

GED_RETURN_VALUE GEDIns::Init(const /* GED_MODEL */ uint8_t modelId, /* GED_OPCODE */ uint32_t opcode)
{
    GED_RETURN_VALUE ret = GED_RETURN_VALUE_SUCCESS;
    if (modelId >= numOfSupportedModels)
    {
        ret = GED_RETURN_VALUE_INVALID_MODEL;
        return ret;
    }
    _modelId = modelId;
    _opcode = invalidOpcode;
    _decodingTable = NULL;
    ret = SetOpcode(opcode);
#if defined(GED_VALIDATE)
    if (GED_RETURN_VALUE_OPCODE_NOT_SUPPORTED != ret)
    {
        GEDASSERT(invalidOpcode != _opcode);
        GEDASSERT(NULL != _decodingTable);
    }
#endif // GED_VALIDATE
    return ret;
}


GED_RETURN_VALUE GEDIns::Decode(const /* GED_MODEL */ uint8_t modelId, const unsigned char* rawBytes, const unsigned int size)
{
    if (NULL == rawBytes)
    {
        return GED_RETURN_VALUE_NULL_POINTER;
    }
    if (modelId >= numOfSupportedModels)
    {
        return GED_RETURN_VALUE_INVALID_MODEL;
    }
    if (size < GED_COMPACT_INS_SIZE)
    {
        // Buffer isn't large enough to extract opcode and compact bit.
        return GED_RETURN_VALUE_BUFFER_TOO_SHORT;
    }
    ClearStatus();
    _modelId = modelId;
    ExtractOpcode(rawBytes);
    _decodingTable = GetCurrentModelData().opcodeTables[_opcode].nativeDecoding;
    if (NULL == _decodingTable)
    {
        return GED_RETURN_VALUE_OPCODE_NOT_SUPPORTED;
    }
    ExtractCmptCtrl(rawBytes);
    if (!IsCompactValid())
    {
        if (size < GED_NATIVE_INS_SIZE)
        {
            // Buffer isn't large enough for the given Native instruction.
            return GED_RETURN_VALUE_BUFFER_TOO_SHORT;
        }
    }

    GED_RETURN_VALUE ret = GED_RETURN_VALUE_SUCCESS;
    if (IsCompactValid())
    {
        SetInstructionBytes(_compactBytes, rawBytes, size, GED_COMPACT_INS_SIZE);
        ret = BuildNativeInsFromCompact(); // should always succeed, however this is not true, see Mantis 3755
        if (GED_RETURN_VALUE_SUCCESS != ret) return ret;

        // The API function IsCompact() checks the GED_INS_STATUS_COMPACT_ENCODED bit, so we must enforce the encoding restrictions
        // and set the instruction's compact form as encoded.
        ApplyCompactEncodingMasks(_compactBytes); // enforce the per-instruction encoding restrictions for the compact format

#if defined (GED_VALIDATE)
        // TODO: Save the current instruction bytes, then call BuildNativeInsFromCompact again and compare the two
        //       to prevent field/padding conflicts in the XMLs.
#endif // GED_VALIDATE
    }
    else
    {
        SetInstructionBytes(_nativeBytes, rawBytes, size, GED_NATIVE_INS_SIZE);
        SetNativeValid();

        // The API function IsModified() checks the GED_INS_STATUS_NATIVE_ENCODED bit, so we must enforce the encoding restrictions
        // and set the instruction's native format as encoded.
        ApplyNativeEncodingMasks(); // enforce the per-instruction encoding restrictions for the native format
    }
    GEDASSERT(IsNativeValid());
    return ret;
}


GED_RETURN_VALUE GEDIns::Encode(const GED_INS_TYPE insType, unsigned char* rawBytes)
{
    GED_RETURN_VALUE ret = GED_RETURN_VALUE_SUCCESS;
    if (GED_INS_TYPE_COMPACT == insType) // retrieve the compact instruction bytes
    {
        if (!IsCompactEncoded())
        {
            if (!IsCompactValid())
            {
                GEDASSERT(IsNativeValid());
                // Try to build a compact format for this instruction.
                if (!BuildCompactInsFromNative())
                {
                    GEDASSERT(!IsCompactValid());
                    ret = GED_RETURN_VALUE_NO_COMPACT_FORM;
                    return ret; // the instruction does not have a compact format in its current state
                }
                GEDASSERT(IsCompactValid());
            }
            ApplyCompactEncodingMasks(_compactBytes); // enforce the per-instruction encoding restrictions for the compact format
        }
        GEDASSERT(IsCompactValid());
        if (NULL != rawBytes)
        {
            MEMCPY(rawBytes, _compactBytes, GED_COMPACT_INS_SIZE); // copy the compact instruction bytes
        }
    }
    else // retrieve the native instruction bytes
    {
        GEDASSERT(GED_INS_TYPE_NATIVE == insType);
        if (!IsNativeEncoded())
        {
            if (!IsNativeValid())
            {
                GEDASSERT(IsCompactValid());
                BuildNativeInsFromCompact(); // should always succeed, however this is not true, see Mantis 3755
                GEDASSERT(IsNativeValid());
            }
            ApplyNativeEncodingMasks(); // enforce the per-instruction encoding restrictions for the native format
        }
        GEDASSERT(IsNativeValid());
        if (NULL != rawBytes)
        {
            MEMCPY(rawBytes, _nativeBytes, GED_NATIVE_INS_SIZE); // copy the native instruction bytes
        }
    }
    return ret;
}


#if GED_VALIDATION_API

GED_RETURN_VALUE GEDIns::CountCompacted(unsigned int& count)
{
    if (!IsNativeEncoded())
    {
        if (!IsNativeValid())
        {
            // Can only use the native format as a reference, so first uncompact the instruction
            GEDASSERT(IsCompactValid());
            BuildNativeInsFromCompact(); // Should always succeed, however this is not true, see Mantis 3755
            GEDASSERT(IsNativeValid());
        }
        ApplyNativeEncodingMasks(); // enforce the per-instruction encoding restrictions for the native format
    }
    GEDASSERT(IsNativeValid());
    if (!CountCompactFormats(count))
    {
        GEDASSERT(!IsCompactValid());
        return GED_RETURN_VALUE_NO_COMPACT_FORM; // the instruction does not have a compact form in its current state
    }
    return GED_RETURN_VALUE_SUCCESS;
}


GED_RETURN_VALUE GEDIns::RetrieveAllCompactedFormats(const unsigned int size, unsigned char* compactBytesArray)
{
    GEDFORASSERT(unsigned int count = 0);
    GEDASSERT(GED_RETURN_VALUE_SUCCESS == CountCompacted(count));
    GEDASSERT((size / GED_COMPACT_INS_SIZE) <= count);
    if (NULL == compactBytesArray)
    {
        return GED_RETURN_VALUE_NULL_POINTER;
    }
    if (!IsNativeEncoded())
    {
        if (!IsNativeValid())
        {
            // Can only use the native format as a reference, so first uncompact the instruction
            GEDASSERT(IsCompactValid());
            BuildNativeInsFromCompact(); // should always succeed, however this is not true, see Mantis 3755
            GEDASSERT(IsNativeValid());
        }
        ApplyNativeEncodingMasks(); // enforce the per-instruction encoding restrictions for the native format
    }
    GEDASSERT(IsNativeValid());
    if (!BuildAllCompactedFormats(compactBytesArray, size))
    {
        GEDASSERT(!IsCompactValid());
        return GED_RETURN_VALUE_NO_COMPACT_FORM; // the instruction does not have a compact form in its current state
    }
    unsigned char* compactBytesPtr = compactBytesArray;
    for (unsigned int i = 0; i < size / GED_COMPACT_INS_SIZE; ++i)
    {
        ApplyCompactEncodingMasks(compactBytesPtr);
        compactBytesPtr += GED_COMPACT_INS_SIZE;
    }
    return GED_RETURN_VALUE_SUCCESS;
}


GED_RETURN_VALUE GEDIns::PrintFieldBitLocation(const /* GED_INS_FIELD */ uint32_t field) const
{
    GEDASSERT(field < GetCurrentModelData().numberOfInstructionFields);
    GED_RETURN_VALUE status = GED_RETURN_VALUE_INVALID_FIELD;
    ged_ins_decoding_table_t table = _decodingTable;
    const unsigned char* bytes = _nativeBytes;
    if (IsCompactValid())
    {
        table = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;
         bytes = _compactBytes;
    }
    const string& fieldName = fieldNameByField[field];
    const ged_ins_field_entry_t* dataEntry = GetInstructionDataEntry(table, field);
    if (NULL == dataEntry)
    {
        cout << "Field " << fieldName << " is invalid for the current instruction." << endl;
        return status;
    }

    vector<ged_ins_field_mapping_fragment_t> mappingFragments;
    bool hasFixedValue = false;

    // Record padding, if exists.
    hasFixedValue |= RecordPadding(mappingFragments, dataEntry);

    // Record the explicit position fragments, if exists.
    hasFixedValue |= RecordPosition(mappingFragments, dataEntry);

    // Sort the vector by the from.lowBit
    sort(mappingFragments.begin(), mappingFragments.end());

    // Merge fragments which are consecutive by both from and to values.
    MergeFragments(mappingFragments);

    // Print the bit mapping of the field.
    const uint32_t maxBitPosition = (uint32_t)mappingFragments.back()._from._highBit;
    const uint32_t maxBitPositionLength = static_cast<uint32_t>(DecStr(maxBitPosition).length());
    const uint32_t bitSpacing = maxBitPositionLength * 2 + 1; // printing 2 bit positions, plus a colon
    const uint64_t rawValue = GetField<uint64_t>(bytes, table, field, GED_VALUE_TYPE_ENCODED, status);
    static const string implicitString = "Implicit";
    GEDASSERT(GED_RETURN_VALUE_SUCCESS == status);
    cout << "Field " << fieldName << ", width: " << (uint32_t)dataEntry->_bitSize <<
            ((uint32_t)dataEntry->_bitSize == 1 ? " bit, " : " bits, ") << "raw value: " <<
            HexStr(rawValue) << " (" << BinStr(rawValue, dataEntry->_bitSize) << "b)" << endl;
    for (const auto& it : mappingFragments)
    {
        const size_t fragmentBitLength = it._from._highBit - it._from._lowBit + 1;
        stringstream strm;
        strm << (uint32_t)it._from._lowBit << ":" << (uint32_t)it._from._highBit;
        cout << left << setfill(' ') << setw(bitSpacing) << strm.str() << " = ";
        if (it._fixed)
            cout << implicitString << " (" << BinStr(it._value, fragmentBitLength) << "b)" << endl;
        else
        {
            strm.str(string()); // clear stream
            strm.clear();
            uint64_t mask = 0;
            for (uint8_t i = it._from._lowBit; i <= it._from._highBit; ++i)
            {
                mask |= (1ULL << i);
            }
            const uint64_t fragmentVal = ((rawValue & mask) >> it._from._lowBit);
            strm << (uint32_t)it._to._lowBit << ":" << (uint32_t)it._to._highBit;
            cout << left << setfill(' ') << setw(hasFixedValue ? implicitString.length() : bitSpacing) << strm.str() << " (" <<
                    BinStr(fragmentVal, fragmentBitLength) << "b)" << endl;
        }
    }
    return GED_RETURN_VALUE_SUCCESS;
}
#endif //GED_VALIDATION_API


uint32_t GEDIns::GetFieldSize(const /* GED_INS_FIELD */ uint32_t field) const
{
    if (GetCurrentModelData().numberOfInstructionFields <= field) return 0;

    // First try the native format.
    const ged_ins_field_entry_t* dataEntry = GetInstructionDataEntry(_decodingTable, field);
    if (NULL != dataEntry) return (uint32_t)dataEntry->_bitSize;

    // The field was not found in the native format, try the compact format (if it exists).
    const ged_ins_decoding_table_t compactTable = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;
    if (NULL != compactTable)
    {
        const ged_ins_field_entry_t* compactDataEntry = GetInstructionDataEntry(compactTable, field);
        if (NULL != compactDataEntry) return (uint32_t)compactDataEntry->_bitSize;
    }

    // The field is not valid in either format.
    return 0;
}


GED_RETURN_VALUE GEDIns::SetOpcode(/* GED_OPCODE */ uint32_t opcode)
{
    GED_RETURN_VALUE ret = GED_RETURN_VALUE_SUCCESS;
    const uint32_t highestOpcodeValue = GED_MAX_ENTRIES_IN_OPCODE_TABLE - 1;
    if (!GEDRestrictionsHandler::ConvertEnumeratedValueToRawEncodedValue(opcode, highestOpcodeValue, GetCurrentModelData().Opcodes))
    {
        ret = GED_RETURN_VALUE_OPCODE_NOT_SUPPORTED;
        return ret;
    }

    if (opcode == _opcode)
    {
        return ret;
    }
    _opcode = opcode;

    const ged_ins_decoding_table_t decodingTable = GetCurrentModelData().opcodeTables[opcode].nativeDecoding;
    GEDASSERT(NULL != decodingTable);
    if (decodingTable != _decodingTable)
    {
        ClearStatus();
        memset(_nativeBytes, 0, GED_NATIVE_INS_SIZE);

        // Set the new decoding table.
        _decodingTable = decodingTable;
        SetNativeValid(); // mark that the native instruction bytes are valid
        SetNativeOpcode(); // set the opcode in the native instruction bytes

        // Check if the new opcode has a compact format.
        if (GetCurrentModelData().opcodeTables[_opcode].compactDecoding)
        {
            // TODO: Once the compact bit is set in the encoding masks, remove the next line and uncomment the one after that (Mantis 3732).
            *(reinterpret_cast<uint64_t*>(_compactBytes)) = compactInsInitializer;
            //memset(_compactBytes, 0, GED_COMPACT_INS_SIZE);
            SetCompactValid(); // mark that the compact instruction bytes are valid
            SetCompactOpcode(); // set the opcode in the compact instruction bytes
        }
    }
    GEDASSERT(IsValid()); // at least one format must be valid
    return ret;
}

# if GED_EXPERIMENTAL
GED_RETURN_VALUE GEDIns::SetRawBits(const uint8_t lowBit, const uint8_t highBit, const uint64_t val)
{
    GEDASSERT(lowBit < GED_NATIVE_INS_SIZE_BITS);
    GEDASSERT(highBit < GED_NATIVE_INS_SIZE_BITS);
    GEDASSERT(lowBit <= highBit);
    GEDASSERT(IsNativeValid());
    const uint8_t lowDword = lowBit / GED_DWORD_BITS;
    const uint8_t highDword = highBit / GED_DWORD_BITS;
    const unsigned int numberOfDwords = highDword - lowDword;
    ged_ins_field_position_fragment_t pos;
    if (lowDword == highDword)
    {
        pos._lowBit = lowBit;
        pos._highBit = highBit;
        pos._dwordIndex = lowBit / GED_DWORD_BITS;
        pos._shift = lowBit % GED_DWORD_BITS;
        pos._bitMask = rightShiftedMasks[(highBit - lowBit)] << pos._shift;
        SetFragment(_nativeBytes, pos, val);
    }
    else
    {
        NYI;
    }
    SetEncodingMasksDisabled();
    return GED_RETURN_VALUE_SUCCESS;
}
# endif // GED_EXPERIMENTAL

/*************************************************************************************************
 * class GEDIns protected member functions
 *************************************************************************************************/

uint8_t GEDIns::GetFieldWidth(const uint16_t field, const bool interpField /* = false */) const
{
    const ged_ins_field_entry_t* entry =
        GetInstructionDataEntry((interpField) ? GetCurrentModelData().pseudoFields : _decodingTable, field);
    if (NULL == entry) return 0;
    if (IsVariableField(entry))
    {
        return entry->_restrictions[0]->_fieldType._attr._bits;
    }
    else
    {
        return entry->_bitSize;
    }
}


string GEDIns::GetInstructionBytes() const
{
    return (IsCompact()) ? GetInstructionBytes(_compactBytes, GED_NUM_OF_COMPACT_INS_DWORDS)
                         : GetInstructionBytes(_nativeBytes, GED_NUM_OF_NATIVE_INS_DWORDS);
}


/*************************************************************************************************
 * class GEDIns private member functions
 *************************************************************************************************/

void GEDIns::SetInstructionBytes(unsigned char* dst, const unsigned char* src, unsigned int size, const unsigned int maxSize) const
{
    if (size > maxSize)
    {
        size = maxSize; // given buffer is too large, ignore the rest of the bytes.
    }
    MEMCPY(dst, src, size);
}


uint32_t GEDIns::GetMappedField(const /* GED_INS_FIELD */ uint32_t field, const unsigned char* validBits, bool& extracted) const
{
    GEDASSERT(NULL != validBits);
    GEDASSERT(field < GetCurrentModelData().numberOfInstructionFields);
    GEDASSERT(field == _decodingTable[field]._field);

    // Traverse the intermediate tables (if necessary).
    extracted = false;
    const ged_ins_field_entry_t* dataEntry = GetMappedInstructionDataEntry(_decodingTable, field, validBits, extracted);
    if (!extracted) return MAX_UINT32_T;
    GEDASSERT(NULL != dataEntry);

    // Now that we are at the bottommost table, extract the actual data if they were already mapped. The "extracted" out parameter is
    // already set to TRUE.
    switch (dataEntry->_entryType)
    {
    case GED_TABLE_ENTRY_TYPE_CONSECUTIVE:
        if (0 != ExtractConsecutiveEntryValue(validBits, dataEntry->_consecutive._position)) break;
        return ExtractConsecutiveEntryValue(_nativeBytes, dataEntry->_consecutive._position);
    case GED_TABLE_ENTRY_TYPE_FRAGMENTED:
        if (0 != ExtractFragmentedEntryValue<uint32_t>(validBits, dataEntry)) break;
        return ExtractFragmentedEntryValue<uint32_t>(_nativeBytes, dataEntry);
    default:
        GEDASSERT(0);
    }

    // Mark that some (or all) necessary bits for this field were not yet mapped.
    extracted = false;
    return MAX_UINT32_T;
}


const ged_ins_field_entry_t* GEDIns::GetDependentInstructionDataEntry(ged_ins_decoding_table_t table,
                                                                      /* GED_INS_FIELD */ uint32_t tableIndex) const
{
    GEDASSERT(NULL != table);
    while (GED_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
    {
        const ged_ins_field_next_table_t* nextTable = &table[tableIndex]._nextTable;
        GED_RETURN_VALUE ret = GED_RETURN_VALUE_INVALID_FIELD;
        tableIndex = GetField<uint32_t>(_nativeBytes, _decodingTable, nextTable->_tableKey, GED_VALUE_TYPE_ENCODED, ret);

        // GetField is expected to succeed since the models ensure that the dependent fields are always valid. However, we need to
        // account for invalid instructions, i.e. instructions with an illegal encoding. Such instructions may have invalid values for
        // valid fields. If a dependee field is in itself a dependent field, it may even be an invalid field in the given (poorly
        // encoded) instruction.
        if (GED_RETURN_VALUE_INVALID_FIELD == ret || GED_RETURN_VALUE_INVALID_VALUE == ret) return NULL;
        GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
        table = nextTable->_tablePtr;
        GEDASSERT(NULL != table);
    }

    // Verify that this is a legal entry.
    GEDASSERT(table[tableIndex]._entryType < GED_TABLE_ENTRY_TYPE_SIZE);

    // Verify that the field is supported in this format.
    if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == table[tableIndex]._entryType)
    {
        return NULL;
    }

    // Verify that the final entry actually holds data.
    GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != table[tableIndex]._entryType);

    return &table[tableIndex];
}


const ged_ins_field_entry_t* GEDIns::GetDependentMappedInstructionDataEntry(ged_ins_decoding_table_t table,
                                                                            /* GED_INS_FIELD */ uint32_t tableIndex,
                                                                            const unsigned char* validBits, bool& extracted) const
{
    GEDASSERT(NULL != table);
    GEDASSERT(NULL != validBits);
    while (GED_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
    {
        // Extract the value from the _nativeBytes array if it is valid (i.e. if it has already been mapped)
        const ged_ins_field_next_table_t* nextTable = &table[tableIndex]._nextTable;
        tableIndex = GetMappedField(nextTable->_tableKey, validBits, extracted);
        if (!extracted) return NULL; // the field is not valid in the _nativeBytes array (i.e. it was not yet mapped)
        GEDASSERT(MAX_UINT32_T != tableIndex);
        table = nextTable->_tablePtr;
        GEDASSERT(NULL != table);
    }

    // Verify that this is a legal entry.
    GEDASSERT(table[tableIndex]._entryType < GED_TABLE_ENTRY_TYPE_SIZE);

    // Verify that the field is supported in this format.
    GEDASSERT(GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED != table[tableIndex]._entryType);

    // Verify that the final entry actually holds data.
    GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != table[tableIndex]._entryType);

    extracted = true;
    return &table[tableIndex];
}


GED_RETURN_VALUE GEDIns::BuildNativeInsFromCompact()
{
    GEDASSERT(IsCompactValid()); // nothing to do if the instruction is not compacted
    memset(_nativeBytes, 0, GED_NATIVE_INS_SIZE);

    const ged_ins_decoding_table_t compactTable = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;  // map from this
    if (NULL == compactTable) return GED_RETURN_VALUE_NO_COMPACT_FORM;

    const ged_compact_mapping_table_t mappingTable = GetCurrentModelData().opcodeTables[_opcode].compactMapping;
    GEDASSERT(NULL != mappingTable);

    //  Mapping algorithm:
    //
    //  Iterate compactTable. For each valid entry i.e. GED_TABLE_ENTRY_TYPE_CONSECUTIVE, GED_TABLE_ENTRY_TYPE_FRAGMENTED
    //  or GED_TABLE_ENTRY_TYPE_NEXT_TABLE:
    //      a) Get compact field value (from _compactBytes using compactTable). If the field depends on another field which was not
    //           yet mapped, add it to the unmapped list and continue to the next field.
    //      b) Find mapping type:
    //          One of the four combinations between value/index and consecutive/fragmented. The mapping type may have dependencies,
    //          in which case there will be a next table entry and finding the mapping type will require iterating the mapping chain.
    //          The mapping type may be GED_MAPPING_TABLE_ENTRY_TYPE_NOT_SUPPORTED if the field is not supported in this format, in
    //          which case the corresponding entry in the decoding table will be of type GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED as well.
    //      c) If this is a value mapping (either consecutive or fragmented):
    //          Map bits from _compactBytes to _nativeBytes according to the mapping table entry.
    //      d) If this is an index mapping, i.e. mapping from a compaction table:
    //          i)  Get the value from the compaction table (ged_compaction_table_t), in which case the field's value serves
    //              as the index of the entry in the compaction table.
    //          ii) Map the bits from the compaction table entry value to _nativeBytes.
    //      e) Repeat steps (a)-(d) for every field in the unmapped list until it is empty.

    // The validBits array will be used to note which bits of the instruction's native format were already mapped (and can be used as
    // dependencies). We start with all bits set and clear the relevant bits as they are being mapped. Thus, in order to check if a
    // certain field was mapped or not, the relevant bits are simply compared to zero.
    unsigned char validBits[GED_NATIVE_INS_SIZE];
    memset(validBits, 0xff, GED_NATIVE_INS_SIZE);
    set<uint32_t> unMapped;

    for (uint32_t i = 0; i < GetCurrentModelData().numberOfInstructionFields; ++i)
    {
        GEDASSERT(compactTable[i]._entryType < GED_TABLE_ENTRY_TYPE_SIZE); // verify that this is a valid entry
        if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == compactTable[i]._entryType) continue; // this field is not supported in this format
        if (!MapCurrentField(compactTable, mappingTable, i, validBits))
        {
            // The field depends on another field which was not yet mapped. Add it to the unmapped list for later processing.
            GEDASSERT(0 == unMapped.count(i));
            unMapped.insert(i);
            continue;
        }
    }

    // In every iteration of the outer loop, try to map the first unmapped (dependent) field in the unMapped set. If it depends on
    // another dependent field which was not yet mapped, skip it and try the rest one by one (the inner loop) until one field is
    // mapped successfully. If no field was mapped successfully, it means that the remaining fields have a cyclic dependency on each
    // other, in which case an error is emitted.
    const set<uint32_t>::const_iterator end = unMapped.end();
    while (!unMapped.empty())
    {
        set<uint32_t>::const_iterator it = unMapped.begin();
        for (; end != it; ++it)
        {
            if (MapCurrentField(compactTable, mappingTable, *it, validBits)) break;
        }

        // Make sure a dependent field was mapped successfully.
        if (end == it)
        {
            // TODO: Call this function after adding logging to GED.
            // EmitMappingCyclicDependencyError(unMapped, validBits);
            return GED_RETURN_VALUE_BAD_COMPACT_ENCODING;
        }
        unMapped.erase(it);
    }

    // This instruction is marked as compact in the _status field, but the instruction bits in the _nativeBytes array should reflect
    // a native instruction now that it is expanded.
    SetNonCompact();
    SetNativeValid();
    return GED_RETURN_VALUE_SUCCESS;
}


bool GEDIns::MapCurrentField(const ged_ins_decoding_table_t compactTable, const ged_compact_mapping_table_t mappingTable,
                             const /* GED_INS_FIELD */ uint32_t field, unsigned char* validBits)
{
    GEDASSERT(NULL != compactTable);
    GEDASSERT(NULL != mappingTable);
    GEDASSERT(NULL != validBits);

    // Get the mapping table entry for this field.
    const ged_compact_mapping_entry_t* mappingEntry = GetCompactionMappingEntry(mappingTable, field, validBits);
    if (NULL == mappingEntry) return false; // when building compact from native but the mapping is invalid
    if (GED_MAPPING_TABLE_ENTRY_TYPE_NO_MAPPING == mappingEntry->_entryType) return true; // early out
    GEDASSERT(mappingEntry->_field == field);

    // Get the compact field value.
    GED_RETURN_VALUE ret = GED_RETURN_VALUE_INVALID_FIELD;
    uint32_t mappingValue = GetField<uint32_t>(_compactBytes, compactTable, field, GED_VALUE_TYPE_ENCODED, ret);
    GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);

    // Map the compact field to the native field(s).
    switch (mappingEntry->_entryType)
    {
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE:
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        GEDASSERT(MAX_UINT32_T >= mappingEntry->_compactionTable[mappingValue]);
        MapRawBytes((uint32_t)mappingEntry->_compactionTable[mappingValue], &mappingEntry->_consecutive._to,
                    mappingEntry->_consecutive._fromMask, validBits);
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_CONSECUTIVE:
        MapRawBytes(mappingValue, &mappingEntry->_consecutive._to, mappingEntry->_consecutive._fromMask, validBits);
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED:
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        MapRawBytes(mappingEntry->_compactionTable[mappingValue], mappingEntry->_fragmented._numOfMappingFragments,
                    mappingEntry->_fragmented._fragments, validBits);
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED:
        MapRawBytes(mappingValue, mappingEntry->_fragmented._numOfMappingFragments, mappingEntry->_fragmented._fragments, validBits);
        break;
    default:
        GEDASSERT(0);
    }
    return true;
}


const ged_compact_mapping_entry_t* GEDIns::GetDependentCompactionMappingEntry(ged_compact_mapping_table_t table,
                                                                              /* GED_INS_FIELD */ uint32_t tableIndex,
                                                                              const unsigned char* validBits) const
{
    GEDASSERT(NULL != table);
    GEDASSERT(NULL != validBits);
    while (GED_MAPPING_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
    {
        const ged_compact_mapping_next_table_t* nextTable = &table[tableIndex]._nextTable;
        // Extract the value from the _nativeBytes array if it is valid (i.e. if it has already been mapped)
        bool extracted = false;
        tableIndex = GetMappedField(nextTable->_tableKey, validBits, extracted);
        if (!extracted) return NULL; // the field is not valid in the _nativeBytes array (i.e. it was not yet mapped)
        table = nextTable->_tablePtr;
        GEDASSERT(NULL != table);
    }

    // Verify that this is a legal entry.
    GEDASSERT(table[tableIndex]._entryType < GED_MAPPING_TABLE_ENTRY_TYPE_SIZE);

    // Verify that the field is supported in this format.
    if (GED_MAPPING_TABLE_ENTRY_TYPE_NOT_SUPPORTED == table[tableIndex]._entryType) return NULL;

    // Verify that the final entry actually holds data.
    GEDASSERT(GED_MAPPING_TABLE_ENTRY_TYPE_NEXT_TABLE != table[tableIndex]._entryType);

    return &table[tableIndex];
}


void GEDIns::MapRawBytes(const uint32_t src, const ged_ins_field_position_fragment_t* to, const uint32_t fromMask,
                         unsigned char* validBits)
{
    GEDASSERT(NULL != to);
    GEDASSERT(NULL != validBits);
    uint32_t value = (src & fromMask);
    GEDASSERT(0 <= to->_shift);
    value <<= to->_shift;
    SetMappedBits(to->_dwordIndex, to->_bitMask, value, validBits);
}


void GEDIns::MapRawBytes(const uint32_t src, const uint32_t numOfFragments, const ged_compact_mapping_fragment_t* fragments,
                         unsigned char* validBits)
{
    GEDASSERT(NULL != fragments);
    GEDASSERT(NULL != validBits);
    for (uint32_t i = 0; i < numOfFragments; ++i)
    {
        if (GED_COMPACT_MAPPING_TYPE_REP == fragments[i]._mappingType)
        {
            GEDASSERT(0 == fragments[i]._from._dwordIndex); // src only has 32 bits
            MapReppedValue(src, &fragments[i]._to, &fragments[i]._from, validBits);
        }
        else if (GED_COMPACT_MAPPING_TYPE_1x1 == fragments[i]._mappingType)
        {
            GEDASSERT(0 == fragments[i]._from._dwordIndex); // src only has 32 bits
            MapOneToOneValue(src, &fragments[i]._to, &fragments[i]._from, validBits);
        }
        else if (GED_COMPACT_MAPPING_TYPE_FIXED == fragments[i]._mappingType)
        {
            GEDASSERT(0 == fragments[i]._from._dwordIndex); // src only has 32 bits
            MapFixedValue(fragments[i]._from._lowBit, &fragments[i]._to, validBits);
        }
        else { GEDASSERT(0); }
    }
}


void GEDIns::MapRawBytes(const uint64_t src, const uint32_t numOfFragments, const ged_compact_mapping_fragment_t* fragments,
                         unsigned char* validBits)
{
    GEDASSERT(NULL != fragments);
    GEDASSERT(NULL != validBits);
    for (uint32_t i = 0; i < numOfFragments; ++i)
    {
        if (GED_COMPACT_MAPPING_TYPE_REP == fragments[i]._mappingType)
        {
            GEDASSERT(fragments[i]._from._dwordIndex < 2); // src only has 64 bits
            MapReppedValue((0 == fragments[i]._from._dwordIndex) ? (uint32_t)src : (uint32_t)(src >> GED_DWORD_BITS),
                           &fragments[i]._to, &fragments[i]._from, validBits);
        }
        else if (GED_COMPACT_MAPPING_TYPE_1x1 == fragments[i]._mappingType)
        {
            GEDASSERT(fragments[i]._from._dwordIndex < 2); // src only has 64 bits
            MapOneToOneValue((0 == fragments[i]._from._dwordIndex) ? (uint32_t)src : (uint32_t)(src >> GED_DWORD_BITS),
                             &fragments[i]._to, &fragments[i]._from, validBits);
        }
        else if (GED_COMPACT_MAPPING_TYPE_FIXED == fragments[i]._mappingType)
        {
            NYI; // no fixed mapping for fragmented index mapping
        }
        else { GEDASSERT(0); }
    }
}


void GEDIns::MapReppedValue(const uint32_t src, const ged_ins_field_position_fragment_t* to,
                            const ged_ins_field_position_fragment_t* from, unsigned char* validBits)
{
    GEDASSERT(NULL != to);
    GEDASSERT(NULL != from);
    const uint8_t fromSize = FragmentSize(from);
    const uint8_t toSize = FragmentSize(to);
    GEDASSERT(fromSize > 0);               // verify that the source size is non-zero (about to be used as a denominator)
    GEDASSERT(toSize >= fromSize);         // verify that the target is at least as wide as the source
    GEDASSERT(0 == (toSize % fromSize));   // verify that the target is a whole multiple of the source
    const uint8_t numOfReps = toSize / fromSize;
    const uint32_t rep = ((src & from->_bitMask) >> from->_shift);
    uint32_t value = rep;
    for (uint8_t i = 1; i < numOfReps; ++i)
    {
        value <<= fromSize;
        value |= rep;
    }
    value <<= to->_shift;
    SetMappedBits(to->_dwordIndex, to->_bitMask, value, validBits);
}


void GEDIns::MapOneToOneValue(const uint32_t src, const ged_ins_field_position_fragment_t* to,
                              const ged_ins_field_position_fragment_t* from, unsigned char* validBits)
{
    GEDASSERT(NULL != to);
    GEDASSERT(NULL != from);
    uint32_t value = (src & from->_bitMask);
    const int8_t shift = from->_shift - to->_shift;
    if (shift > 0)
    {
        value >>= shift;
    }
    else
    {
        value <<= abs(shift);
    }
    SetMappedBits(to->_dwordIndex, to->_bitMask, value, validBits);
}


void GEDIns::MapFixedValue(const uint32_t value, const ged_ins_field_position_fragment_t* to, unsigned char* validBits)
{
    GEDASSERT(NULL != to);
    SetMappedBits(to->_dwordIndex, to->_bitMask, value << to->_shift, validBits);
}


void GEDIns::EmitMappingCyclicDependencyError(const set<uint32_t>& unMapped, const unsigned char* validBits) const
{
    GEDASSERT(NULL != validBits);
    set<uint32_t>::const_iterator it = unMapped.begin();
    const set<uint32_t>::const_iterator end = unMapped.end();
    string depErrorStr = DecStr(*it);
    for (++it; end != it; ++it)
    {
        depErrorStr += ", " + DecStr(*it);
    }
#if defined(GED_VALIDATE)
    stringstream strm;
    strm << setfill('0') << hex;
    for (int i = GED_NUM_OF_NATIVE_INS_DWORDS - 1; i >= 0; --i)
    {
        strm << setw(8) << ((uint32_t*)validBits)[i];
    }
    depErrorStr += "\nValidBits: 0x" + strm.str();
#endif // GED_VALIDATE
    // TODO: This should use GED logs instead of emitting an error. Also, consider changing the function name.
    GEDERROR("Unable to map remaining unmapped fields, probably due to an implicit dependency cycle: " + depErrorStr);
}


bool GEDIns::BuildCompactInsFromNative()
{
    GEDASSERT(IsNativeValid());
    GEDASSERT(!IsCompactValid());
    GEDASSERT(!IsCompactEncoded());
    memset(_compactBytes, 0, GED_COMPACT_INS_SIZE); // clear the compact bytes

    const ged_ins_decoding_table_t compactTable = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;  // map to this
    if (NULL == compactTable) return false; // the instruction doesn't have a compact format

    const ged_compact_mapping_table_t mappingTable = GetCurrentModelData().opcodeTables[_opcode].compactMapping;
    GEDASSERT(NULL != mappingTable);

    // Traverse all the compact instruction fields. For every valid field, collect its value from the native instruction and set that
    // value in the compact instruction bytes.
    unsigned char orMask[GED_NATIVE_INS_SIZE] = { 0 }; // used in CollectCurrentField, see documentation therein for its usage
    BuildNativeOrMask(orMask); // prepare the or-mask
    for (uint32_t i = 0; i < GetCurrentModelData().numberOfInstructionFields; ++i)
    {
        GEDASSERT(compactTable[i]._entryType < GED_TABLE_ENTRY_TYPE_SIZE); // verify that this is a valid entry
        if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == compactTable[i]._entryType) continue; // this field is not supported in this format
        if (!CollectCurrentField(compactTable, mappingTable, orMask, i)) return false;
    }

    // Set the CmptCtrl bit - is was cleared by the loop above when reverse-mapping the CmptCtrl field from the native instruction.
    SetCompact(_compactBytes);
    SetCompactValid(); // the compact format is now valid
    return true;
}


#if GED_VALIDATION_API
bool GEDIns::CountCompactFormats(unsigned int& count)
{
    unsigned int tempCounter = 1;
    unsigned char orMask[GED_NATIVE_INS_SIZE] = { 0 }; // used in CountCurrentField, see documentation therein for its usage
    BuildNativeOrMask(orMask); // prepare the or-mask
    const ged_ins_decoding_table_t compactTable = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;  // map to this
    if (NULL == compactTable) return false; // the instruction doesn't have a compact format

    const ged_compact_mapping_table_t mappingTable = GetCurrentModelData().opcodeTables[_opcode].compactMapping;
    GEDASSERT(NULL != mappingTable);
    for (uint32_t i = 0; i < GetCurrentModelData().numberOfInstructionFields; ++i)
    {
        GEDASSERT(compactTable[i]._entryType < GED_TABLE_ENTRY_TYPE_SIZE); // verify that this is a valid entry
        if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == compactTable[i]._entryType) continue; // this field is not supported in this format
        if (!CountCurrentField(compactTable, mappingTable, orMask, i, tempCounter)) return false;
    }
    count = tempCounter;
    return true;
}


bool GEDIns::CountCurrentField(const ged_ins_decoding_table_t compactTable, const ged_compact_mapping_table_t mappingTable,
                               const unsigned char* const orMask, const /* GED_INS_FIELD */ uint32_t field, unsigned int& count)
{
    GEDASSERT(NULL != compactTable);
    GEDASSERT(NULL != mappingTable);
    // Get the mapping table entry for this field.
    static const unsigned char validBits[GED_NATIVE_INS_SIZE] = { 0 }; // assume that all the native instruction fields are valid
    const ged_compact_mapping_entry_t* mappingEntry = GetCompactionMappingEntry(mappingTable, field, validBits);
    if (NULL == mappingEntry) return false; // when building compact from native but the mapping is invalid
    GEDASSERT(mappingEntry->_field == field);

    // For each entry type, count the amount of valid values: Value mappings have 1 valid value, and index mappings require iterating
    // over all indexes, counting the valid ones and multiplying in the end.
    switch (mappingEntry->_entryType)
    {
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE:
    {
        // This is an index field, so the actual value to be encoded is an index in the field's compaction table. Try to find a
        // compaction table entry that matches the collected value. If such an entry is found, "count" is multiplied by the amount
        // of valid entries in the compact table, otherwise the function returns FALSE without setting the field.
        uint64_t qwval = (uint64_t)ExtractConsecutiveEntryValue(_nativeBytes, mappingEntry->_consecutive._to);
        const uint64_t valMask = (uint64_t)ExtractConsecutiveEntryValue(orMask, mappingEntry->_consecutive._to);
        GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
        GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
        const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        if (!CountCompactionTableEntry(qwval, valMask, tableSize, mappingEntry->_compactionTable, count)) return false;
        break;
    }
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_CONSECUTIVE:
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED:
    {
        // See documentation in the GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE case above.
        uint64_t qwval = 0;
        uint64_t tempValMask = 0;
        if (!CollectFragmentedEntryQWValue(qwval, _nativeBytes, mappingEntry)) return false;
        if (!CollectFragmentedEntryQWValue(tempValMask, orMask, mappingEntry)) return false;
        const uint64_t valMask = tempValMask;
        GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
        GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
        const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        if (!CountCompactionTableEntry(qwval, valMask, tableSize, mappingEntry->_compactionTable, count)) return false;
        break;
    }
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED:
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_NO_MAPPING:
        break;
    default:
        GEDASSERT(0);
    }
    return true;
}


bool GEDIns::CountCompactionTableEntry(uint64_t& val, const uint64_t& valMask, const uint32_t tableSize,
                                       ged_compaction_table_t table, unsigned int& count) const
{
    uint32_t counter = 0;
    GEDASSERT(0 != tableSize);
    GEDASSERT(tableSize < GED_MAX_ENTRIES_IN_COMPACT_TABLE); // sanity check
    val |= valMask;
    for (uint32_t i = 0; i < tableSize; ++i)
    {
        if ((table[i] | valMask) == val)
        {
            ++counter;
        }
    }
    count *= counter;
    return (0 != counter);
}


bool GEDIns::BuildAllCompactedFormats(unsigned char* compactBytesArray, const unsigned int size)
{
    const ged_ins_decoding_table_t compactTable = GetCurrentModelData().opcodeTables[_opcode].compactDecoding;  // map to this
    if (NULL == compactTable) return false; // the instruction doesn't have a compact format

    const ged_compact_mapping_table_t mappingTable = GetCurrentModelData().opcodeTables[_opcode].compactMapping;
    GEDASSERT(NULL != mappingTable);

    // Traverse all the compact instruction fields. For every valid non-indexed field, collect its value from the native instruction
    // and set that value in the compact instruction template.
    unsigned char compactBytesTemplate[GED_COMPACT_INS_SIZE] = { 0 };
    for (uint32_t i = 0; i < GetCurrentModelData().numberOfInstructionFields; ++i)
    {
        GEDASSERT(compactTable[i]._entryType < GED_TABLE_ENTRY_TYPE_SIZE); // verify that this is a valid entry
        if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == compactTable[i]._entryType) continue; // this field is not supported in this format
        if (!CollectCurrentValueField(compactTable, mappingTable, i, compactBytesTemplate)) return false;
    }
    SetCompact(compactBytesTemplate);

    // Traverse all the compact instruction fields. For every valid indexed field, collect all possible options.
    // Basically, create a Cartesian product of all possible indexes which create a valid compact encoding.
    unsigned char orMask[GED_NATIVE_INS_SIZE] = { 0 }; // used in CollectCurrentMappedFields, see documentation therein for its usage
    BuildNativeOrMask(orMask); // prepare the or-mask
    bool succeeded = true;
    vector<vector<unsigned char> > compactBytesIndexedVector = CollectCurrentMappedFields(compactTable, mappingTable, orMask, 0,
                                                                                          succeeded);
    if (!succeeded) return false;

    // Merge the mapped values with the indexed ones
    memset(compactBytesArray, 0x0, size); // clear the entire preallocated buffer
    unsigned char* ptr = compactBytesArray;
    for (uint32_t i = 0; i < size / GED_COMPACT_INS_SIZE; ++i)
    {
        for (uint32_t j = 0; j < GED_NUM_OF_COMPACT_INS_DWORDS; ++j)
        {
            vector<unsigned char>* tmpVecPtr = &compactBytesIndexedVector[i];
            unsigned char* tmpWordPtr = &((*tmpVecPtr)[0]);
            ((uint32_t*)ptr)[j] = ((uint32_t*)compactBytesTemplate)[j] | ((uint32_t*)tmpWordPtr)[j];
        }
        ptr += GED_COMPACT_INS_SIZE;
    }
    return true;

    // Set an arbitrary valid encoding into the instruction's compact bytes.
    MEMCPY(_compactBytes, compactBytesArray, GED_COMPACT_INS_SIZE);
    SetCompactValid();
    return true;
}


bool GEDIns::CollectCurrentValueField(const ged_ins_decoding_table_t compactTable, const ged_compact_mapping_table_t mappingTable,
                                      const /* GED_INS_FIELD */ uint32_t field, unsigned char* buf)
{
    GEDASSERT(NULL != compactTable);
    GEDASSERT(NULL != mappingTable);

    // Get the mapping table entry for this field.
    static const unsigned char validBits[GED_NATIVE_INS_SIZE] = { 0 }; // assume that all the native instruction fields are valid
    const ged_compact_mapping_entry_t* mappingEntry = GetCompactionMappingEntry(mappingTable, field, validBits);
    if (NULL == mappingEntry) return false; // when building compact from native but the mapping is invalid
    GEDASSERT(mappingEntry->_field == field);

    // Collect the compact field from the native field(s).
    uint32_t val = MAX_UINT32_T;
    switch (mappingEntry->_entryType)
    {
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE:
        return true;
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_CONSECUTIVE:
        val = ExtractConsecutiveEntryValue(_nativeBytes, mappingEntry->_consecutive._to);
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED:
        return true;
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED:
        if (!CollectFragmentedEntryDWValue(val, _nativeBytes, mappingEntry)) return false;
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_NO_MAPPING:
        return true;
    default:
        GEDASSERT(0);
    }

    // Set the field in the compact bytes. The given value is already the raw value, no need to convert it if it has an enumeration.
    GEDFORASSERT(GED_RETURN_VALUE ret = )
        SetField(buf, compactTable, field, GED_VALUE_TYPE_ENCODED, val);
    GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
    return true;
}


vector<vector<unsigned char> > GEDIns::CollectCurrentMappedFields(const ged_ins_decoding_table_t compactTable,
                                                                  const ged_compact_mapping_table_t mappingTable,
                                                                  const unsigned char* const orMask,
                                                                  /* GED_INS_FIELD */ uint32_t field, bool& succeeded)
{
    // Collecting all mapped fields algorithm:
    //
    // CollectCurrentMappedFields is a backtracking recursion designed to generate a Cartesian product of all mapped fields.
    // The slight difference from a regular AxBxCx... Cartesian product where each element is on its own in the n-tuple, here each
    // element in the n-tuple consists of fields and their location. A is a field, set into its position in the compacted bytes by
    // calling SetField()
    //
    // How the recursion works:
    // Stop condition: when finished traversing all fields, return a vector with a single, empty encoding.
    // Recursion step:
    // 1        Declare temporary vector of compact instruction "res"
    // 2        For each Compact Encoding "it" in CollectCurrentMappedFields(..., field + 1, ...):
    // 2.1        For each valid index "compactedTableIndex" in the compacted table:
    // 2.1.1        Create a buffer "buf" and copy the compact encoding of "it" to it
    // 2.1.2        Set the compactedTableIndex in the appropriate location in buf
    // 2.1.3        Push "buf" into the vector of compacted instructions "res".
    // 3        Return "res".

    GEDASSERT(NULL != compactTable);
    GEDASSERT(NULL != mappingTable);
    GEDASSERT(field < GetCurrentModelData().numberOfInstructionFields);

    static const unsigned char validBits[GED_NATIVE_INS_SIZE] = { 0 }; // assume that all the native instruction fields are valid
    const ged_compact_mapping_entry_t* mappingEntry;

    // Locate the next Index-mapped field.
    while (true)
    {
        if (GetCurrentModelData().numberOfInstructionFields == field)
        {
            // Stop condition.
            vector<vector<unsigned char> > stubVector;
            vector<unsigned char> stubEncoding(8); // insert an empty encoding to the vector
            stubVector.push_back(stubEncoding);
            return stubVector;
        }
        if (GED_TABLE_ENTRY_TYPE_NOT_SUPPORTED == compactTable[field]._entryType)
        {
            ++field;
            continue;
        }
        mappingEntry = GetCompactionMappingEntry(mappingTable, field, validBits);
        GEDASSERT(NULL != mappingTable); // this is potentially wrong, see other GetCompactionMappingEntry "if" checks
        if (GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED == mappingEntry->_entryType  ||
            GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE == mappingEntry->_entryType)
        {
            break; // Handle index fields - enter the body of the function.
        }
        ++field;
    }

    // Step 1
    vector<vector<unsigned char> > res;

    // Step 2
    vector<vector<unsigned char> > v = CollectCurrentMappedFields(compactTable, mappingTable, orMask, field + 1, succeeded);
    if (!succeeded) return v;

    for (vector<vector<unsigned char> >::const_iterator it = v.begin(); it != v.end(); ++it)
    {
        switch (mappingEntry->_entryType)
        {
        case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE:
        {
            uint32_t val = MAX_UINT32_T;
            uint64_t qwval = (uint64_t)ExtractConsecutiveEntryValue(_nativeBytes, mappingEntry->_consecutive._to);
            const uint64_t valMask = (uint64_t)ExtractConsecutiveEntryValue(orMask, mappingEntry->_consecutive._to);
            GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
            GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
            const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
            GEDASSERT(NULL != mappingEntry->_compactionTable);
            GEDASSERT(0 != tableSize);
            GEDASSERT(tableSize < GED_MAX_ENTRIES_IN_COMPACT_TABLE); // sanity check
            qwval |= valMask;
            // Step 2.1
            for (unsigned int compactedTableIndex = 0; compactedTableIndex < tableSize; ++compactedTableIndex)
            {
                if ((mappingEntry->_compactionTable[compactedTableIndex] | valMask) == qwval)
                {
                    val = (uint32_t)compactedTableIndex;
                    unsigned char buf[GED_COMPACT_INS_SIZE];
                    // Step 2.1.1
                    std::copy(it->begin(), it->end(), buf);
                    // Step 2.1.2
                    GEDFORASSERT(GED_RETURN_VALUE ret = )
                        SetField(buf, compactTable, field, GED_VALUE_TYPE_ENCODED, val);
                    GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
                    // Step 2.1.3
                    res.push_back(vector<unsigned char>(buf, buf + GED_COMPACT_INS_SIZE * sizeof(buf[0])));
                }
            }
            if (MAX_UINT32_T == val) succeeded = false;
            break;
        }
        case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED:
        {
            uint32_t val = MAX_UINT32_T;
            uint64_t qwval = 0;
            uint64_t tempValMask = 0;
            if (!CollectFragmentedEntryQWValue(qwval, _nativeBytes, mappingEntry)) succeeded = false;
            if (!CollectFragmentedEntryQWValue(tempValMask, orMask, mappingEntry)) succeeded = false;
            if (!succeeded) break;
            const uint64_t valMask = tempValMask;
            GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
            GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
            const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
            GEDASSERT(NULL != mappingEntry->_compactionTable);
            GEDASSERT(0 != tableSize);
            GEDASSERT(tableSize < GED_MAX_ENTRIES_IN_COMPACT_TABLE); // sanity check
            qwval |= valMask;
            // Step 2.1
            for (unsigned int compactedTableIndex = 0; compactedTableIndex < tableSize; ++compactedTableIndex)
            {
                if ((mappingEntry->_compactionTable[compactedTableIndex] | valMask) == qwval)
                {
                    val = (uint32_t)compactedTableIndex;
                    unsigned char buf[GED_COMPACT_INS_SIZE];
                    // Step 2.1.1
                    std::copy(it->begin(), it->end(), buf);
                    // Step 2.1.2
                    GEDFORASSERT(GED_RETURN_VALUE ret = )
                        SetField(buf, compactTable, field, GED_VALUE_TYPE_ENCODED, val);
                    GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
                    // Step 2.1.3
                    res.push_back(vector<unsigned char>(buf, buf + GED_COMPACT_INS_SIZE * sizeof(buf[0])));
                }
            }
            if (MAX_UINT32_T == val) succeeded = false;
            break;
        }
        default:
            GEDASSERT(0);
        }
    }
    // Step 3.
    return res;
}
#endif // GED_VALIDATION_API


bool GEDIns::CollectCurrentField(const ged_ins_decoding_table_t compactTable, const ged_compact_mapping_table_t mappingTable,
                                 const unsigned char* const orMask, const /* GED_INS_FIELD */ uint32_t field)
{
    GEDASSERT(NULL != compactTable);
    GEDASSERT(NULL != mappingTable);

    // Get the mapping table entry for this field.
    static const unsigned char validBits[GED_NATIVE_INS_SIZE] = { 0 }; // assume that all the native instruction fields are valid
    const ged_compact_mapping_entry_t* mappingEntry = GetCompactionMappingEntry(mappingTable, field, validBits);
    if (NULL == mappingEntry) return false; // when building compact from native but the mapping is invalid
    GEDASSERT(mappingEntry->_field == field);

    // Collect the compact field from the native field(s).
    uint32_t val = MAX_UINT32_T;
    switch (mappingEntry->_entryType)
    {
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE:
    {
        // This is an index field, so the actual value to be encoded is an index in the field's compaction table. Try to find a
        // compaction table entry that matches the collected value. If such an entry is found, "val" is changed to that entry's
        // index, otherwise the function returns FALSE without setting the field.

        // If a certain bit in a compaction table entry is set but it is mapped to a reserved field in the native instruction, it
        // will be cleared in the native instruction. As a result it will also be cleared in the collected value, which means that
        // the collected value will not match the compaction table entry. To overcome this, an or-mask of the reserved bits is
        // applied to the collected value and to the compaction table entries when they are being compared. This may result in
        // several compaction table entries that match the collected value, in which case the first entry found will be returned.

        uint64_t qwval = (uint64_t)ExtractConsecutiveEntryValue(_nativeBytes, mappingEntry->_consecutive._to);
        const uint64_t valMask = (uint64_t)ExtractConsecutiveEntryValue(orMask, mappingEntry->_consecutive._to);
        GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
        GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
        const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        if (!FindCompactionTableEntry(qwval, valMask, tableSize, mappingEntry->_compactionTable)) return false;
        GEDASSERT(MAX_UINT32_T >= qwval);
        val = (uint32_t)qwval;
        break;
    }
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_CONSECUTIVE:
        val = ExtractConsecutiveEntryValue(_nativeBytes, mappingEntry->_consecutive._to);
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED:
    {
        // See documentation in the GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_CONSECUTIVE case above.
        uint64_t qwval = 0;
        uint64_t tempValMask = 0;
        if (!CollectFragmentedEntryQWValue(qwval, _nativeBytes, mappingEntry)) return false;
        if (!CollectFragmentedEntryQWValue(tempValMask, orMask, mappingEntry)) return false;
        const uint64_t valMask = tempValMask;
        GEDASSERT(GED_TABLE_ENTRY_TYPE_NEXT_TABLE != compactTable[field]._entryType); // index fields must be explicit
        GEDASSERT(MAX_UINT32_T > MaxValue(compactTable[field]));
        const uint32_t tableSize = BitsToNumOfValues(compactTable[field]._bitSize); // size of the compaction table
        GEDASSERT(NULL != mappingEntry->_compactionTable);
        if (!FindCompactionTableEntry(qwval, valMask, tableSize, mappingEntry->_compactionTable)) return false;
        GEDASSERT(MAX_UINT32_T >= qwval);
        val = (uint32_t)qwval;
        break;
    }
    case GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED:
        if (!CollectFragmentedEntryDWValue(val, _nativeBytes, mappingEntry)) return false;
        break;
    case GED_MAPPING_TABLE_ENTRY_TYPE_NO_MAPPING:
        return true;
    default:
        GEDASSERT(0);
    }

    // Set the field in the compact bytes. The given value is already the raw value, no need to convert it if it has an enumeration.
    GEDFORASSERT(GED_RETURN_VALUE ret = )
        SetField(_compactBytes, compactTable, field, GED_VALUE_TYPE_ENCODED, val);
    GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
    return true;
}


bool GEDIns::CollectFragmentedEntryDWValue(uint32_t& fullVal, const unsigned char* bytes, const ged_compact_mapping_entry_t* mappingEntry) const
{
    GEDASSERT(NULL != bytes);
    GEDASSERT(NULL != mappingEntry);
    GEDASSERT(GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED == mappingEntry->_entryType ||
              GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED == mappingEntry->_entryType);
    GEDASSERT(mappingEntry->_fragmented._numOfMappingFragments > 1);

    // The field is fragmented, so all fragments will eventually be gathered in fullVal.
    fullVal = 0;
    for (unsigned int i = 0; i < mappingEntry->_fragmented._numOfMappingFragments; ++i)
    {
        GEDASSERT(0 == mappingEntry->_fragmented._fragments[i]._from._dwordIndex); // the mapping source may only have 32 bits
        uint32_t fragmentVal = 0;
        if (!CollectFragmentValue(fragmentVal, bytes, mappingEntry->_fragmented._fragments[i])) return false;
        fullVal |= fragmentVal;
    }
    return true;
}


bool GEDIns::CollectFragmentedEntryQWValue(uint64_t& val, const unsigned char* bytes, const ged_compact_mapping_entry_t* mappingEntry) const
{
    GEDASSERT(NULL != bytes);
    GEDASSERT(NULL != mappingEntry);
    GEDASSERT(GED_MAPPING_TABLE_ENTRY_TYPE_INDEX_MAPPING_FRAGMENTED == mappingEntry->_entryType ||
              GED_MAPPING_TABLE_ENTRY_TYPE_VALUE_MAPPING_FRAGMENTED == mappingEntry->_entryType);
    GEDASSERT(mappingEntry->_fragmented._numOfMappingFragments > 1);

    // The field is fragmented, so all fragments will eventually be gathered in fullVal. A fragment can't be larger than 32 bits, but
    // the full value may be an entry in a compaction table, which may be 64-bit long.
    val = 0;
    for (unsigned int i = 0; i < mappingEntry->_fragmented._numOfMappingFragments; ++i)
    {
        GEDASSERT(mappingEntry->_fragmented._fragments[i]._from._dwordIndex < 2); // the mapping source may only have 64 bits
        uint32_t fragmentVal = 0;
        if (!CollectFragmentValue(fragmentVal, bytes, mappingEntry->_fragmented._fragments[i])) return false;
        (reinterpret_cast<uint32_t*>(&val))[mappingEntry->_fragmented._fragments[i]._from._dwordIndex] |= fragmentVal;
    }
    return true;
}


bool GEDIns::CollectFragmentValue(uint32_t& val, const unsigned char* bytes, const ged_compact_mapping_fragment_t& mapping) const
{
    val = ((uint32_t*)bytes)[mapping._to._dwordIndex];
    val &= mapping._to._bitMask;
    if (0 == val) return true; // early out - nothing to do
    const int8_t shift = mapping._to._shift - mapping._from._shift;
    if (shift > 0)
    {
        val >>= shift;
    }
    else if (shift < 0)
    {
        val <<= abs(shift);
    }

    // For 1-to-1 mappings there is nothing more to do.
    if (GED_COMPACT_MAPPING_TYPE_1x1 == mapping._mappingType) return true;
    if (GED_COMPACT_MAPPING_TYPE_FIXED == mapping._mappingType)
    {
        return (mapping._from._lowBit == val);
    }
    // Now handle the repped mapping case.
    GEDASSERT(GED_COMPACT_MAPPING_TYPE_REP == mapping._mappingType);
    const uint8_t fromSize = FragmentSize(mapping._from);
    const uint8_t toSize = FragmentSize(mapping._to);
    GEDASSERT(fromSize > 0);               // verify that the source size is non-zero
    GEDASSERT(toSize >= fromSize);         // verify that the target is at least as wide as the source
    GEDASSERT(0 == (toSize % fromSize));   // verify that the target is a whole multiple of the source
    GEDASSERT(0 <= mapping._from._shift);  // verify that the shifting amount is non-negative (in order to shift left)
    // Validate the value is repeated by its substring.
    const uint32_t maxSourceValue = MaxFragmentValue(mapping._from) << mapping._from._shift; // shift the max value to its position
    uint32_t shiftedMask = maxSourceValue;
    uint32_t shiftedRep = val & maxSourceValue;
    // TODO: optimization opportunity: if FragmentSize(fragment) is 1 (which right now, always is), it's possible to simply
    //       compare with all ones (using MaxGragmentValue(mapping._to)) or all-zeros.
    //       The current method used is a general ways, which uses loops and thus slower.
    for (unsigned int i = 0; i < toSize; i += fromSize)
    {
        if (((val & shiftedMask) != shiftedRep)) return false;
        shiftedRep <<= fromSize;
        shiftedMask <<= fromSize;
    }
    val &= maxSourceValue;
    return true;
}


bool GEDIns::FindCompactionTableEntry(uint64_t& val, const uint64_t& valMask, const uint32_t tableSize,
                                      ged_compaction_table_t table) const
{
    GEDASSERT(0 != tableSize);
    GEDASSERT(tableSize < GED_MAX_ENTRIES_IN_COMPACT_TABLE); // sanity check
    val |= valMask;
    for (uint32_t i = 0; i < tableSize; ++i)
    {
        if ((table[i] | valMask) == val)
        {
            val = i;
            return true;
        }
    }
    return false;
}


void GEDIns::ApplyNativeEncodingMasks()
{
# if GED_EXPERIMENTAL
    if (!ShouldApplyEncodingMasks())
    {
        return;
    }
# endif // GED_EXPERIMENTAL
    const ged_instruction_masks_table_t topLevelTable = GetCurrentModelData().opcodeTables[_opcode].nativeEncodingMasks;
    GEDASSERT(NULL != topLevelTable);

    for (unsigned int i = 0; GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS != topLevelTable[i]._entryType; ++i)
    {
        // Traverse the intermediate tables (if necessary).
        unsigned int tableIndex = i;
        ged_instruction_masks_table_t table = topLevelTable;
        while (GED_MASKS_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
        {
            const ged_instruction_masks_next_table_t* nextTable = &table[tableIndex]._nextTable;
            GED_RETURN_VALUE ret = GED_RETURN_VALUE_INVALID_FIELD;
            tableIndex = GetField<uint32_t>(_nativeBytes, _decodingTable, nextTable->_tableKey, GED_VALUE_TYPE_ENCODED, ret);

            // GetField is expected to succeed since the models ensure that the dependent fields are always valid. However, we need to
            // account for invalid instructions, i.e. instructions with an illegal encoding. Such instructions may have invalid values
            // for valid fields. If a dependee field is in itself a dependent field, it may even be an invalid field in the given
            // (poorly encoded) instruction.
            if (GED_RETURN_VALUE_INVALID_FIELD == ret || GED_RETURN_VALUE_INVALID_VALUE == ret) return;
            GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
            table = nextTable->_tablePtr;
            GEDASSERT(NULL != table);
        }
        if (GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS == table[tableIndex]._entryType) continue; // no mask necessary

        // Verify that this is a masks entry.
        GEDASSERTM(GED_MASKS_TABLE_ENTRY_TYPE_MASKS == table[tableIndex]._entryType, DecStr(table[tableIndex]._entryType));
        for (unsigned int dword = 0; dword < GED_NUM_OF_NATIVE_INS_DWORDS; ++dword)
        {
            ((uint32_t*)_nativeBytes)[dword] |= ((uint32_t*)(table[tableIndex]._masks._or))[dword];
            ((uint32_t*)_nativeBytes)[dword] &= ((uint32_t*)(table[tableIndex]._masks._and))[dword];
        }
    }
    SetNativeEncoded();
}


void GEDIns::ApplyCompactEncodingMasks(unsigned char* compactBytes)
{
    const ged_instruction_masks_table_t topLevelTable = GetCurrentModelData().opcodeTables[_opcode].compactEncodingMasks;
    GEDASSERT(NULL != topLevelTable);

    for (unsigned int i = 0; GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS != topLevelTable[i]._entryType; ++i)
    {
        // Traverse the intermediate tables (if necessary).
        unsigned int tableIndex = i;
        ged_instruction_masks_table_t table = topLevelTable;
        while (GED_MASKS_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
        {
            const ged_instruction_masks_next_table_t* nextTable = &table[tableIndex]._nextTable;
            GED_RETURN_VALUE ret = GED_RETURN_VALUE_INVALID_FIELD;
            tableIndex = GetField<uint32_t>(_nativeBytes, _decodingTable, nextTable->_tableKey, GED_VALUE_TYPE_ENCODED, ret);

            // GetField is expected to succeed since the models ensure that the dependent fields are always valid. However, we need to
            // account for invalid instructions, i.e. instructions with an illegal encoding. Such instructions may have invalid values
            // for valid fields. If a dependee field is in itself a dependent field, it may even be an invalid field in the given
            // (poorly encoded) instruction.
            if (GED_RETURN_VALUE_INVALID_FIELD == ret || GED_RETURN_VALUE_INVALID_VALUE == ret) return;
            GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
            table = nextTable->_tablePtr;
            GEDASSERT(NULL != table);
        }
        if (GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS == table[tableIndex]._entryType) continue; // no mask necessary

        // Verify that this is a masks entry.
        GEDASSERT(GED_MASKS_TABLE_ENTRY_TYPE_MASKS == table[tableIndex]._entryType);
        for (unsigned int dword = 0; dword < GED_NUM_OF_COMPACT_INS_DWORDS; ++dword)
        {
            ((uint32_t*)compactBytes)[dword] |= ((uint32_t*)(table[tableIndex]._masks._or))[dword];
            ((uint32_t*)compactBytes)[dword] &= ((uint32_t*)(table[tableIndex]._masks._and))[dword];
        }
    }
    SetCompactEncoded();
}


void GEDIns::BuildNativeOrMask(unsigned char* orMask) const
{
    const ged_instruction_masks_table_t topLevelTable = GetCurrentModelData().opcodeTables[_opcode].nativeEncodingMasks;
    GEDASSERT(NULL != topLevelTable);

    for (unsigned int i = 0; GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS != topLevelTable[i]._entryType; ++i)
    {
        // Traverse the intermediate tables (if necessary).
        unsigned int tableIndex = i;
        ged_instruction_masks_table_t table = topLevelTable;
        while (GED_MASKS_TABLE_ENTRY_TYPE_NEXT_TABLE == table[tableIndex]._entryType)
        {
            const ged_instruction_masks_next_table_t* nextTable = &table[tableIndex]._nextTable;
            GED_RETURN_VALUE ret = GED_RETURN_VALUE_INVALID_FIELD;
            tableIndex = GetField<uint32_t>(_nativeBytes, _decodingTable, nextTable->_tableKey, GED_VALUE_TYPE_ENCODED, ret);
            GEDASSERT(GED_RETURN_VALUE_SUCCESS == ret);
            table = nextTable->_tablePtr;
            GEDASSERT(NULL != table);
        }
        if (GED_MASKS_TABLE_ENTRY_TYPE_NO_MASKS == table[tableIndex]._entryType) continue; // no mask necessary

        // Verify that this is a masks entry.
        GEDASSERTM(GED_MASKS_TABLE_ENTRY_TYPE_MASKS == table[tableIndex]._entryType, DecStr(table[tableIndex]._entryType));
        for (unsigned int dword = 0; dword < GED_NUM_OF_NATIVE_INS_DWORDS; ++dword)
        {
            ((uint32_t*)orMask)[dword] |= ((uint32_t*)(table[tableIndex]._masks._or))[dword];
            ((uint32_t*)orMask)[dword] &= ((uint32_t*)(table[tableIndex]._masks._and))[dword];
        }
    }
}


string GEDIns::GetInstructionBytes(const unsigned char* instructionBytes, int dwords) const
{
    stringstream strm;
    strm << "0x" << setfill('0') << hex;
    for (--dwords; dwords >= 0; --dwords)
    {
        strm << setw(8) << reinterpret_cast<const uint32_t*>(instructionBytes)[dwords];
    }
    return strm.str();
}


#if GED_VALIDATION_API
bool GEDIns::RecordPadding(vector<ged_ins_field_mapping_fragment_t> &mappingFragments, const ged_ins_field_entry_t* dataEntry) const
{
    if (dataEntry->_restrictions && (dataEntry->_restrictions[0]->_restrictionType == GED_FIELD_RESTRICTIONS_TYPE_PADDING))
    {
        const get_field_restriction_padding_t& padding = dataEntry->_restrictions[0]->_padding;
        const uint32_t paddingMask = padding._mask;
        const uint32_t paddingValue = padding._value;
        for (unsigned int i = 0; i < GED_DWORD_BITS; ++i)
        {
            if (0 == (paddingMask & (1 << i))) continue;

            // Bit number i is set to one, count the length of sequential ones.
            unsigned int sequenceLength = 0;
            for (; paddingMask & (1 << i); ++i)
            {
                ++sequenceLength;
            }

            // Record the padding fragment.
            const uint8_t lowBit = i - sequenceLength;
            const uint8_t mask = (1 << (sequenceLength + 1)) - 1; // starting from LSB
            ged_ins_field_mapping_fragment_t fixedFragment;
            fixedFragment._fixed = true;
            fixedFragment._from._lowBit = lowBit;
            fixedFragment._from._highBit = i - 1;
            fixedFragment._value = (paddingValue >> lowBit) & mask;
            mappingFragments.emplace_back(fixedFragment);
        }
        return true;
    }
    return false;
}


bool GEDIns::RecordPosition(vector<ged_ins_field_mapping_fragment_t> &mappingFragments, const ged_ins_field_entry_t* dataEntry) const
{
    bool hasFixedValue = false;
    switch (dataEntry->_entryType)
    {
    case GED_TABLE_ENTRY_TYPE_CONSECUTIVE:
    {
        GEDASSERT(dataEntry->_bitSize <= GED_DWORD_BITS);
        if (0 == dataEntry->_consecutive._position._bitMask)
        {
            // This field consists only from padded positions, which was already recorded previously.
            GEDASSERT(dataEntry->_restrictions);
            GEDASSERT(dataEntry->_restrictions[0]->_restrictionType == GED_FIELD_RESTRICTIONS_TYPE_PADDING);
            break;
        }
        RecordSingleFragment(mappingFragments, dataEntry->_consecutive._position);
        break;
    }
    case GED_TABLE_ENTRY_TYPE_FRAGMENTED:
    {
        const ged_ins_field_multiple_fragments_t& fragmentedPosition = dataEntry->_fragmented;
        const uint32_t numOfFragments = fragmentedPosition._numOfPositionFragments;
        for (uint8_t i = 0; i < numOfFragments; ++i)
        {
            RecordSingleFragment(mappingFragments, fragmentedPosition._fragments[i]);
        }
        break;
    }
    case GED_TABLE_ENTRY_TYPE_FIXED_VALUE:
    {
        GEDASSERT(dataEntry->_bitSize <= GED_DWORD_BITS);
        ged_ins_field_mapping_fragment_t fixedFragment;
        fixedFragment._fixed = true;
        fixedFragment._from._lowBit = 0;
        fixedFragment._from._highBit = dataEntry->_bitSize - 1;
        fixedFragment._value = dataEntry->_fixed._value;
        mappingFragments.emplace_back(fixedFragment);
        hasFixedValue = true;
        break;
    }
    default:
        GEDASSERT(0);
    }
    GEDASSERT(!mappingFragments.empty());
    return hasFixedValue;
}


void GEDIns::RecordSingleFragment(vector<ged_ins_field_mapping_fragment_t> &mappingFragments,
                                  const ged_ins_field_position_fragment_t &position) const
{
    const uint8_t normalizedLowBit = position._lowBit - position._dwordIndex * GED_DWORD_BITS - position._shift;
    ged_ins_field_mapping_fragment_t fragment;
    fragment._fixed = false;
    fragment._from._lowBit = normalizedLowBit;
    fragment._from._highBit = normalizedLowBit + position._highBit - position._lowBit;
    fragment._to = position;
    mappingFragments.emplace_back(fragment);
}


void GEDIns::MergeFragments(vector<ged_ins_field_mapping_fragment_t> &mappingFragments) const
{
    for (auto iter = mappingFragments.begin(); iter != mappingFragments.end(); ++iter)
    {
        if (mappingFragments.end() == iter + 1) break;

        const auto nextIter = next(iter);
        if ((iter->_from._highBit + 1 == nextIter->_from._lowBit) &&
            (iter->_fixed == nextIter->_fixed) &&
            (iter->_to._highBit + 1 == nextIter->_to._lowBit))
        {
            // Merge the next cell into current
            iter->_from._highBit = nextIter->_from._highBit;
            iter->_to._highBit = nextIter->_to._highBit;
            mappingFragments.erase(nextIter);
        }
    }
}

#endif // GED_VALIDATION_API
