def _find(self, credential_type, target=None, query=None):  # O(n) generator
        """Returns a generator of matching entries.

        It is O(1) for AT hits, and O(n) for other types.
        Note that it holds a lock during the entire search.
        """
        target = sorted(target or [])  # Match the order sorted by add()
        assert isinstance(target, list), "Invalid parameter type"

        preferred_result = None
        if (credential_type == self.CredentialType.ACCESS_TOKEN
            and isinstance(query, dict)
            and "home_account_id" in query and "environment" in query
            and "client_id" in query and "realm" in query and target
        ):  # Special case for O(1) AT lookup
            preferred_result = self._get_access_token(
                query["home_account_id"], query["environment"],
                query["client_id"], query["realm"], target)
            if preferred_result:
                yield preferred_result

        target_set = set(target)
        with self._lock:
            # Since the target inside token cache key is (per schema) unsorted,
            # there is no point to attempt an O(1) key-value search here.
            # So we always do an O(n) in-memory search.
            for entry in self._cache.get(credential_type, {}).values():
                if is_subdict_of(query or {}, entry) and (
                        target_set <= set(entry.get("target", "").split())
                        if target else True):
                    if entry != preferred_result:  # Avoid yielding the same entry twice
                        yield entry
Returns a generator of matching entries.

        It is O(1) for AT hits, and O(n) for other types.
        Note that it holds a lock during the entire search.
        
Solutions/CyberArkAudit/Data Connectors/.python_packages/lib/site-packages/msal/token_cache.py