FileSystem (Polyglot)¶

In [ ]:
#r @"../../../../../../../.nuget/packages/fsharp.control.asyncseq/3.2.1/lib/netstandard2.1/FSharp.Control.AsyncSeq.dll"
#r @"../../../../../../../.nuget/packages/system.reactive/6.0.1-preview.1/lib/net6.0/System.Reactive.dll"
#r @"../../../../../../../.nuget/packages/system.reactive.linq/6.0.1-preview.1/lib/netstandard2.0/System.Reactive.Linq.dll"
#r @"../../../../../../../.nuget/packages/argu/6.2.4/lib/netstandard2.0/Argu.dll"
In [ ]:
#!import ../../lib/fsharp/Notebooks.dib
#!import ../../lib/fsharp/Testing.dib
In [ ]:
#!import ../../lib/fsharp/Common.fs
#!import ../../lib/fsharp/CommonFSharp.fs
#!import ../../lib/fsharp/Async.fs
#!import ../../lib/fsharp/AsyncSeq.fs
#!import ../../lib/fsharp/Runtime.fs
In [ ]:
#if !INTERACTIVE
open Lib
#endif
In [ ]:
open Common
open SpiralFileSystem.Operators

watchDirectory¶

In [ ]:
[<RequireQualifiedAccess>]
type FileSystemChangeType =
    | Failure
    | Changed
    | Created
    | Deleted
    | Renamed

[<RequireQualifiedAccess>]
type FileSystemChange =
    | Failure of exn: exn
    | Changed of path: string * content: string option
    | Created of path: string * content: string option
    | Deleted of path: string
    | Renamed of oldPath: string * (string * string option)


let inline watchDirectoryWithFilter filter shouldReadContent path =
    let fullPath = path |> System.IO.Path.GetFullPath
    let _locals () = $"filter: {filter} / {_locals ()}"

    let watcher =
        new System.IO.FileSystemWatcher (
            Path = fullPath,
            NotifyFilter = filter,
            EnableRaisingEvents = true,
            IncludeSubdirectories = true
        )

    let inline getEventPath (path : string) =
        path |> SpiralSm.trim |> SpiralSm.replace fullPath "" |> SpiralSm.trim_start [| '/'; '\\' |]

    let inline ticks () =
        System.DateTime.UtcNow.Ticks

    let changedStream =
        AsyncSeq.subscribeEvent
            watcher.Changed
            (fun event ->
                ticks (),
                [ FileSystemChange.Changed (getEventPath event.FullPath, None) ]
            )

    let deletedStream =
        AsyncSeq.subscribeEvent
            watcher.Deleted
            (fun event ->
                ticks (),
                [ FileSystemChange.Deleted (getEventPath event.FullPath) ]
            )

    let createdStream =
        AsyncSeq.subscribeEvent
            watcher.Created
            (fun event ->
                let path = getEventPath event.FullPath
                ticks (), [
                    FileSystemChange.Created (path, None)
                    if SpiralPlatform.is_windows () then
                        FileSystemChange.Changed (path, None)
                ])

    let renamedStream =
        AsyncSeq.subscribeEvent
            watcher.Renamed
            (fun event ->
                ticks (), [
                    FileSystemChange.Renamed (
                        getEventPath event.OldFullPath,
                        (getEventPath event.FullPath, None)
                    )
                ]
            )

    let failureStream =
        AsyncSeq.subscribeEvent
            watcher.Error
            (fun event -> ticks (), [ FileSystemChange.Failure (event.GetException ()) ])

    let stream =
        [
            changedStream
            deletedStream
            createdStream
            renamedStream
            failureStream
        ]
        |> FSharp.Control.AsyncSeq.mergeAll
        |> FSharp.Control.AsyncSeq.map (fun (t, events) ->
            events
            |> List.fold
                (fun (i, events) event ->
                    i + 1L,
                    (t + i, event) :: events)
                (0L, [])
            |> snd
            |> List.rev
        )
        |> FSharp.Control.AsyncSeq.concatSeq
        |> FSharp.Control.AsyncSeq.mapAsyncParallel (fun (t, event) -> async {
            match shouldReadContent event, event with
            | true, FileSystemChange.Changed (path, _) ->
                do! Async.Sleep 5
                let! content = fullPath </> path |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Changed (path, content)
            | true, FileSystemChange.Created (path, _) ->
                do! Async.Sleep 5
                let! content = fullPath </> path |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Created (path, content)
            | true, FileSystemChange.Renamed (oldPath, (newPath, _)) ->
                let! content = fullPath </> newPath |> SpiralFileSystem.read_all_text_retry_async
                return t, FileSystemChange.Renamed (oldPath, (newPath, content))
            | _ -> return t, event
        })

    let disposable =
        new_disposable (fun () ->
            trace Debug (fun () -> "FileSystem.watchWithFilter / Disposing watch stream") _locals
            watcher.EnableRaisingEvents <- false
            watcher.Dispose ()
        )

    stream, disposable

let inline watchDirectory path =
    watchDirectoryWithFilter
        (System.IO.NotifyFilters.FileName
        // ||| System.IO.NotifyFilters.DirectoryName
        // ||| System.IO.NotifyFilters.Attributes
        //// ||| System.IO.NotifyFilters.Size
        ||| System.IO.NotifyFilters.LastWrite
        //// ||| System.IO.NotifyFilters.LastAccess
        // ||| System.IO.NotifyFilters.CreationTime
        // ||| System.IO.NotifyFilters.Security
        )
        path

testEventsRaw (test)¶

In [ ]:
//// test

let inline testEventsRaw
    (watchFn : (_ -> bool) -> string -> FSharp.Control.AsyncSeq<int64 * FileSystemChange> * IDisposable)
    write
    =
    let struct (tempDir, tempDisposable) =
        "FileSystem.testEventsRaw"
        |> SpiralCrypto.hash_text
        |> SpiralFileSystem.create_temp_dir'
    let stream, disposable = watchFn (fun _ -> true) tempDir

    let events = System.Collections.Concurrent.ConcurrentBag ()

    let inline iter () =
        stream
        |> FSharp.Control.AsyncSeq.iterAsyncParallel (fun event -> async { events.Add event })

    let run = async {
        let! _ = iter () |> Async.StartChild
        do! Async.Sleep 250
        return! write tempDir
    }

    try
        run
        |> Async.runWithTimeout 60000
        |> _assertEqual (Some ())
    finally
        disposable.Dispose ()
        tempDisposable.Dispose ()

    let eventsLog =
        events
        |> Seq.toList
        |> List.sortBy fst
        |> List.fold
            (fun (prev, acc) (ticks, event) ->
                ticks, (ticks, (if prev = 0L then 0L else ticks - prev), event) :: acc
            )
            (0L, [])
        |> snd
        |> List.rev
        |> List.map (fun (diff, n, event) -> $"{n} / {diff} / {event}" |> SpiralSm.ellipsis_end 100L)
        |> SpiralSm.concat "\n"
    let _locals () = $"eventsLog: \n{eventsLog} / {_locals ()}"
    trace Debug (fun () -> "FileSystem.testEventsRaw") _locals

    events
    |> Seq.toList
    |> List.sortBy fst
    |> List.map snd
    |> List.fold
        (fun acc event ->
            match acc, event with
            | FileSystemChange.Changed (lastPath, Some lastContent) as lastEvent :: acc,
                FileSystemChange.Changed (path, Some content)
                when lastPath = path && content |> SpiralSm.starts_with lastContent
                ->
                event :: acc
            | _ -> event :: acc
        )
        []
    |> List.rev

fast (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"a{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! $"b{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 250

    for i = 1 to n do
        do! $"c{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    do! Async.Sleep 250

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 250
}

let inline run () =
    let events = testEventsRaw watchDirectory write

    events
    |> _sequenceEqual [
        FileSystemChange.Created ("file1.txt", Some "a1")
        FileSystemChange.Changed ("file1.txt", Some "a1")
        FileSystemChange.Created ("file2.txt", Some "a2")
        FileSystemChange.Changed ("file2.txt", Some "a2")

        FileSystemChange.Changed ("file1.txt", Some "b1")
        FileSystemChange.Changed ("file2.txt", Some "b2")

        FileSystemChange.Renamed ("file1.txt", ("file_1.txt", Some "b1"))
        FileSystemChange.Renamed ("file2.txt", ("file_2.txt", Some "b2"))

        FileSystemChange.Changed ("file_1.txt", Some "c1")
        FileSystemChange.Changed ("file_2.txt", Some "c2")

        FileSystemChange.Deleted "file_1.txt"
        FileSystemChange.Deleted "file_2.txt"
    ]

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:03 d #1 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
00:00:03 d #2 FileSystem.testEventsRaw / eventsLog: 
0 / 638989491342911185 / Created ("file1.txt", Some "a1")
14231 / 638989491342925416 / Changed ("file1.txt", Some "a1")
1738 / 638989491342927154 / Created ("file2.txt", Some "a2")
43 / 638989491342927197 / Changed ("file2.txt", Some "a2")
2501942 / 638989491345429139 / Changed ("file1.txt", Some "b1")
469 / 638989491345429608 / Changed ("file1.txt", Some "b1")
5655 / 638989491345435263 / Changed ("file2.txt", Some "b2")
225 / 638989491345435488 / Changed ("file2.txt", Some "b2")
2589646 / 638989491348025134 / Renamed ("file1.txt", ("file_1.txt", Some "b1"))
7769 / 638989491348032903 / Renamed ("file2.txt", ("file_2.txt", Some "b2"))
2541171 / 638989491350574074 / Changed ("file_1.txt", Some "c1")
441 / 638989491350574515 / Changed ("file_1.txt", Some "c1")
5728 / 638989491350580243 / Changed ("file_2.txt", Some "c2")
235 / 638989491350580478 / Changed ("file_2.txt", Some "c2")
2554526 / 638989491353135004 / Deleted "file_1.txt"
1253 / 638989491353136257 / Deleted "file_2.txt"
[Created ("file1.txt", Some "a1"); Changed ("file1.txt", Some "a1"); Created ("file2.txt", Some "a2");
 Changed ("file2.txt", Some "a2"); Changed ("file1.txt", Some "b1"); Changed ("file2.txt", Some "b2");
 Renamed ("file1.txt", ("file_1.txt", Some "b1")); Renamed ("file2.txt", ("file_2.txt", Some "b2"));
 Changed ("file_1.txt", Some "c1"); Changed ("file_2.txt", Some "c2"); Deleted "file_1.txt"; Deleted "file_2.txt"]

Some ()

slow (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    let contents =
        [ 1 .. n ]
        |> List.map (string >> String.replicate 1_000_000)

    for i = 1 to n do
        do! $"{contents.[i - 1]}a" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! $"{contents.[i - 1]}b" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 1500

    for i = 1 to n do
        do! $"{contents.[i - 1]}c" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    do! Async.Sleep 1500

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 1500
}

let inline run () =
    let events =
        testEventsRaw watchDirectory write
        |> List.map (function
            | FileSystemChange.Changed (path, Some content) ->
                FileSystemChange.Changed (path, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
            | FileSystemChange.Created (path, Some content) ->
                FileSystemChange.Created (path, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
            | FileSystemChange.Renamed (oldPath, (newPath, Some content)) ->
                FileSystemChange.Renamed (
                    oldPath,
                    (newPath, content |> Seq.distinct |> Seq.map string |> SpiralSm.concat "" |> Some)
                )
            | event -> event
        )

    events
    |> _sequenceEqual [
        FileSystemChange.Created ("file1.txt", Some "1a")
        FileSystemChange.Changed ("file1.txt", Some "1a")
        FileSystemChange.Created ("file2.txt", Some "2a")
        FileSystemChange.Changed ("file2.txt", Some "2a")

        FileSystemChange.Changed ("file1.txt", Some "1b")
        FileSystemChange.Changed ("file2.txt", Some "2b")

        FileSystemChange.Renamed ("file1.txt", ("file_1.txt", Some "1b"))
        FileSystemChange.Renamed ("file2.txt", ("file_2.txt", Some "2b"))

        FileSystemChange.Changed ("file_1.txt", Some "1c")
        FileSystemChange.Changed ("file_2.txt", Some "2c")

        FileSystemChange.Deleted "file_1.txt"
        FileSystemChange.Deleted "file_2.txt"
    ]

run
|> retry_fn 5
|> _assertEqual (Some ())
Some ()

00:00:12 d #3 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
00:00:13 d #4 FileSystem.testEventsRaw / eventsLog: 
0 / 638989491368548102 / Created
  ("file1.txt",
 ...11111111111111111111111111111111111111111111111a")
3161 / 638989491368551263 / Changed
  ("file1.txt"...11111111111111111111111111111111111111111111111a")
236 / 638989491368551499 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
80 / 638989491368551579 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
236 / 638989491368551815 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
62 / 638989491368551877 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
693 / 638989491368552570 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
138 / 638989491368552708 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
332 / 638989491368553040 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
72 / 638989491368553112 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
173 / 638989491368553285 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
73 / 638989491368553358 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
106 / 638989491368553464 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
45 / 638989491368553509 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
144 / 638989491368553653 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
82 / 638989491368553735 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
177 / 638989491368553912 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
138 / 638989491368554050 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
111 / 638989491368554161 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
62 / 638989491368554223 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
189 / 638989491368554412 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
36 / 638989491368554448 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
64 / 638989491368554512 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
281 / 638989491368554793 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
42 / 638989491368554835 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
45 / 638989491368554880 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
188 / 638989491368555068 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
175 / 638989491368555243 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
30 / 638989491368555273 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
176 / 638989491368555449 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
88 / 638989491368555537 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
262 / 638989491368555799 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
54 / 638989491368555853 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
44 / 638989491368555897 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
193 / 638989491368556090 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
166 / 638989491368556256 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
55 / 638989491368556311 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
65 / 638989491368556376 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
269 / 638989491368556645 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
81 / 638989491368556726 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
30 / 638989491368556756 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
115 / 638989491368556871 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
199 / 638989491368557070 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
120 / 638989491368557190 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
113 / 638989491368557303 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111a")
71 / 638989491368557374 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
44 / 638989491368557418 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111a")
17406 / 638989491368574824 / Created
  ("file2.txt...22222222222222222222222222222222222222222222222a")
131 / 638989491368574955 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
59 / 638989491368575014 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
846 / 638989491368575860 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
1387 / 638989491368577247 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
3509 / 638989491368580756 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
669 / 638989491368581425 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
83 / 638989491368581508 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
194 / 638989491368581702 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
44 / 638989491368581746 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
192 / 638989491368581938 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
54 / 638989491368581992 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
188 / 638989491368582180 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
310 / 638989491368582490 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
50 / 638989491368582540 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
3378 / 638989491368585918 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
89 / 638989491368586007 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
42 / 638989491368586049 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
957 / 638989491368587006 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222a")
70 / 638989491368587076 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
1105 / 638989491368588181 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
87 / 638989491368588268 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222a")
8081 / 638989491368596349 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222a")
15040998 / 638989491383637347 / Changed
  ("file1....11111111111111111111111111111111111111111111111b")
433 / 638989491383637780 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
2613 / 638989491383640393 / Changed
  ("file1.txt"...11111111111111111111111111111111111111111111111b")
104 / 638989491383640497 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
357 / 638989491383640854 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
213 / 638989491383641067 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
196 / 638989491383641263 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
377 / 638989491383641640 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
472 / 638989491383642112 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
403 / 638989491383642515 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
183 / 638989491383642698 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
184 / 638989491383642882 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
173 / 638989491383643055 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
172 / 638989491383643227 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
164 / 638989491383643391 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
51 / 638989491383643442 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
228 / 638989491383643670 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
162 / 638989491383643832 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
169 / 638989491383644001 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
187 / 638989491383644188 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
165 / 638989491383644353 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
169 / 638989491383644522 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
47 / 638989491383644569 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
268 / 638989491383644837 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
159 / 638989491383644996 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
176 / 638989491383645172 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
156 / 638989491383645328 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
163 / 638989491383645491 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
48 / 638989491383645539 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
195 / 638989491383645734 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
331 / 638989491383646065 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
76 / 638989491383646141 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
241 / 638989491383646382 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
155 / 638989491383646537 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
176 / 638989491383646713 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
176 / 638989491383646889 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
230 / 638989491383647119 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
169 / 638989491383647288 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
173 / 638989491383647461 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
210 / 638989491383647671 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
48 / 638989491383647719 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
166 / 638989491383647885 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
154 / 638989491383648039 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
173 / 638989491383648212 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
165 / 638989491383648377 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
171 / 638989491383648548 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
171 / 638989491383648719 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
50 / 638989491383648769 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
188 / 638989491383648957 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
144 / 638989491383649101 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
169 / 638989491383649270 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
175 / 638989491383649445 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
159 / 638989491383649604 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
189 / 638989491383649793 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
65 / 638989491383649858 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
38 / 638989491383649896 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
299 / 638989491383650195 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
215 / 638989491383650410 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
48 / 638989491383650458 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
183 / 638989491383650641 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
40 / 638989491383650681 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
245 / 638989491383650926 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
234 / 638989491383651160 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
170 / 638989491383651330 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
44 / 638989491383651374 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
200 / 638989491383651574 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
182 / 638989491383651756 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
171 / 638989491383651927 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
49 / 638989491383651976 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
197 / 638989491383652173 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
358 / 638989491383652531 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
247 / 638989491383652778 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
178 / 638989491383652956 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
47 / 638989491383653003 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
208 / 638989491383653211 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
163 / 638989491383653374 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
43 / 638989491383653417 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
236 / 638989491383653653 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
45 / 638989491383653698 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
320 / 638989491383654018 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
45 / 638989491383654063 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
187 / 638989491383654250 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
28 / 638989491383654278 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
183 / 638989491383654461 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
187 / 638989491383654648 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
67 / 638989491383654715 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
39 / 638989491383654754 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
300 / 638989491383655054 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
54 / 638989491383655108 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
219 / 638989491383655327 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
179 / 638989491383655506 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
48 / 638989491383655554 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
203 / 638989491383655757 / Changed
  ("file1.txt",...11111111111111111111111111111111111111111111111b")
58 / 638989491383655815 / Changed
  ("file1.txt",
...11111111111111111111111111111111111111111111111b")
11881 / 638989491383667696 / Changed
  ("file2.txt...22222222222222222222222222222222222222222222222b")
114 / 638989491383667810 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
306 / 638989491383668116 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
212 / 638989491383668328 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
45 / 638989491383668373 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
191 / 638989491383668564 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
53 / 638989491383668617 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
198 / 638989491383668815 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
24 / 638989491383668839 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
261 / 638989491383669100 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
29 / 638989491383669129 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
196 / 638989491383669325 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
23 / 638989491383669348 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
194 / 638989491383669542 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
50 / 638989491383669592 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
826 / 638989491383670418 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
57 / 638989491383670475 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
45 / 638989491383670520 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
37 / 638989491383670557 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
253 / 638989491383670810 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
47 / 638989491383670857 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
153 / 638989491383671010 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
598 / 638989491383671608 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
64 / 638989491383671672 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
35 / 638989491383671707 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
227 / 638989491383671934 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
166 / 638989491383672100 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
368 / 638989491383672468 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
302 / 638989491383672770 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
202 / 638989491383672972 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
50 / 638989491383673022 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
195 / 638989491383673217 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
177 / 638989491383673394 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
425 / 638989491383673819 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
60 / 638989491383673879 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
164 / 638989491383674043 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
1224 / 638989491383675267 / Changed
  ("file2.txt"...22222222222222222222222222222222222222222222222b")
97 / 638989491383675364 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
38 / 638989491383675402 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
223 / 638989491383675625 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
52 / 638989491383675677 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
179 / 638989491383675856 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
162 / 638989491383676018 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
236 / 638989491383676254 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
222 / 638989491383676476 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
181 / 638989491383676657 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
193 / 638989491383676850 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
156 / 638989491383677006 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
511 / 638989491383677517 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
173 / 638989491383677690 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
164 / 638989491383677854 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
234 / 638989491383678088 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
46 / 638989491383678134 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
171 / 638989491383678305 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
173 / 638989491383678478 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
146 / 638989491383678624 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
631 / 638989491383679255 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
183 / 638989491383679438 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
175 / 638989491383679613 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
180 / 638989491383679793 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
159 / 638989491383679952 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
46 / 638989491383679998 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
180 / 638989491383680178 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
45 / 638989491383680223 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
162 / 638989491383680385 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
664 / 638989491383681049 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
123 / 638989491383681172 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
42 / 638989491383681214 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
233 / 638989491383681447 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
199 / 638989491383681646 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
207 / 638989491383681853 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
997 / 638989491383682850 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
68 / 638989491383682918 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
38 / 638989491383682956 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
293 / 638989491383683249 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
206 / 638989491383683455 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
165 / 638989491383683620 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
133 / 638989491383683753 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
527 / 638989491383684280 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
199 / 638989491383684479 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
47 / 638989491383684526 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
163 / 638989491383684689 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
61 / 638989491383684750 / Changed
  ("file2.txt",
...22222222222222222222222222222222222222222222222b")
138 / 638989491383684888 / Changed
  ("file2.txt",...22222222222222222222222222222222222222222222222b")
15048698 / 638989491398733586 / Renamed
  ("file1....1111111111111111111111111111111111111111111111b"))
538 / 638989491398734124 / Renamed
  ("file2.txt",...2222222222222222222222222222222222222222222222b"))
15055228 / 638989491413789352 / Changed
  ("file_1...11111111111111111111111111111111111111111111111c")
535 / 638989491413789887 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
764 / 638989491413790651 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
128 / 638989491413790779 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
71 / 638989491413790850 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
45 / 638989491413790895 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
434 / 638989491413791329 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
57 / 638989491413791386 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
74 / 638989491413791460 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
44 / 638989491413791504 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
57 / 638989491413791561 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
409 / 638989491413791970 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
95 / 638989491413792065 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
38 / 638989491413792103 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
1058 / 638989491413793161 / Changed
  ("file_1.txt...11111111111111111111111111111111111111111111111c")
173 / 638989491413793334 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
210 / 638989491413793544 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
708 / 638989491413794252 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
486 / 638989491413794738 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
471 / 638989491413795209 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
87 / 638989491413795296 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
192 / 638989491413795488 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
184 / 638989491413795672 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
314 / 638989491413795986 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
67 / 638989491413796053 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
150 / 638989491413796203 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
171 / 638989491413796374 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
5236 / 638989491413801610 / Changed
  ("file_1.txt...11111111111111111111111111111111111111111111111c")
114 / 638989491413801724 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
527 / 638989491413802251 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
825 / 638989491413803076 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
77 / 638989491413803153 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
59 / 638989491413803212 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
1296 / 638989491413804508 / Changed
  ("file_1.txt...11111111111111111111111111111111111111111111111c")
97 / 638989491413804605 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
519 / 638989491413805124 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
80 / 638989491413805204 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
63 / 638989491413805267 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
344 / 638989491413805611 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
230 / 638989491413805841 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
44 / 638989491413805885 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
75 / 638989491413805960 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
354 / 638989491413806314 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
72 / 638989491413806386 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
55 / 638989491413806441 / Changed
  ("file_1.txt",...11111111111111111111111111111111111111111111111c")
337 / 638989491413806778 / Changed
  ("file_1.txt"...11111111111111111111111111111111111111111111111c")
17846 / 638989491413824624 / Changed
  ("file_2.tx...22222222222222222222222222222222222222222222222c")
197 / 638989491413824821 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
335 / 638989491413825156 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
90 / 638989491413825246 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
164 / 638989491413825410 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
52 / 638989491413825462 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
290 / 638989491413825752 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
113 / 638989491413825865 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
298 / 638989491413826163 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
210 / 638989491413826373 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
60 / 638989491413826433 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
262 / 638989491413826695 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
152 / 638989491413826847 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
168 / 638989491413827015 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
48 / 638989491413827063 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
208 / 638989491413827271 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
160 / 638989491413827431 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
47 / 638989491413827478 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
60 / 638989491413827538 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
848 / 638989491413828386 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
87 / 638989491413828473 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
232 / 638989491413828705 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
57 / 638989491413828762 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
190 / 638989491413828952 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
363 / 638989491413829315 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
367 / 638989491413829682 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
101 / 638989491413829783 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
46 / 638989491413829829 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
348 / 638989491413830177 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
215 / 638989491413830392 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
27 / 638989491413830419 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
174 / 638989491413830593 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
48 / 638989491413830641 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
271 / 638989491413830912 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
49 / 638989491413830961 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
196 / 638989491413831157 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
333 / 638989491413831490 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
65 / 638989491413831555 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
244 / 638989491413831799 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
52 / 638989491413831851 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
369 / 638989491413832220 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
212 / 638989491413832432 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
991 / 638989491413833423 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
77 / 638989491413833500 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
210 / 638989491413833710 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
174 / 638989491413833884 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
53 / 638989491413833937 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
187 / 638989491413834124 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
66 / 638989491413834190 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
175 / 638989491413834365 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
155 / 638989491413834520 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
161 / 638989491413834681 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
166 / 638989491413834847 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
49 / 638989491413834896 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
170 / 638989491413835066 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
66 / 638989491413835132 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
121 / 638989491413835253 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
177 / 638989491413835430 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
39 / 638989491413835469 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
708 / 638989491413836177 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
73 / 638989491413836250 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
40 / 638989491413836290 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
216 / 638989491413836506 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
57 / 638989491413836563 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
157 / 638989491413836720 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
152 / 638989491413836872 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
176 / 638989491413837048 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
165 / 638989491413837213 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
189 / 638989491413837402 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
49 / 638989491413837451 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
148 / 638989491413837599 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
191 / 638989491413837790 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
379 / 638989491413838169 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
61 / 638989491413838230 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
389 / 638989491413838619 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
267 / 638989491413838886 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
53 / 638989491413838939 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
382 / 638989491413839321 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
64 / 638989491413839385 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
431 / 638989491413839816 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
248 / 638989491413840064 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
46 / 638989491413840110 / Changed
  ("file_2.txt",...22222222222222222222222222222222222222222222222c")
325 / 638989491413840435 / Changed
  ("file_2.txt"...22222222222222222222222222222222222222222222222c")
15042778 / 638989491428883213 / Deleted "file_1.txt"
2633 / 638989491428885846 / Deleted "file_2.txt"
[Created ("file1.txt", Some "1a"); Changed ("file1.txt", Some "1a"); Created ("file2.txt", Some "2a");
 Changed ("file2.txt", Some "2a"); Changed ("file1.txt", Some "1b"); Changed ("file2.txt", Some "2b");
 Renamed ("file1.txt", ("file_1.txt", Some "1b")); Renamed ("file2.txt", ("file_2.txt", Some "2b"));
 Changed ("file_1.txt", Some "1c"); Changed ("file_2.txt", Some "2c"); Deleted "file_1.txt"; Deleted "file_2.txt"]

Some ()

testEventsSorted (test)¶

In [ ]:
//// test

let inline sortEvent event =
    match event with
    | FileSystemChange.Failure _ -> 0
    | FileSystemChange.Created _ -> 1
    | FileSystemChange.Changed _ -> 2
    | FileSystemChange.Renamed (_oldPath, _) -> 3
    | FileSystemChange.Deleted _ -> 4

let inline formatEvents events =
    events
    |> Seq.toList
    |> List.sortBy (snd >> sortEvent)
    |> List.choose (fun (ticks, event) ->
        match event with
        | FileSystemChange.Failure _ ->
            None
        | FileSystemChange.Changed (path, _) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Changed)
        | FileSystemChange.Created (path, _) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Created)
        | FileSystemChange.Deleted path ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Deleted)
        | FileSystemChange.Renamed (_oldPath, (path, _)) ->
            Some (ticks, System.IO.Path.GetFileName path, nameof FileSystemChangeType.Renamed)
    )
    |> List.sortBy (fun (_, path, _) -> path)
    |> List.distinctBy (fun (_, path, event) -> path, event)

let inline testEventsSorted
    (watchFn : string -> FSharp.Control.AsyncSeq<int64 * FileSystemChange> * IDisposable)
    write
    =
    let struct (tempDir, tempDisposable) =
        "FileSystem.testEventsSorted"
        |> SpiralCrypto.hash_text
        |> SpiralFileSystem.create_temp_dir'
    let stream, disposable = watchFn tempDir

    let events = System.Collections.Concurrent.ConcurrentBag ()

    let inline iter () =
        stream
        |> FSharp.Control.AsyncSeq.iterAsyncParallel (fun event -> async { events.Add event })

    let run = async {
        let! _ = iter () |> Async.StartChild
        do! Async.Sleep 250
        return! write tempDir
    }

    try
        run
        |> Async.runWithTimeout 5000
        |> _assertEqual (Some ())
    finally
        disposable.Dispose ()
        tempDisposable.Dispose ()

    let events = formatEvents events

    let eventMap =
        events
        |> List.map (fun (ticks, path, event) -> path, (event, ticks))
        |> List.groupBy fst
        |> List.map (fun (path, events) ->
            let event, _ticks =
                events
                |> List.map snd
                |> List.sortByDescending snd
                |> List.head

            path, event
        )
        |> Map.ofList

    let eventList =
        events
        |> List.map (fun (_ticks, path, event) -> path, event)

    eventMap, eventList

create and delete (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 3

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file1.txt", nameof FileSystemChangeType.Deleted

        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Deleted

        "file3.txt", nameof FileSystemChangeType.Created
        "file3.txt", nameof FileSystemChangeType.Changed
        "file3.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Deleted
        "file2.txt", nameof FileSystemChangeType.Deleted
        "file3.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:15 d #5 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file1.txt", "Deleted"); ("file2.txt", "Created");
 ("file2.txt", "Changed"); ("file2.txt", "Deleted"); ("file3.txt", "Created"); ("file3.txt", "Changed");
 ("file3.txt", "Deleted")]

map [("file1.txt", "Deleted"); ("file2.txt", "Deleted"); ("file3.txt", "Deleted")]

Some ()

change (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! "" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file1.txt", nameof FileSystemChangeType.Deleted

        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Deleted
        "file2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:16 d #6 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file1.txt", "Deleted"); ("file2.txt", "Created");
 ("file2.txt", "Changed"); ("file2.txt", "Deleted")]

map [("file1.txt", "Deleted"); ("file2.txt", "Deleted")]

Some ()

rename (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed

        "file_1.txt", nameof FileSystemChangeType.Renamed
        "file_1.txt", nameof FileSystemChangeType.Deleted

        "file_2.txt", nameof FileSystemChangeType.Renamed
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Deleted
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:17 d #7 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file2.txt", "Created"); ("file2.txt", "Changed");
 ("file_1.txt", "Renamed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Renamed"); ("file_2.txt", "Deleted")]

map [("file1.txt", "Changed"); ("file2.txt", "Changed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Deleted")]

Some ()

full (test)¶

In [ ]:
//// test

let inline write path = async {
    let n = 2

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! "" |> SpiralFileSystem.write_all_text_async (path </> $"file{i}.txt")

    for i = 1 to n do
        do! path </> $"file{i}.txt" |> SpiralFileSystem.move_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    for i = 1 to n do
        do! $"{i}" |> SpiralFileSystem.write_all_text_async (path </> $"file_{i}.txt")

    for i = 1 to n do
        do! SpiralFileSystem.delete_file_async (path </> $"file_{i}.txt") |> Async.Ignore

    do! Async.Sleep 150
}

let inline run () =
    let eventMap, eventList = testEventsSorted (watchDirectory (fun _ -> false)) write

    [
        "file1.txt", nameof FileSystemChangeType.Created
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Created
        "file2.txt", nameof FileSystemChangeType.Changed

        "file_1.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Renamed
        "file_1.txt", nameof FileSystemChangeType.Deleted

        "file_2.txt", nameof FileSystemChangeType.Changed
        "file_2.txt", nameof FileSystemChangeType.Renamed
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> _sequenceEqual eventList

    [
        "file1.txt", nameof FileSystemChangeType.Changed
        "file2.txt", nameof FileSystemChangeType.Changed
        "file_1.txt", nameof FileSystemChangeType.Deleted
        "file_2.txt", nameof FileSystemChangeType.Deleted
    ]
    |> Map.ofList
    |> _sequenceEqual eventMap

run
|> retry_fn 3
|> _assertEqual (Some ())
Some ()

00:00:18 d #8 FileSystem.watchWithFilter / Disposing watch stream / filter: FileName, LastWrite
[("file1.txt", "Created"); ("file1.txt", "Changed"); ("file2.txt", "Created"); ("file2.txt", "Changed");
 ("file_1.txt", "Changed"); ("file_1.txt", "Renamed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Changed");
 ("file_2.txt", "Renamed"); ("file_2.txt", "Deleted")]

map [("file1.txt", "Changed"); ("file2.txt", "Changed"); ("file_1.txt", "Deleted"); ("file_2.txt", "Deleted")]

Some ()