mirror of
https://github.com/nextcloud/server.git
synced 2026-02-03 20:41:22 -05:00
Merge pull request #57892 from nextcloud/carl/binary-search-findIn
perf(MountManager): use binary search to find mount in path
This commit is contained in:
commit
8160f0af8a
2 changed files with 77 additions and 26 deletions
|
|
@ -18,8 +18,11 @@ use OCP\Files\Mount\IMountPoint;
|
|||
use OCP\Files\NotFoundException;
|
||||
|
||||
class Manager implements IMountManager {
|
||||
/** @var MountPoint[] */
|
||||
/** @var array<string, IMountPoint> */
|
||||
private array $mounts = [];
|
||||
private bool $areMountsSorted = false;
|
||||
/** @var list<string>|null $mountKeys */
|
||||
private ?array $mountKeys = null;
|
||||
/** @var CappedMemoryCache<IMountPoint> */
|
||||
private CappedMemoryCache $pathCache;
|
||||
/** @var CappedMemoryCache<IMountPoint[]> */
|
||||
|
|
@ -32,19 +35,14 @@ class Manager implements IMountManager {
|
|||
$this->setupManager = $setupManagerFactory->create($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param IMountPoint $mount
|
||||
*/
|
||||
public function addMount(IMountPoint $mount) {
|
||||
public function addMount(IMountPoint $mount): void {
|
||||
$this->mounts[$mount->getMountPoint()] = $mount;
|
||||
$this->pathCache->clear();
|
||||
$this->inPathCache->clear();
|
||||
$this->areMountsSorted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mountPoint
|
||||
*/
|
||||
public function removeMount(string $mountPoint) {
|
||||
public function removeMount(string $mountPoint): void {
|
||||
$mountPoint = Filesystem::normalizePath($mountPoint);
|
||||
if (\strlen($mountPoint) > 1) {
|
||||
$mountPoint .= '/';
|
||||
|
|
@ -52,24 +50,19 @@ class Manager implements IMountManager {
|
|||
unset($this->mounts[$mountPoint]);
|
||||
$this->pathCache->clear();
|
||||
$this->inPathCache->clear();
|
||||
$this->areMountsSorted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $mountPoint
|
||||
* @param string $target
|
||||
*/
|
||||
public function moveMount(string $mountPoint, string $target) {
|
||||
public function moveMount(string $mountPoint, string $target): void {
|
||||
$this->mounts[$target] = $this->mounts[$mountPoint];
|
||||
unset($this->mounts[$mountPoint]);
|
||||
$this->pathCache->clear();
|
||||
$this->inPathCache->clear();
|
||||
$this->areMountsSorted = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the mount for $path
|
||||
*
|
||||
* @param string $path
|
||||
* @return IMountPoint
|
||||
*/
|
||||
public function find(string $path): IMountPoint {
|
||||
$this->setupManager->setupForPath($path);
|
||||
|
|
@ -79,8 +72,6 @@ class Manager implements IMountManager {
|
|||
return $this->pathCache[$path];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (count($this->mounts) === 0) {
|
||||
$this->setupManager->setupRoot();
|
||||
if (count($this->mounts) === 0) {
|
||||
|
|
@ -120,15 +111,57 @@ class Manager implements IMountManager {
|
|||
return $this->inPathCache[$path];
|
||||
}
|
||||
|
||||
$result = [];
|
||||
$pathLen = strlen($path);
|
||||
foreach ($this->mounts as $mountPoint => $mount) {
|
||||
if (strlen($mountPoint) > $pathLen && str_starts_with($mountPoint, $path)) {
|
||||
$result[] = $mount;
|
||||
if (!$this->areMountsSorted) {
|
||||
ksort($this->mounts, SORT_STRING);
|
||||
$this->mountKeys = array_keys($this->mounts);
|
||||
$this->areMountsSorted = true;
|
||||
}
|
||||
|
||||
$result = $this->binarySearch($this->mounts, $this->mountKeys, $path);
|
||||
|
||||
$this->inPathCache[$path] = $result;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for all entries in $sortedArray where $prefix is a prefix but not equal to their key.
|
||||
*
|
||||
* @template T
|
||||
* @param array<string, T> $sortedArray
|
||||
* @param list<string> $sortedKeys
|
||||
* @param string $prefix
|
||||
* @return list<T>
|
||||
*/
|
||||
private function binarySearch(array $sortedArray, array $sortedKeys, string $prefix): array {
|
||||
$low = 0;
|
||||
$high = count($sortedArray) - 1;
|
||||
$start = null;
|
||||
|
||||
// binary search
|
||||
while ($low <= $high) {
|
||||
$mid = ($low + $high) >> 1;
|
||||
if ($sortedKeys[$mid] < $prefix) {
|
||||
$low = $mid + 1;
|
||||
} else {
|
||||
$start = $mid;
|
||||
$high = $mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
if ($start !== null) {
|
||||
for ($i = $start, $n = count($sortedKeys); $i < $n; $i++) {
|
||||
$key = $sortedKeys[$i];
|
||||
if (!str_starts_with($key, $prefix)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($key !== $prefix) {
|
||||
$result[] = $sortedArray[$key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->inPathCache[$path] = $result;
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +234,7 @@ class Manager implements IMountManager {
|
|||
*
|
||||
* @param string $path
|
||||
* @param string[] $mountProviders
|
||||
* @return MountPoint[]
|
||||
* @return IMountPoint[]
|
||||
*/
|
||||
public function getMountsByMountProvider(string $path, array $mountProviders) {
|
||||
$this->getSetupManager()->setupForProvider($path, $mountProviders);
|
||||
|
|
|
|||
|
|
@ -54,6 +54,24 @@ class ManagerTest extends \Test\TestCase {
|
|||
$this->assertEquals([$mount1, $mount3], $this->manager->findByStorageId($id));
|
||||
}
|
||||
|
||||
public function testBinarySearch(): void {
|
||||
$sortedArray = [
|
||||
'a' => false,
|
||||
'aa' => false,
|
||||
'b' => false,
|
||||
'bb' => false,
|
||||
'bba' => true,
|
||||
'bbb' => true,
|
||||
'bdc' => false,
|
||||
];
|
||||
$sortedKey = array_keys($sortedArray);
|
||||
$result = $this->invokePrivate($this->manager, 'binarySearch', [$sortedArray, $sortedKey, 'bb']);
|
||||
$this->assertEquals(2, count($result));
|
||||
foreach ($result as $entry) {
|
||||
$this->assertTrue($entry);
|
||||
}
|
||||
}
|
||||
|
||||
public function testLong(): void {
|
||||
$storage = new LongId([]);
|
||||
$mount = new MountPoint($storage, '/foo');
|
||||
|
|
|
|||
Loading…
Reference in a new issue