Skip to content

Commit 52b9e5d

Browse files
authored
Merge pull request #3394 from mpilquist/topic/walk-drop-eager-optimization
Remove walkEager optimization
2 parents e7bf3ce + b763c99 commit 52b9e5d

File tree

5 files changed

+52
-180
lines changed

5 files changed

+52
-180
lines changed

io/js/src/main/scala/fs2/io/file/FilesPlatform.scala

+50-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ package fs2
2323
package io
2424
package file
2525

26-
import cats.effect.kernel.Async
27-
import cats.effect.kernel.Resource
26+
import cats.Traverse
27+
import cats.effect.kernel.{Async, Resource}
2828
import cats.syntax.all._
2929
import fs2.io.file.Files.UnsealedFiles
3030
import fs2.io.internal.facade
@@ -369,6 +369,54 @@ private[fs2] trait FilesCompanionPlatform {
369369
override def size(path: Path): F[Long] =
370370
stat(path).map(_.size.toString.toLong)
371371

372+
override def walkWithAttributes(start: Path, options: WalkOptions): Stream[F, PathInfo] = {
373+
374+
def go(
375+
start: Path,
376+
maxDepth: Int,
377+
ancestry: List[Either[Path, FileKey]]
378+
): Stream[F, PathInfo] =
379+
Stream.eval(getBasicFileAttributes(start, followLinks = false)).mask.flatMap { attr =>
380+
Stream.emit(PathInfo(start, attr)) ++ {
381+
if (maxDepth == 0) Stream.empty
382+
else if (attr.isDirectory)
383+
list(start).mask.flatMap { path =>
384+
go(path, maxDepth - 1, attr.fileKey.toRight(start) :: ancestry)
385+
}
386+
else if (attr.isSymbolicLink && options.followLinks)
387+
Stream.eval(getBasicFileAttributes(start, followLinks = true)).mask.flatMap { attr =>
388+
val fileKey = attr.fileKey
389+
val isCycle = Traverse[List].existsM(ancestry) {
390+
case Right(ancestorKey) => F.pure(fileKey.contains(ancestorKey))
391+
case Left(ancestorPath) => isSameFile(start, ancestorPath)
392+
}
393+
394+
Stream.eval(isCycle).flatMap { isCycle =>
395+
if (!isCycle)
396+
list(start).mask.flatMap { path =>
397+
go(path, maxDepth - 1, attr.fileKey.toRight(start) :: ancestry)
398+
}
399+
else if (options.allowCycles)
400+
Stream.empty
401+
else
402+
Stream.raiseError(new FileSystemLoopException(start.toString))
403+
}
404+
405+
}
406+
else
407+
Stream.empty
408+
}
409+
}
410+
411+
go(
412+
start,
413+
options.maxDepth,
414+
Nil
415+
)
416+
.chunkN(options.chunkSize)
417+
.flatMap(Stream.chunk)
418+
}
419+
372420
override def writeAll(path: Path, _flags: Flags): Pipe[F, Byte, Nothing] =
373421
in =>
374422
in.through {

io/jvm-native/src/main/scala/fs2/io/file/FilesPlatform.scala

+2-50
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,7 @@ private[file] trait FilesCompanionPlatform {
9292
private case class NioFileKey(value: AnyRef) extends FileKey
9393

9494
private final class AsyncFiles[F[_]](protected implicit val F: Async[F])
95-
extends Files.UnsealedFiles[F]
96-
with AsyncFilesPlatform[F] {
95+
extends Files.UnsealedFiles[F] {
9796

9897
def copy(source: Path, target: Path, flags: CopyFlags): F[Unit] =
9998
Sync[F].blocking {
@@ -391,61 +390,14 @@ private[file] trait FilesCompanionPlatform {
391390
.resource(Resource.fromAutoCloseable(javaCollection))
392391
.flatMap(ds => Stream.fromBlockingIterator[F](collectionIterator(ds), pathStreamChunkSize))
393392

394-
protected def walkEager(start: Path, options: WalkOptions): Stream[F, PathInfo] = {
395-
val doWalk = Sync[F].interruptible {
396-
val bldr = Vector.newBuilder[PathInfo]
397-
JFiles.walkFileTree(
398-
start.toNioPath,
399-
if (options.followLinks) Set(FileVisitOption.FOLLOW_LINKS).asJava else Set.empty.asJava,
400-
options.maxDepth,
401-
new SimpleFileVisitor[JPath] {
402-
private def enqueue(path: JPath, attrs: JBasicFileAttributes): FileVisitResult = {
403-
bldr += PathInfo(Path.fromNioPath(path), new DelegatingBasicFileAttributes(attrs))
404-
FileVisitResult.CONTINUE
405-
}
406-
407-
override def visitFile(file: JPath, attrs: JBasicFileAttributes): FileVisitResult =
408-
if (Thread.interrupted()) FileVisitResult.TERMINATE else enqueue(file, attrs)
409-
410-
override def visitFileFailed(file: JPath, t: IOException): FileVisitResult =
411-
t match {
412-
case _: FileSystemLoopException =>
413-
if (options.allowCycles)
414-
enqueue(
415-
file,
416-
JFiles.readAttributes(
417-
file,
418-
classOf[JBasicFileAttributes],
419-
LinkOption.NOFOLLOW_LINKS
420-
)
421-
)
422-
else throw t
423-
case _ => FileVisitResult.CONTINUE
424-
}
425-
426-
override def preVisitDirectory(
427-
dir: JPath,
428-
attrs: JBasicFileAttributes
429-
): FileVisitResult =
430-
if (Thread.interrupted()) FileVisitResult.TERMINATE else enqueue(dir, attrs)
431-
432-
override def postVisitDirectory(dir: JPath, t: IOException): FileVisitResult =
433-
if (Thread.interrupted()) FileVisitResult.TERMINATE else FileVisitResult.CONTINUE
434-
}
435-
)
436-
Chunk.from(bldr.result())
437-
}
438-
Stream.eval(doWalk).flatMap(Stream.chunk)
439-
}
440-
441393
private case class WalkEntry(
442394
path: Path,
443395
attr: JBasicFileAttributes,
444396
depth: Int,
445397
ancestry: List[Either[Path, NioFileKey]]
446398
)
447399

448-
protected def walkJustInTime(
400+
override def walkWithAttributes(
449401
start: Path,
450402
options: WalkOptions
451403
): Stream[F, PathInfo] = {

io/jvm/src/main/scala/fs2/io/file/AsyncFilesPlatform.scala

-41
This file was deleted.

io/native/src/main/scala/fs2/io/file/AsyncFilesPlatform.scala

-38
This file was deleted.

io/shared/src/main/scala/fs2/io/file/Files.scala

-49
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ import cats.effect.std.Hotswap
3131
import cats.syntax.all._
3232

3333
import scala.concurrent.duration._
34-
import cats.Traverse
3534

3635
/** Provides operations related to working with files in the effect `F`.
3736
*
@@ -525,54 +524,6 @@ object Files extends FilesCompanionPlatform with FilesLowPriority {
525524
case _: NoSuchFileException => ()
526525
})
527526

528-
def walkWithAttributes(start: Path, options: WalkOptions): Stream[F, PathInfo] = {
529-
530-
def go(
531-
start: Path,
532-
maxDepth: Int,
533-
ancestry: List[Either[Path, FileKey]]
534-
): Stream[F, PathInfo] =
535-
Stream.eval(getBasicFileAttributes(start, followLinks = false)).mask.flatMap { attr =>
536-
Stream.emit(PathInfo(start, attr)) ++ {
537-
if (maxDepth == 0) Stream.empty
538-
else if (attr.isDirectory)
539-
list(start).mask.flatMap { path =>
540-
go(path, maxDepth - 1, attr.fileKey.toRight(start) :: ancestry)
541-
}
542-
else if (attr.isSymbolicLink && options.followLinks)
543-
Stream.eval(getBasicFileAttributes(start, followLinks = true)).mask.flatMap { attr =>
544-
val fileKey = attr.fileKey
545-
val isCycle = Traverse[List].existsM(ancestry) {
546-
case Right(ancestorKey) => F.pure(fileKey.contains(ancestorKey))
547-
case Left(ancestorPath) => isSameFile(start, ancestorPath)
548-
}
549-
550-
Stream.eval(isCycle).flatMap { isCycle =>
551-
if (!isCycle)
552-
list(start).mask.flatMap { path =>
553-
go(path, maxDepth - 1, attr.fileKey.toRight(start) :: ancestry)
554-
}
555-
else if (options.allowCycles)
556-
Stream.empty
557-
else
558-
Stream.raiseError(new FileSystemLoopException(start.toString))
559-
}
560-
561-
}
562-
else
563-
Stream.empty
564-
}
565-
}
566-
567-
go(
568-
start,
569-
options.maxDepth,
570-
Nil
571-
)
572-
.chunkN(options.chunkSize)
573-
.flatMap(Stream.chunk)
574-
}
575-
576527
def writeAll(
577528
path: Path,
578529
flags: Flags

0 commit comments

Comments
 (0)