function findFile(string $className): ?string
    {
        if ($className[0] === '\\') {
            $className = substr($className, 1);
        }

        // check class map
        if (isset($this->classMap[$className])) {
            return $this->classMap[$className];
        }

        if ($this->usePrefixes) {
            // determine index
            $firstNsSep = strpos($className, '\\');
            if ($firstNsSep === false) {
                $isPsr4Compatible = false;
                $index = $className[0];
                $firstNsSep = null;
            } else {
                $isPsr4Compatible = true;
                $index = substr($className, 0, $firstNsSep);
            }

            // cache PSR-0 subpath
            $subpathPsr0 = null;

            // scan prefixes
            if (
                isset($this->prefixes[$index])
                || ($isPsr4Compatible && isset($this->prefixes[$index = $className[0]]))
            ) {
                foreach ($this->prefixes[$index] as $prefix) {
                    // 0 => type, 1 => prefix, 2 => prefix_len, 3 => paths
                    if (
                        ($isPsr4 = ($prefix[0] === static::PSR4)) && !$isPsr4Compatible
                        || strncmp($prefix[1], $className, $prefix[2]) !== 0
                    ) {
                        // no match or we are locating a PSR-0 class but the prefix is PSR-4
                        continue;
                    }

                    // compose subpath
                    if ($isPsr4) {
                        // PSR-4 subpath must be built for different prefix lengths
                        $subpath = $this->buildPsr4Subpath($className, $prefix[2]);
                    } else {
                        // PSR-0 subpath contains the entire namespace so it needs to be built only once
                        if ($subpathPsr0 === null) {
                            $subpathPsr0 = $this->buildPsr0Subpath($className, $firstNsSep);
                        }

                        $subpath = $subpathPsr0;
                    }

                    // iterate over possible paths
                    foreach ($prefix[3] as $path) {
                        if (($filePath = $this->findFileWithKnownSuffix($path . $subpath)) !== null) {
                            return $filePath;
                        }
                    }
                }
            }

            // scan fallbacks
            if (isset($this->fallbacks[static::PSR0])) {
                $subpath = $subpathPsr0 ?: $this->buildPsr0Subpath($className, $firstNsSep);

                foreach ($this->fallbacks[static::PSR0] as $fallback) {
                    if (($filePath = $this->findFileWithKnownSuffix($fallback . $subpath)) !== null) {
                        return $filePath;
                    }
                }
            }

            if (isset($this->fallbacks[static::PSR4])) {
                $subpath = $this->buildPsr4Subpath($className, 0);

                foreach ($this->fallbacks[static::PSR4] as $fallback) {
                    if (($filePath = $this->findFileWithKnownSuffix($fallback . $subpath)) !== null) {
                        return $filePath;
                    }
                }
            }

            // cache failed lookup
            $this->classMap[$className] = false;
        }

        return null;
    }