@@ -5,60 +5,76 @@ Imports System.Threading
5
5
6
6
Public Class Analyser
7
7
8
- Public Sub New (folder As String )
9
- FolderName = folder
10
- End Sub
11
-
12
8
Public Property FolderName As String
13
9
Public Property UncompressedBytes As Long
14
10
Public Property CompressedBytes As Long
15
11
Public Property ContainsCompressedFiles As Boolean
16
12
Public Property FileCompressionDetailsList As List( Of AnalysedFileDetails)
17
- Private _testField As Integer = 0
18
13
19
- Public Async Function AnalyseFolder(cancellationToken As CancellationToken) As Task( Of Boolean )
14
+ Public Sub New (folder As String )
15
+ FolderName = folder
16
+ End Sub
20
17
18
+
19
+ Public Async Function AnalyseFolder(cancellationToken As CancellationToken) As Task( Of Boolean )
21
20
Dim allFiles = Await Task.Run( Function () Directory.EnumerateFiles(FolderName, "*" , New EnumerationOptions() With {.RecurseSubdirectories = True , .IgnoreInaccessible = True }).AsShortPathNames, cancellationToken).ConfigureAwait( False )
22
- Dim compressedFilesCount As Integer
23
21
Dim fileDetails As New Concurrent.ConcurrentBag( Of AnalysedFileDetails)
22
+ Dim compressedFilesCount As Integer = 0
23
+
24
+ ' Use local variables to reduce contention
25
+ Dim localCompressedBytes As Long = 0
26
+ Dim localUncompressedBytes As Long = 0
24
27
25
28
Try
26
- Dim res = Await Task.Run( Function () Parallel.ForEach(allFiles, New ParallelOptions With {.CancellationToken = cancellationToken}, Sub (file) AnalyseFile(file, compressedFilesCount, fileDetails)))
29
+ Parallel.ForEach(allFiles, New ParallelOptions With {.CancellationToken = cancellationToken},
30
+ Sub (file)
31
+ Dim details = AnalyseFile(file)
32
+ If details IsNot Nothing Then
33
+ fileDetails.Add(details)
34
+ If details.CompressionMode <> CompressionAlgorithm.NO_COMPRESSION Then
35
+ Interlocked.Increment(compressedFilesCount)
36
+ End If
37
+ Interlocked.Add(localCompressedBytes, details.CompressedSize)
38
+ Interlocked.Add(localUncompressedBytes, details.UncompressedSize)
39
+ End If
40
+ End Sub )
41
+
42
+ ' Update the shared state after the parallel loop to minimize contention
43
+ CompressedBytes = localCompressedBytes
44
+ UncompressedBytes = localUncompressedBytes
27
45
Catch ex As OperationCanceledException
28
46
Debug.WriteLine(ex.Message)
29
47
Return Nothing
30
48
End Try
31
49
32
- ContainsCompressedFiles = compressedFilesCount <> 0
50
+ Debug.WriteLine(CompressedBytes)
51
+ Debug.WriteLine(UncompressedBytes)
52
+
53
+
54
+ ContainsCompressedFiles = compressedFilesCount > 0
33
55
FileCompressionDetailsList = fileDetails.ToList
34
56
Return ContainsCompressedFiles
35
-
36
57
End Function
37
58
38
59
39
- Private Sub AnalyseFile(file As String , ByRef compressedFilesCount As Integer , ByRef fileDetails As Concurrent.ConcurrentBag( Of AnalysedFileDetails))
40
-
60
+ Private Function AnalyseFile(file As String ) As AnalysedFileDetails
41
61
Try
42
62
Dim fInfo As New FileInfo(file)
43
63
Dim unCompSize = fInfo.Length
44
64
Dim compSize = GetFileSizeOnDisk(file)
45
65
If compSize < 0 Then
46
- 'GetFileSizeOnDisk failed, fall back to unCompSize
47
- compSize = unCompSize
66
+ compSize = unCompSize ' GetFileSizeOnDisk failed, fall back to unCompSize
48
67
End If
49
68
Dim cLevel As CompressionAlgorithm = If (compSize = unCompSize, CompressionAlgorithm.NO_COMPRESSION, DetectCompression(fInfo))
50
69
51
- 'Sets the backing private fields directly because Interlocked doesn't play nice with properties!
52
- Interlocked.Add(_CompressedBytes, compSize)
53
- Interlocked.Add(_UncompressedBytes, unCompSize)
54
- Interlocked.Add(_testField, 1 )
55
- fileDetails.Add( New AnalysedFileDetails With {.FileName = file, .CompressedSize = compSize, .UncompressedSize = unCompSize, .CompressionMode = cLevel})
56
- If cLevel <> CompressionAlgorithm.NO_COMPRESSION Then Interlocked.Increment(compressedFilesCount)
70
+ Return New AnalysedFileDetails With {.FileName = file, .CompressedSize = compSize, .UncompressedSize = unCompSize, .CompressionMode = cLevel}
57
71
Catch ex As IOException
58
72
Debug.WriteLine( $"Error analysing file {file}: {ex.Message}" )
73
+ Return Nothing
59
74
End Try
75
+ End Function
76
+
60
77
61
- End Sub
62
78
Public Async Function GetPoorlyCompressedExtensions() As Task( Of List( Of ExtensionResult))
63
79
Dim extClassResults As New List( Of ExtensionResult)
64
80
Await Task.Run(
@@ -70,22 +86,22 @@ Public Class Analyser
70
86
If fl.UncompressedSize = 0 Then Return
71
87
72
88
extRes.AddOrUpdate(xt,
73
- Function (addKey) ' Add value factory
74
- Return New ExtensionResult With {
89
+ Function (addKey) ' Add value factory
90
+ Return New ExtensionResult With {
75
91
.extension = xt,
76
92
.totalFiles = 1 ,
77
93
.uncompressedBytes = fl.UncompressedSize,
78
94
.compressedBytes = fl.CompressedSize
79
95
}
80
- End Function ,
81
- Function (updateKey, oldValue) ' Update value factory
82
- Return New ExtensionResult With {
96
+ End Function ,
97
+ Function (updateKey, oldValue) ' Update value factory
98
+ Return New ExtensionResult With {
83
99
.extension = xt,
84
100
.totalFiles = oldValue.totalFiles + 1 ,
85
101
.uncompressedBytes = oldValue.uncompressedBytes + fl.UncompressedSize,
86
102
.compressedBytes = oldValue.compressedBytes + fl.CompressedSize
87
103
}
88
- End Function )
104
+ End Function )
89
105
End Sub )
90
106
91
107
' Filter and convert to list after aggregation
@@ -95,6 +111,7 @@ Public Class Analyser
95
111
Return extClassResults
96
112
End Function
97
113
114
+
98
115
Private Function DetectCompression(fInfo As FileInfo) As CompressionAlgorithm
99
116
100
117
Dim isextFile As Integer
@@ -110,45 +127,6 @@ Public Class Analyser
110
127
End Function
111
128
112
129
113
-
114
- 'Public Function HasDirectoryWritePermission() As Boolean
115
-
116
- ' Try
117
- ' Dim ACRules = New DirectoryInfo(FolderName).GetAccessControl().GetAccessRules(True, True, GetType(Security.Principal.SecurityIdentifier))
118
-
119
- ' Dim identity = Security.Principal.WindowsIdentity.GetCurrent
120
- ' Dim principal = New Security.Principal.WindowsPrincipal(identity)
121
- ' Dim writeDenied = False
122
-
123
- ' For Each FSRule As FileSystemAccessRule In ACRules
124
- ' If (FSRule.FileSystemRights And FileSystemRights.Write) = 0 Then Continue For
125
-
126
-
127
- ' ' Use Translate to safely convert to NTAccount
128
- ' Dim ntAccount As Security.Principal.NTAccount = Nothing
129
- ' Try
130
- ' ntAccount = DirectCast(FSRule.IdentityReference.Translate(GetType(Security.Principal.NTAccount)), System.Security.Principal.NTAccount)
131
- ' Catch ex As Exception
132
- ' Continue For
133
- ' End Try
134
-
135
- ' If ntAccount Is Nothing OrElse Not principal.IsInRole(ntAccount.Value) Then Continue For
136
-
137
- ' If FSRule.AccessControlType = AccessControlType.Deny Then
138
- ' writeDenied = True
139
- ' Exit For
140
- ' End If
141
-
142
- ' Next
143
-
144
- ' Return Not writeDenied
145
- ' Catch ex As UnauthorizedAccessException
146
- '
147
- ' Return False
148
- ' End Try
149
-
150
- 'End Function
151
-
152
130
Public Function HasDirectoryWritePermission() As Boolean
153
131
Try
154
132
Dim directoryInfo = New DirectoryInfo(FolderName)
0 commit comments