void *evp_generic_fetch(OPENSSL_CTX *libctx, int operation_id,
                        const char *name, const char *properties,
                        void *(*new_method)(const char *name,
                                            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);
    int nameid = 0;
    uint32_t methid = 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;

    /*
     * 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 ((nameid = ossl_namemap_name2num(namemap, name)) != 0
        && (methid = method_id(operation_id, nameid)) == 0)
        return NULL;

    if (nameid == 0
        || !ossl_method_store_cache_get(store, methid, 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.name = name;
        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, name,
                                            properties, 0 /* !force_cache */,
                                            &mcm, &mcmdata)) != NULL) {
            /*
             * If construction did create a method for us, we know that
             * there is a correct nameid and methodid, since those have
             * already been calculated in get_method_from_store() and
             * put_method_in_store() above.
             */
            nameid = ossl_namemap_name2num(namemap, name);
            methid = method_id(operation_id, nameid);
            ossl_method_store_cache_set(store, methid, properties, method);
        }
    } else {
        up_ref_method(method);
    }

    return method;
}