static void *inner_generic_fetch(OPENSSL_CTX *libctx, int operation_id,
                                 int name_id, const char *name,
                                 const char *properties,
                                 void *(*new_method)(int name_id,
                                                     const OSSL_DISPATCH *fns,
                                                     OSSL_PROVIDER *prov,
                                                     void *method_data),
                                 void *method_data,
                                 int (*up_ref_method)(void *),
                                 void (*free_method)(void *))
{
    OSSL_METHOD_STORE *store = get_default_method_store(libctx);
    OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx);
    uint32_t meth_id = 0;
    void *method = NULL;

    if (store == NULL || namemap == NULL)
        return NULL;

    /*
     * If there's ever an operation_id == 0 passed, we have an internal
     * programming error.
     */
    if (!ossl_assert(operation_id > 0))
        return NULL;

    /*
     * If we have been passed neither a name_id or a name, we have an
     * internal programming error.
     */
    if (!ossl_assert(name_id != 0 || name != NULL))
        return NULL;

    /* If we haven't received a name id yet, try to get one for the name */
    if (name_id == 0)
        name_id = ossl_namemap_name2num(namemap, name);

    /*
     * If we have a name id, calculate a method id with method_id().
     *
     * method_id returns 0 if we have too many operations (more than
     * about 2^8) or too many names (more than about 2^24).  In that
     * case, we can't create any new method.
     */
    if (name_id != 0 && (meth_id = method_id(operation_id, name_id)) == 0)
        return NULL;

    if (meth_id == 0
        || !ossl_method_store_cache_get(store, meth_id, properties, &method)) {
        OSSL_METHOD_CONSTRUCT_METHOD mcm = {
            alloc_tmp_method_store,
            dealloc_tmp_method_store,
            get_method_from_store,
            put_method_in_store,
            construct_method,
            destruct_method
        };
        struct method_data_st mcmdata;

        mcmdata.mcm = &mcm;
        mcmdata.libctx = libctx;
        mcmdata.operation_id = operation_id;
        mcmdata.name_id = name_id;
        mcmdata.name = name;
        mcmdata.propquery = properties;
        mcmdata.method_from_dispatch = new_method;
        mcmdata.destruct_method = free_method;
        mcmdata.refcnt_up_method = up_ref_method;
        mcmdata.destruct_method = free_method;
        mcmdata.method_data = method_data;
        if ((method = ossl_method_construct(libctx, operation_id,
                                            0 /* !force_cache */,
                                            &mcm, &mcmdata)) != NULL) {
            /*
             * If construction did create a method for us, we know that
             * there is a correct name_id and methodid, since those have
             * already been calculated in get_method_from_store() and
             * put_method_in_store() above.
             */
            if (name_id == 0)
                name_id = ossl_namemap_name2num(namemap, name);
            meth_id = method_id(operation_id, name_id);
            ossl_method_store_cache_set(store, meth_id, properties, method);
        }
    } else {
        up_ref_method(method);
    }

    return method;
}