//     Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file

/** Providing access to the constants binary blob.
 *
 * There are multiple ways, the constants binary is accessed, and its
 * definition depends on how that is done.
 *
 * This deals with loading the resource from a DLL under Windows.
 *
 */

// This file is included from another C file, help IDEs to still parse it on
// its own.
#ifdef __IDE_ONLY__
#include "nuitka/prelude.h"

// Most often used modes per OS, more exist and could be used of course.
#if defined(_WIN32)
#define _NUITKA_CONSTANTS_FROM_COFF_OBJ 1
#elif defined(__APPLE__)
#define _NUITKA_CONSTANTS_FROM_MACOS_SECTION 1
#else
#define _NUITKA_CONSTANTS_FROM_CODE 1
#endif

#endif

#if _NUITKA_EXPERIMENTAL_WRITEABLE_CONSTANTS
#define CONSTANT_BIN_CONSTANT
#else
#define CONSTANT_BIN_CONSTANT const
#endif

#if defined(_NUITKA_CONSTANTS_FROM_LINKER) || defined(_NUITKA_CONSTANTS_FROM_COFF_OBJ) ||                              \
    defined(_NUITKA_CONSTANTS_FROM_CODE) || defined(_NUITKA_CONSTANTS_FROM_INCBIN) ||                                  \
    defined(_NUITKA_CONSTANTS_FROM_C23_EMBED) || defined(_NUITKA_CONSTANTS_FROM_RESOURCE) ||                           \
    defined(_NUITKA_CONSTANTS_FROM_MACOS_SECTION)
NUITKA_DECLARE_CONSTANT_BLOB(constant_bin, ConstantBlob, CONSTANT_BIN_CONSTANT, 3);
#endif

#include "nuitka/constants_blob_spec.h"

// Symbol to be assigned locally.
unsigned char const *constant_bin = NULL;

#if PYTHON_VERSION < 0x300
static PyObject *int_cache = NULL;
#endif

static PyObject *long_cache = NULL;

static PyObject *float_cache = NULL;

#if PYTHON_VERSION >= 0x300
static PyObject *bytes_cache = NULL;
#endif

#if PYTHON_VERSION < 0x300
static PyObject *unicode_cache = NULL;
#endif

static PyObject *tuple_cache = NULL;

static PyObject *list_cache = NULL;

static PyObject *dict_cache = NULL;

static PyObject *set_cache = NULL;

static PyObject *frozenset_cache = NULL;

// Use our own non-random hash for some of the things to be fast. This is inspired
// from the original Python2 hash func, but we are mostly using it on pointer values
static Py_hash_t Nuitka_FastHashBytes(const void *value, Py_ssize_t size) {
    if (unlikely(size == 0)) {
        return 0;
    }

    unsigned char *w = (unsigned char *)value;
    long x = *w << 7;

    while (--size >= 0) {
        x = (1000003 * x) ^ *w++;
    }

    x ^= size;

    // The value -1 is reserved for errors.
    if (x == -1) {
        x = -2;
    }

    return x;
}

static Py_hash_t our_list_hash(PyListObject *list) {
    return Nuitka_FastHashBytes(&list->ob_item[0], Py_SIZE(list) * sizeof(PyObject *));
}

static PyObject *our_list_tp_richcompare(PyListObject *list1, PyListObject *list2, int op) {
    assert(op == Py_EQ);

    PyObject *result;

    if (list1 == list2) {
        result = Py_True;
    } else if (Py_SIZE(list1) != Py_SIZE(list2)) {
        result = Py_False;
    } else if (memcmp(&list1->ob_item[0], &list2->ob_item[0], Py_SIZE(list1) * sizeof(PyObject *)) == 0) {
        result = Py_True;
    } else {
        result = Py_False;
    }

    Py_INCREF_IMMORTAL(result);
    return result;
}

static Py_hash_t our_tuple_hash(PyTupleObject *tuple) {
    return Nuitka_FastHashBytes(&tuple->ob_item[0], Py_SIZE(tuple) * sizeof(PyObject *));
}

static PyObject *our_tuple_tp_richcompare(PyTupleObject *tuple1, PyTupleObject *tuple2, int op) {
    assert(op == Py_EQ);

    PyObject *result;

    if (tuple1 == tuple2) {
        result = Py_True;
    } else if (Py_SIZE(tuple1) != Py_SIZE(tuple2)) {
        result = Py_False;
    } else if (memcmp(&tuple1->ob_item[0], &tuple2->ob_item[0], Py_SIZE(tuple1) * sizeof(PyObject *)) == 0) {
        result = Py_True;
    } else {
        result = Py_False;
    }

    Py_INCREF_IMMORTAL(result);
    return result;
}

static Py_hash_t our_set_hash(PyObject *set) {
    Py_hash_t result = 0;
    PyObject *key;
    Py_ssize_t pos = 0;

#if PYTHON_VERSION < 0x300
    // Same sized set, simply check if values are identical. Other reductions should
    // make it identical, or else this won't have the effect intended.
    while (_PySet_Next(set, &pos, &key)) {
        result *= 1000003;
        result ^= Nuitka_FastHashBytes(&key, sizeof(PyObject *));
    }
#else
    Py_hash_t unused;

    while (_PySet_NextEntry(set, &pos, &key, &unused)) {
        result *= 1000003;
        result ^= Nuitka_FastHashBytes(&key, sizeof(PyObject *));
    }
#endif

    return result;
}

static PyObject *our_set_tp_richcompare(PyObject *set1, PyObject *set2, int op) {
    assert(op == Py_EQ);

    PyObject *result;

    Py_ssize_t pos1 = 0, pos2 = 0;
    PyObject *key1, *key2;

    if (Py_SIZE(set1) != Py_SIZE(set2)) {
        result = Py_False;
    } else {
        result = Py_True;

#if PYTHON_VERSION < 0x300
        // Same sized set, simply check if values are identical. Other reductions should
        // make it identical, or else this won't have the effect intended.
        while (_PySet_Next(set1, &pos1, &key1)) {
            {
                NUITKA_MAY_BE_UNUSED int res = _PySet_Next(set2, &pos2, &key2);
                assert(res != 0);
            }

            if (key1 != key2) {
                result = Py_False;
                break;
            }
        }
#else
        Py_hash_t unused1, unused2;

        // Same sized dictionary, simply check if values are identical. Other reductions should
        // make it identical, or else this won't have the effect intended.
        while (_PySet_NextEntry(set1, &pos1, &key1, &unused1)) {
            {
                NUITKA_MAY_BE_UNUSED int res = _PySet_NextEntry(set2, &pos2, &key2, &unused2);
                assert(res != 0);
            }

            if (key1 != key2) {
                result = Py_False;
                break;
            }
        }
#endif
    }

    Py_INCREF_IMMORTAL(result);
    return result;
}

static PyObject *our_float_tp_richcompare(PyFloatObject *a, PyFloatObject *b, int op) {
    assert(op == Py_EQ);

    PyObject *result;

    // Avoid the C math when comparing, for it makes too many values equal or unequal.
    if (memcmp(&a->ob_fval, &b->ob_fval, sizeof(b->ob_fval)) == 0) {
        result = Py_True;
    } else {
        result = Py_False;
    }

    Py_INCREF_IMMORTAL(result);
    return result;
}

static Py_hash_t our_dict_hash(PyObject *dict) {
    Py_hash_t result = 0;

    Py_ssize_t pos = 0;
    PyObject *key, *value;

    while (Nuitka_DictNext(dict, &pos, &key, &value)) {
        result *= 1000003;
        result ^= Nuitka_FastHashBytes(&key, sizeof(PyObject *));
        result *= 1000003;
        result ^= Nuitka_FastHashBytes(&value, sizeof(PyObject *));
    }

    return result;
}

static PyObject *our_dict_tp_richcompare(PyObject *a, PyObject *b, int op) {
    PyObject *result;

    if (Py_SIZE(a) != Py_SIZE(b)) {
        result = Py_False;
    } else {
        result = Py_True;

        Py_ssize_t pos1 = 0, pos2 = 0;
        PyObject *key1, *value1;
        PyObject *key2, *value2;

        // Same sized dictionary, simply check if key and values are identical.
        // Other reductions should make it identical, or else this won't have the
        // effect intended.
        while (Nuitka_DictNext(a, &pos1, &key1, &value1)) {
            {
                NUITKA_MAY_BE_UNUSED int res = Nuitka_DictNext(b, &pos2, &key2, &value2);
                assert(res != 0);
            }

            if (key1 != key2 || value1 != value2) {
                result = Py_False;
                break;
            }
        }
    }

    Py_INCREF_IMMORTAL(result);
    return result;
}

// For creation of small long singleton long values as required by Python3.
#if PYTHON_VERSION < 0x3b0
#if PYTHON_VERSION >= 0x390
PyObject **Nuitka_Long_SmallValues;
#elif PYTHON_VERSION >= 0x300
PyObject *Nuitka_Long_SmallValues[NUITKA_STATIC_SMALLINT_VALUE_MAX - NUITKA_STATIC_SMALLINT_VALUE_MIN + 1];
#endif
#endif

static void initCaches(void) {
    static bool init_done = false;
    if (init_done == true) {
        return;
    }

#if PYTHON_VERSION < 0x300
    int_cache = PyDict_New();
#endif

    long_cache = PyDict_New();

    float_cache = PyDict_New();

#if PYTHON_VERSION >= 0x300
    bytes_cache = PyDict_New();
#endif

#if PYTHON_VERSION < 0x300
    unicode_cache = PyDict_New();
#endif

    tuple_cache = PyDict_New();

    list_cache = PyDict_New();

    dict_cache = PyDict_New();

    set_cache = PyDict_New();

    frozenset_cache = PyDict_New();

#if PYTHON_VERSION < 0x3b0
#if PYTHON_VERSION >= 0x390
    // On Python3.9+ these are exposed in the interpreter.
    Nuitka_Long_SmallValues = (PyObject **)_PyInterpreterState_GET()->small_ints;
#elif PYTHON_VERSION >= 0x300
    for (long i = NUITKA_STATIC_SMALLINT_VALUE_MIN; i < NUITKA_STATIC_SMALLINT_VALUE_MAX; i++) {
        // Have to use the original API here since out "Nuitka_PyLong_FromLong"
        // would insist on using "Nuitka_Long_SmallValues" to produce it.
        PyObject *value = PyLong_FromLong(i);
        Nuitka_Long_SmallValues[NUITKA_TO_SMALL_VALUE_OFFSET(i)] = value;
    }
#endif
#endif

    init_done = true;
}

static void insertToDictCache(PyObject *dict, PyObject **value) {
    PyObject *item = PyDict_GetItem(dict, *value);

    if (item != NULL) {
        *value = item;
    } else {
        PyDict_SetItem(dict, *value, *value);
    }
}

static void insertToDictCacheForcedHash(PyObject *dict, PyObject **value, hashfunc tp_hash,
                                        richcmpfunc tp_richcompare) {
    hashfunc old_hash = Py_TYPE(*value)->tp_hash;
    richcmpfunc old_richcmpfunc = Py_TYPE(*value)->tp_richcompare;

    // Hash is optional, e.g. for floats we can spare us doing our own hash,
    // but we do equality
    if (tp_hash != NULL) {
        Py_TYPE(*value)->tp_hash = tp_hash;
    }
    Py_TYPE(*value)->tp_richcompare = tp_richcompare;

    insertToDictCache(dict, value);

    Py_TYPE(*value)->tp_hash = old_hash;
    Py_TYPE(*value)->tp_richcompare = old_richcmpfunc;
}

static uint16_t unpackValueUint16(unsigned char const **data) {
    uint16_t value;

    memcpy(&value, *data, sizeof(value));

    assert(sizeof(value) == 2);

    *data += sizeof(value);

    return value;
}

static uint32_t unpackValueUint32(unsigned char const **data) {
    uint32_t value;

    memcpy(&value, *data, sizeof(value));

    assert(sizeof(value) == 4);

    *data += sizeof(value);

    return value;
}

static double unpackValueFloat(unsigned char const **data) {
    double size;

    memcpy(&size, *data, sizeof(size));
    *data += sizeof(size);

    return size;
}

static unsigned char const *_unpackValueCString(unsigned char const *data) {
    while (*(data++) != 0) {
    }

    return data;
}

// Decoding Variable-length quantity values
static uint64_t _unpackVariableLength(unsigned char const **data) {
    uint64_t result = 0;
    uint64_t factor = 1;

    while (1) {
        unsigned char value = **data;
        *data += 1;

        result += (value & 127) * factor;

        if (value < 128) {
            break;
        }

        factor <<= 7;
    }

    return result;
}

static PyObject *_unpackAnonValue(unsigned char anon_index) {
    switch (anon_index) {
    case 0:
        return (PyObject *)Py_TYPE(Py_None);
    case 1:
        return (PyObject *)&PyEllipsis_Type;
    case 2:
        return (PyObject *)Py_TYPE(Py_NotImplemented);
    case 3:
        return (PyObject *)&PyFunction_Type;
    case 4:
        return (PyObject *)&PyGen_Type;
    case 5:
        return (PyObject *)&PyCFunction_Type;
    case 6:
        return (PyObject *)&PyCode_Type;
    case 7:
        return (PyObject *)&PyModule_Type;

#if PYTHON_VERSION < 0x300
    case 8:
        return (PyObject *)&PyFile_Type;
    case 9:
        return (PyObject *)&PyClass_Type;
    case 10:
        return (PyObject *)&PyInstance_Type;
    case 11:
        return (PyObject *)&PyMethod_Type;
#endif

#if PYTHON_VERSION >= 0x3a0
    case 10:
        return (PyObject *)Nuitka_PyUnion_Type;
#endif

    default:
        PRINT_FORMAT("Missing anon value for %d\n", (int)anon_index);
        NUITKA_CANNOT_GET_HERE("Corrupt constants blob");
    }
}

PyObject *_unpackSpecialValue(unsigned char special_index) {
    switch (special_index) {
    case 0:
        return PyObject_GetAttrString((PyObject *)builtin_module, "Ellipsis");
    case 1:
        return PyObject_GetAttrString((PyObject *)builtin_module, "NotImplemented");
    case 2:
        return Py_SysVersionInfo;
    default:
        PRINT_FORMAT("Missing special value for %d\n", (int)special_index);
        NUITKA_CANNOT_GET_HERE("Corrupt constants blob");
    }
}

static PyObject *_Nuitka_Unicode_ImmortalFromStringAndSize(PyThreadState *tstate, const char *data, Py_ssize_t size,
                                                           bool is_ascii) {
#if PYTHON_VERSION < 0x300
    PyObject *u = PyUnicode_FromStringAndSize((const char *)data, size);
#else
    // spell-checker: ignore surrogatepass
    PyObject *u = PyUnicode_DecodeUTF8((const char *)data, size, "surrogatepass");
#endif

#if PYTHON_VERSION >= 0x3d0 || (PYTHON_VERSION >= 0x3c7 && _NUITKA_EXE_MODE)
    _PyUnicode_InternImmortal(tstate->interp, &u);
#elif PYTHON_VERSION >= 0x3c0
    if (is_ascii) {
        PyUnicode_InternInPlace(&u);
    }

#if PYTHON_VERSION >= 0x3c7
    _PyUnicode_STATE(u).interned = SSTATE_INTERNED_IMMORTAL_STATIC;

#if _NUITKA_EXE_MODE
    _PyUnicode_STATE(u).statically_allocated = 1;
#else
    if (Py_Version >= 0x30c0700) {
        _PyUnicode_STATE(u).statically_allocated = 1;
    }
#endif
#endif

#elif PYTHON_VERSION >= 0x300
    if (is_ascii) {
        PyUnicode_InternInPlace(&u);
    }
#else
    insertToDictCache(unicode_cache, &u);
#endif

    // Make sure our strings are consistent.
    // TODO: Check with an assertion making build of Python 3.13.0 if this is really true,
    // for 3.14 it ought to not be done.
#if PYTHON_VERSION >= 0x3c0 && PYTHON_VERSION < 0x3e0 && !defined(__NUITKA_NO_ASSERT__)
    // Note: Setting to immortal happens last, but we want to check now.
    Py_SET_REFCNT_IMMORTAL(u);

    assert(Nuitka_PyUnicode_CheckConsistency(u, 1));
#endif

    return u;
}

static unsigned char const *_unpackBlobConstants(PyThreadState *tstate, PyObject **output, unsigned char const *data,
                                                 int count);

static unsigned char const *_unpackBlobConstant(PyThreadState *tstate, PyObject **output, unsigned char const *data) {

    // Make sure we discover failures to assign.
    *output = NULL;
    bool is_object;

    unsigned char c = *data++;
#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
    unsigned char const *data_old = data;
    printf("Type %u:\n", (unsigned int)c);
#endif
    switch (c) {

    case NUITKA_CONSTANT_BLOB_TAG_PREVIOUS: {
        *output = *(output - 1);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_TUPLE: {
        int size = (int)_unpackVariableLength(&data);

        PyObject *t = PyTuple_New(size);
        CHECK_OBJECT(t);

        if (size > 0) {
            data = _unpackBlobConstants(tstate, &PyTuple_GET_ITEM(t, 0), data, size);

            CHECK_OBJECTS(&PyTuple_GET_ITEM(t, 0), size);
        }

        insertToDictCacheForcedHash(tuple_cache, &t, (hashfunc)our_tuple_hash, (richcmpfunc)our_tuple_tp_richcompare);

        *output = t;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_LIST: {
        int size = (int)_unpackVariableLength(&data);

        PyObject *l = PyList_New(size);
        CHECK_OBJECT(l);

        if (size > 0) {
            data = _unpackBlobConstants(tstate, &PyList_GET_ITEM(l, 0), data, size);

            CHECK_OBJECTS(&PyList_GET_ITEM(l, 0), size);
        }

        insertToDictCacheForcedHash(list_cache, &l, (hashfunc)our_list_hash, (richcmpfunc)our_list_tp_richcompare);

        *output = l;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_DICT: {
        int size = (int)_unpackVariableLength(&data);

        PyObject *d = _PyDict_NewPresized(size);
        CHECK_OBJECT(d);

        if (size > 0) {
            NUITKA_DYNAMIC_ARRAY_DECL(keys, PyObject *, size);
            NUITKA_DYNAMIC_ARRAY_DECL(values, PyObject *, size);

            data = _unpackBlobConstants(tstate, &keys[0], data, size);
            data = _unpackBlobConstants(tstate, &values[0], data, size);

            CHECK_OBJECTS(&keys[0], size);
            CHECK_OBJECTS(&values[0], size);

            for (int i = 0; i < size; i++) {
                NUITKA_MAY_BE_UNUSED int res = PyDict_SetItem(d, keys[i], values[i]);
                assert(res == 0);
            }
        }

        insertToDictCacheForcedHash(dict_cache, &d, (hashfunc)our_dict_hash, (richcmpfunc)our_dict_tp_richcompare);

        *output = d;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_FROZENSET:
    case NUITKA_CONSTANT_BLOB_TAG_SET: {
        int size = (int)_unpackVariableLength(&data);

        PyObject *s;

        if (c == NUITKA_CONSTANT_BLOB_TAG_SET) {
            s = PySet_New(NULL);
        } else {
            if (size == 0) {
                // Get at the frozenset singleton of CPython and use it too. Some things
                // rely on it being a singleton across the board.
                static PyObject *empty_frozenset = NULL;

                if (empty_frozenset == NULL) {
                    empty_frozenset = CALL_FUNCTION_WITH_SINGLE_ARG(tstate, (PyObject *)&PyFrozenSet_Type,
                                                                    Nuitka_Bytes_FromStringAndSize("", 0));
                }

                s = empty_frozenset;
            } else {
                s = PyFrozenSet_New(NULL);
            }
        }

        CHECK_OBJECT(s);

        if (size > 0) {
            NUITKA_DYNAMIC_ARRAY_DECL(values, PyObject *, size);

            data = _unpackBlobConstants(tstate, &values[0], data, size);

            CHECK_OBJECTS(&values[0], size);

            for (int i = 0; i < size; i++) {
                NUITKA_MAY_BE_UNUSED int res = PySet_Add(s, values[i]);
                assert(res == 0);
            }
        }

        // sets are cached globally too.
        if (c == NUITKA_CONSTANT_BLOB_TAG_SET) {
            insertToDictCacheForcedHash(set_cache, &s, (hashfunc)our_set_hash, (richcmpfunc)our_set_tp_richcompare);
        } else {
            insertToDictCacheForcedHash(frozenset_cache, &s, (hashfunc)our_set_hash,
                                        (richcmpfunc)our_set_tp_richcompare);
        }

        *output = s;
        is_object = true;

        break;
    }
#if PYTHON_VERSION < 0x300
    case NUITKA_CONSTANT_BLOB_TAG_INT_NEGATIVE:
    case NUITKA_CONSTANT_BLOB_TAG_INT_POSITIVE: {
        long value = (long)_unpackVariableLength(&data);
        if (c == NUITKA_CONSTANT_BLOB_TAG_INT_NEGATIVE) {
            value = -value;
        }

        PyObject *i = PyInt_FromLong(value);

        insertToDictCache(int_cache, &i);

        *output = i;
        is_object = true;

        break;
    }
#endif
    case NUITKA_CONSTANT_BLOB_TAG_LONG_POSITIVE_SMALL:
    case NUITKA_CONSTANT_BLOB_TAG_LONG_NEGATIVE_SMALL: {
        // Positive/negative integer value with abs value < 2**31
        uint64_t value = _unpackVariableLength(&data);

        PyObject *l =
            Nuitka_LongFromCLong((c == NUITKA_CONSTANT_BLOB_TAG_LONG_POSITIVE_SMALL) ? ((long)value) : (-(long)value));
        assert(l != NULL);

        // Avoid the long cache, won't do anything useful for small ints
#if PYTHON_VERSION >= 0x300
        long check_value = (c == NUITKA_CONSTANT_BLOB_TAG_LONG_POSITIVE_SMALL) ? (long)value : -(long)value;
        if (check_value < NUITKA_STATIC_SMALLINT_VALUE_MIN || check_value >= NUITKA_STATIC_SMALLINT_VALUE_MAX)
#endif
        {
            insertToDictCache(long_cache, &l);
        }

        *output = l;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_LONG_NEGATIVE_LARGE:
    case NUITKA_CONSTANT_BLOB_TAG_LONG_POSITIVE_LARGE: {
        PyObject *result = Nuitka_PyLong_FromLong(0);

        int size = (int)_unpackVariableLength(&data);

        PyObject *shift = Nuitka_PyLong_FromLong(31);

        for (int i = 0; i < size; i++) {
            result = PyNumber_InPlaceLshift(result, shift);

            uint64_t value = _unpackVariableLength(&data);
            PyObject *part = Nuitka_LongFromCLong((long)value);
            assert(part != NULL);
            result = PyNumber_InPlaceAdd(result, part);
            Py_DECREF(part);
        }

        Py_DECREF(shift);

        if (c == NUITKA_CONSTANT_BLOB_TAG_LONG_NEGATIVE_LARGE) {
            Nuitka_LongSetSignNegative(result);
        }

        insertToDictCache(long_cache, &result);

        *output = result;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_FLOAT: {
        double value = unpackValueFloat(&data);

        PyObject *f = PyFloat_FromDouble(value);

        // Floats are cached globally too.
        insertToDictCacheForcedHash(float_cache, &f, NULL, (richcmpfunc)our_float_tp_richcompare);

        *output = f;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_COMPLEX: {
        double real = unpackValueFloat(&data);
        double imag = unpackValueFloat(&data);

        *output = PyComplex_FromDoubles(real, imag);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_COMPLEX_SPECIAL: {
        PyObject *parts[2];

        // Complex via float is done for ones that are 0, nan, float.
        data = _unpackBlobConstants(tstate, &parts[0], data, 2);

        *output = BUILTIN_COMPLEX2(tstate, parts[0], parts[1]);
        is_object = true;

        break;
    }
#if PYTHON_VERSION < 0x300
    case NUITKA_CONSTANT_BLOB_TAG_ATTRIBUTE_NAME:
    case NUITKA_CONSTANT_BLOB_TAG_BYTES_ZERO_TERMINATED: {
        // Python2 str, potentially attribute, zero terminated.
        size_t size = strlen((const char *)data);

        PyObject *s = PyString_FromStringAndSize((const char *)data, size);
        CHECK_OBJECT(s);

        data += size + 1;

        if (c == NUITKA_CONSTANT_BLOB_TAG_ATTRIBUTE_NAME) {
            PyString_InternInPlace(&s);
        }

        *output = s;
        is_object = true;

        break;
    }
#else
    case NUITKA_CONSTANT_BLOB_TAG_BYTES_ZERO_TERMINATED: {
        // Python3 bytes, zero terminated.
        size_t size = strlen((const char *)data);

        PyObject *b = Nuitka_Bytes_FromStringAndSize((const char *)data, size);
        CHECK_OBJECT(b);

        data += size + 1;

        // Empty bytes value is here as well.
        if (size > 1) {
            insertToDictCache(bytes_cache, &b);
        }

        *output = b;
        is_object = true;

        break;
    }
#endif
    case NUITKA_CONSTANT_BLOB_TAG_BYTES_SINGLE: {
        // Python2 str length 1 str, potentially attribute, or Python3 single byte

#if PYTHON_VERSION < 0x300
        PyObject *s = PyString_FromStringAndSize((const char *)data, 1);
        data += 1;
        *output = s;
#else
        PyObject *b = Nuitka_Bytes_FromStringAndSize((const char *)data, 1);
        data += 1;
        *output = b;
#endif

        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_TEXT_SINGLE: {
        // Python2 unicode, Python3 str length 1, potentially attribute in Python3
        PyObject *u = _Nuitka_Unicode_ImmortalFromStringAndSize(tstate, (const char *)data, 1, true);
        data += 1;

        *output = u;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BYTES_LENGTH_PREFIXED: {
        // Python2 str or Python3 bytes, length indicated.
        int size = (int)_unpackVariableLength(&data);
        assert(size > 1);

        PyObject *b = Nuitka_Bytes_FromStringAndSize((const char *)data, size);
        CHECK_OBJECT(b);

        data += size;

#if PYTHON_VERSION >= 0x300
        insertToDictCache(bytes_cache, &b);
#endif

        *output = b;
        is_object = true;

        break;
    }

    case NUITKA_CONSTANT_BLOB_TAG_BYTEARRAY: {
        int size = (int)_unpackVariableLength(&data);

        PyObject *b = PyByteArray_FromStringAndSize((const char *)data, size);
        data += size;

        *output = b;
        is_object = true;

        break;
    }
#if PYTHON_VERSION >= 0x300
    case NUITKA_CONSTANT_BLOB_TAG_ATTRIBUTE_NAME: // Python3 attributes
#endif
    case NUITKA_CONSTANT_BLOB_TAG_TEXT_UTF8_ZERO_TERMINATED: { // Python2 unicode, Python3 str, zero terminated.
        size_t size = strlen((const char *)data);
        assert(size != 0);

        PyObject *u = _Nuitka_Unicode_ImmortalFromStringAndSize(tstate, (const char *)data, size,
                                                                c == NUITKA_CONSTANT_BLOB_TAG_ATTRIBUTE_NAME);
        data += size + 1;

        *output = u;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_TEXT_UTF8_LENGTH_PREFIXED: {
        int size = (int)_unpackVariableLength(&data);
        assert(size != 0);

        PyObject *u = _Nuitka_Unicode_ImmortalFromStringAndSize(tstate, (const char *)data, size, false);
        data += size;

        *output = u;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_NONE: {
        *output = Py_None;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_TEXT_EMPTY: {
        *output = _Nuitka_Unicode_ImmortalFromStringAndSize(tstate, (const char *)data, 0, true);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_TRUE: {
        *output = Py_True;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_FALSE: {
        *output = Py_False;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_SLICE: {
        // Slice object
        PyObject *items[3];
        data = _unpackBlobConstants(tstate, &items[0], data, 3);

        PyObject *s = MAKE_SLICE_OBJECT3(tstate, items[0], items[1], items[2]);

        *output = s;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_RANGE: {
        // (x)range objects
        PyObject *items[3];
        data = _unpackBlobConstants(tstate, &items[0], data, 3);
#if PYTHON_VERSION < 0x300
        assert(PyInt_CheckExact(items[0]));
        assert(PyInt_CheckExact(items[1]));
        assert(PyInt_CheckExact(items[2]));

        long start = PyInt_AS_LONG(items[0]);
        long stop = PyInt_AS_LONG(items[1]);
        long step = PyInt_AS_LONG(items[2]);

        PyObject *s = MAKE_XRANGE(tstate, start, stop, step);
#else
        PyObject *s = BUILTIN_XRANGE3(tstate, items[0], items[1], items[2]);
#endif
        *output = s;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BUILTIN_ANON: {
        // Anonymous builtin by table index value.
        unsigned char anon_index = *data++;

        *output = _unpackAnonValue(anon_index);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BUILTIN_SPECIAL: {
        // Anonymous builtin by table index value.
        unsigned char special_index = *data++;

        *output = _unpackSpecialValue(special_index);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BUILTIN_NAMED: {
        // Builtin by name. TODO: Define number table shared by C and Python
        // serialization to avoid using strings here.
        char const *builtin_name = (char const *)data;
        data = _unpackValueCString(data);

        *output = PyObject_GetAttrString((PyObject *)builtin_module, builtin_name);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BUILTIN_EXCEPTION: {
        // Builtin exception by name. TODO: Define number table shared by C and Python
        // serialization to avoid using strings here.
        char const *builtin_exception_name = (char const *)data;
        data = _unpackValueCString(data);

        *output = PyObject_GetAttrString((PyObject *)builtin_module, builtin_exception_name);
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_FLOAT_SPECIAL: {
        unsigned char v = *data++;

        PyObject *z = NULL;

        switch (v) {
        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_POS_ZERO: {
            static PyObject *_const_float_0_0 = NULL;

            if (_const_float_0_0 == NULL) {
                _const_float_0_0 = PyFloat_FromDouble(0.0);
            }
            z = _const_float_0_0;

            break;
        }
        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_NEG_ZERO: {
            static PyObject *_const_float_minus_0_0 = NULL;

            if (_const_float_minus_0_0 == NULL) {
                _const_float_minus_0_0 = PyFloat_FromDouble(0.0);

                // Older Python3 has variable signs from C, so be explicit about it.
                PyFloat_SET_DOUBLE(_const_float_minus_0_0, copysign(PyFloat_AS_DOUBLE(_const_float_minus_0_0), -1.0));
            }
            z = _const_float_minus_0_0;

            break;
        }

        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_POS_NAN: {
            static PyObject *_const_float_plus_nan = NULL;

            if (_const_float_plus_nan == NULL) {
                _const_float_plus_nan = PyFloat_FromDouble(Py_NAN);

                // Older Python3 has variable signs for NaN from C, so be explicit about it.
                PyFloat_SET_DOUBLE(_const_float_plus_nan, copysign(PyFloat_AS_DOUBLE(_const_float_plus_nan), 1.0));
            }
            z = _const_float_plus_nan;

            break;
        }
        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_NEG_NAN: {
            static PyObject *_const_float_minus_nan = NULL;

            if (_const_float_minus_nan == NULL) {
                _const_float_minus_nan = PyFloat_FromDouble(Py_NAN);

                // Older Python3 has variable signs for NaN from C, so be explicit about it.
                PyFloat_SET_DOUBLE(_const_float_minus_nan, copysign(PyFloat_AS_DOUBLE(_const_float_minus_nan), -1.0));
            }
            z = _const_float_minus_nan;

            break;
        }
        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_POS_INF: {
            static PyObject *_const_float_plus_inf = NULL;

            if (_const_float_plus_inf == NULL) {
                _const_float_plus_inf = PyFloat_FromDouble(Py_HUGE_VAL);

                // Older Python3 has variable signs from C, so be explicit about it.
                PyFloat_SET_DOUBLE(_const_float_plus_inf, copysign(PyFloat_AS_DOUBLE(_const_float_plus_inf), 1.0));
            }
            z = _const_float_plus_inf;

            break;
        }
        case NUITKA_CONSTANT_BLOB_FLOAT_SPECIAL_NEG_INF: {
            static PyObject *_const_float_minus_inf = NULL;

            if (_const_float_minus_inf == NULL) {
                _const_float_minus_inf = PyFloat_FromDouble(Py_HUGE_VAL);

                // Older Python3 has variable signs from C, so be explicit about it.
                PyFloat_SET_DOUBLE(_const_float_minus_inf, copysign(PyFloat_AS_DOUBLE(_const_float_minus_inf), -1.0));
            }
            z = _const_float_minus_inf;

            break;
        }
        default: {
            PRINT_FORMAT("Missing decoding for %d\n", (int)c);
            NUITKA_CANNOT_GET_HERE("Corrupt constants blob");
        }
        }

        // Floats are cached globally too.
        insertToDictCacheForcedHash(float_cache, &z, NULL, (richcmpfunc)our_float_tp_richcompare);

        *output = z;
        is_object = true;

        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_BLOB_DATA: {
        // Blob data pointer, user knowns size.
        uint64_t size = _unpackVariableLength(&data);

        *output = (PyObject *)data;
        is_object = false;

        data += size;

        break;
    }
#if PYTHON_VERSION >= 0x390
    case NUITKA_CONSTANT_BLOB_TAG_GENERIC_ALIAS: {
        // GenericAlias object
        PyObject *items[2];
        data = _unpackBlobConstants(tstate, &items[0], data, 2);

        PyObject *g = Py_GenericAlias(items[0], items[1]);

        // TODO: Maybe deduplicate.
        *output = g;

        is_object = true;
        break;
    }
#endif
#if PYTHON_VERSION >= 0x3a0
    case NUITKA_CONSTANT_BLOB_TAG_UNION_TYPE: {
        // UnionType object
        PyObject *args;
        data = _unpackBlobConstants(tstate, &args, data, 1);

        PyObject *union_type = MAKE_UNION_TYPE(args);

        // TODO: Maybe deduplicate.
        *output = union_type;

        is_object = true;
        break;
    }
#endif
    case NUITKA_CONSTANT_BLOB_TAG_CODE_OBJECT: {
        // Code object, without the filename, we let the module do that,
        // depending on the source mode and this is highly compact
        // representation of it.

        // First, flags with the optional bits. It's handling
        // must match that of encoder 100%.
        uint64_t flags = _unpackVariableLength(&data);

        // Code object flags as used by Python, encoded in the
        // flags as well.
        int co_flags = 0;

        // Name is mandatory, no flag needed.
        PyObject *function_name;
        data = _unpackBlobConstant(tstate, &function_name, data);

        // Line number is mandatory, no flag needed. Encoded values start at 0,
        // where 1 is what is normally used.
        int line_number = (int)_unpackVariableLength(&data) + 1;

        // Right now this is only argument names, so argument count is implied,
        // it is mandatory so no flag is needed, empty value is very compact
        // anyway and rare.
        PyObject *arg_names;
        data = _unpackBlobConstant(tstate, &arg_names, data);

        // TODO: Not sure if this is redundant potentially it can be derives
        // from the var names already. It might be possible to derive by other
        // means.
        int arg_count = (int)_unpackVariableLength(&data);

        // It is version specific if we have this, and dependent on a flag, if
        // it's present at all.
#if PYTHON_VERSION >= 0x3b0
        PyObject *function_qualname;

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_QUALNAME) {
            data = _unpackBlobConstant(tstate, &function_qualname, data);
        } else {
            function_qualname = function_name;
        }
#endif

        // Free vars are optional.
        PyObject *free_vars = NULL;

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FREE_VARS) {
            data = _unpackBlobConstant(tstate, &free_vars, data);
        }

#if PYTHON_VERSION >= 0x300
        int kw_only_count = 0;
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_KW_ONLY) {
            kw_only_count = (int)_unpackVariableLength(&data) + 1;
        }
        assert(kw_only_count >= 0);
#endif

#if PYTHON_VERSION >= 0x380
        int pos_only_count = 0;
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_POS_ONLY) {
            pos_only_count = (int)_unpackVariableLength(&data) + 1;
        }
        assert(pos_only_count >= 0);
#endif

        switch (flags & NUITKA_CONSTANT_BLOB_CODE_KIND_MASK) {
#if PYTHON_VERSION >= 0x360
        case NUITKA_CONSTANT_BLOB_CODE_KIND_ASYNCGEN:
            co_flags += CO_ASYNC_GENERATOR;
            break;
#endif
#if PYTHON_VERSION >= 0x350
        case NUITKA_CONSTANT_BLOB_CODE_KIND_COROUTINE:
            co_flags += CO_COROUTINE;
            break;
#endif
        case NUITKA_CONSTANT_BLOB_CODE_KIND_GENERATOR:
            co_flags += CO_GENERATOR;
            break;
        default:
            break;
        }

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_OPTIMIZED) {
            co_flags += CO_OPTIMIZED;
        }

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_NEWLOCALS) {
            co_flags += CO_NEWLOCALS;
        }

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_VARARGS) {
            co_flags += CO_VARARGS;
        }

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_VARKEYWORDS) {
            co_flags += CO_VARKEYWORDS;
        }

#if PYTHON_VERSION >= 0x370
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_ANNOTATIONS) {
            co_flags += CO_FUTURE_ANNOTATIONS;
        }
#endif

#if PYTHON_VERSION < 0x300
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_DIVISION) {
            co_flags += CO_FUTURE_DIVISION;
        }
#endif

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_UNICODE_LITERALS) {
            co_flags += CO_FUTURE_UNICODE_LITERALS;
        }

#if PYTHON_VERSION < 0x300
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_PRINT_FUNCTION) {
            co_flags += CO_FUTURE_PRINT_FUNCTION;
        }
#endif

#if PYTHON_VERSION < 0x300
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_ABSOLUTE_IMPORT) {
            co_flags += CO_FUTURE_ABSOLUTE_IMPORT;
        }
#endif

#if PYTHON_VERSION >= 0x350 && PYTHON_VERSION < 0x370
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_GENERATOR_STOP) {
            co_flags += CO_FUTURE_GENERATOR_STOP;
        }
#endif

#if PYTHON_VERSION >= 0x300
        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_FUTURE_BARRY_AS_BDFL) {
            co_flags += CO_FUTURE_BARRY_AS_BDFL;
        }
#endif

        if (flags & NUITKA_CONSTANT_BLOB_CODE_FLAG_NOFREE) {
            co_flags += CO_NOFREE;
        }

        // Filename will be supplied later during usage.
        *output = (PyObject *)MAKE_CODE_OBJECT(Py_None, line_number, co_flags, function_name, function_qualname,
                                               arg_names, free_vars, arg_count, kw_only_count, pos_only_count);

        CHECK_OBJECT(*output);

        is_object = true;
        break;
    }
    case NUITKA_CONSTANT_BLOB_TAG_END: {
        PRINT_STRING("Missing blob values\n");
        NUITKA_CANNOT_GET_HERE("Corrupt constants blob");
    }
    default:
        PRINT_FORMAT("Missing decoding for %d\n", (int)c);
        NUITKA_CANNOT_GET_HERE("Corrupt constants blob");
    }

#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
    printf("Size for %u was %d\n", (unsigned int)c, (int)(data - data_old));
#endif

    // Discourage in-place operations from modifying these. These
    // might be put into containers, therefore take 2 refs to be
    // accounting for the container too.
    if (is_object == true) {
        CHECK_OBJECT(*output);

#if PYTHON_VERSION < 0x3c0
        Py_INCREF(*output);
        Py_INCREF(*output);
#else
        Py_SET_REFCNT_IMMORTAL(*output);
#endif
    }

    return data;
}

static unsigned char const *_unpackBlobConstants(PyThreadState *tstate, PyObject **output, unsigned char const *data,
                                                 int count) {
    for (int _i = 0; _i < count; _i++) {
        data = _unpackBlobConstant(tstate, output, data);

        output += 1;
    }

    return data;
}

static int unpackBlobConstants(PyThreadState *tstate, PyObject **output, unsigned char const *data) {
    int count = (int)unpackValueUint16(&data);

#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
    printf("unpackBlobConstants count %d\n", count);
#endif
    _unpackBlobConstants(tstate, output, data, count);

    return count;
}

int loadConstantsBlob(PyThreadState *tstate, PyObject **output, char const *name) {
    static bool init_done = false;

    if (init_done == false) {
        NUITKA_PRINT_TIMING("loadConstantsBlob(): One time init.");

#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
        printf("loadConstantsBlob '%s' one time init\n", name);
#endif

#if defined(_NUITKA_CONSTANTS_FROM_INCBIN) || defined(_NUITKA_CONSTANTS_FROM_LINKER) ||                                \
    defined(_NUITKA_CONSTANTS_FROM_COFF_OBJ) || defined(_NUITKA_CONSTANTS_FROM_CODE) ||                                \
    defined(_NUITKA_CONSTANTS_FROM_C23_EMBED) || defined(_NUITKA_CONSTANTS_FROM_RESOURCE) ||                           \
    defined(_NUITKA_CONSTANTS_FROM_MACOS_SECTION)
        constant_bin = getConstantBlobData();
#endif
        NUITKA_PRINT_TIMING("loadConstantsBlob(): Found blob, decoding now.");
        DECODE(constant_bin);

        NUITKA_PRINT_TIMING("loadConstantsBlob(): One time init complete.");

        init_done = true;
    }

#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
    printf("Loading blob named '%s'\n", name);
#endif
    // Python 3.9 or higher cannot create dictionary before calling init so avoid it.
    if (strcmp(name, ".bytecode") != 0) {
        initCaches();
    }

    unsigned char const *w = constant_bin;

    for (;;) {
        int match = strcmp(name, (char const *)w);
        w += strlen((char const *)w) + 1;

#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
        printf("offset of blob size %d\n", w - constant_bin);
#endif

        uint32_t size = unpackValueUint32(&w);

        if (match == 0) {
#ifdef _NUITKA_EXPERIMENTAL_DEBUG_CONSTANTS
            printf("Loading blob named '%s' with size %d\n", name, size);
#endif
            break;
        }

        // Skip other module data.
        w += size;
    }

    return unpackBlobConstants(tstate, output, w);
}

//     Part of "Nuitka", an optimizing Python compiler that is compatible and
//     integrates with CPython, but also works on its own.
//
//     Licensed under the GNU Affero General Public License, Version 3 (the "License");
//     you may not use this file except in compliance with the License.
//     You may obtain a copy of the License at
//
//        https://www.gnu.org/licenses/agpl-3.0.txt
//
//     See also: "Nuitka Runtime Library Exception, Version 1.0" in file
//     "LICENSE-RUNTIME.txt" for additional permissions granted under Section 7.
//
//     Unless required by applicable law or agreed to in writing, software
//     distributed under the License is distributed on an "AS IS" BASIS,
//     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//     See the License for the specific language governing permissions and
//     limitations under the License.
