@@ -7,6 +7,8 @@ import Foundation
77
88typealias CMUpdateClosure = ( _ result: [ CompileMeasure ] , _ didComplete: Bool , _ didCancel: Bool ) -> ( )
99
10+ fileprivate let regex = try ! NSRegularExpression ( pattern: " ^ \\ d* \\ .? \\ d*ms \\ t/ " , options: [ ] )
11+
1012protocol LogProcessorProtocol : class {
1113 var rawMeasures : [ String : RawMeasure ] { get set }
1214 var updateHandler : CMUpdateClosure ? { get set }
@@ -16,86 +18,117 @@ protocol LogProcessorProtocol: class {
1618 func processingDidFinish( )
1719}
1820
19- extension LogProcessorProtocol {
21+ class LogProcessor : NSObject , LogProcessorProtocol {
22+
23+ var rawMeasures : [ String : RawMeasure ] = [ : ]
24+ var updateHandler : CMUpdateClosure ?
25+ var shouldCancel = false
26+ var timer : Timer ?
27+
28+ func processingDidStart( ) {
29+ DispatchQueue . main. async {
30+ self . timer = Timer . scheduledTimer ( timeInterval: 1.5 , target: self , selector: #selector( self . timerCallback ( _: ) ) , userInfo: nil , repeats: true )
31+ }
32+ }
33+
34+ func processingDidFinish( ) {
35+ DispatchQueue . main. async {
36+ self . timer? . invalidate ( )
37+ self . timer = nil
38+ let didCancel = self . shouldCancel
39+ self . shouldCancel = false
40+ self . updateResults ( didComplete: true , didCancel: didCancel)
41+ }
42+ }
43+
44+ @objc func timerCallback( _ timer: Timer ) {
45+ updateResults ( didComplete: false , didCancel: false )
46+ }
47+
2048 func processDatabase( database: XcodeDatabase , updateHandler: CMUpdateClosure ? ) {
2149 guard let text = database. processLog ( ) else {
2250 updateHandler ? ( [ ] , true , false )
2351 return
2452 }
25-
53+
2654 self . updateHandler = updateHandler
27- DispatchQueue . global ( ) . async {
55+ DispatchQueue . global ( qos : . background ) . async {
2856 self . process ( text: text)
2957 }
3058 }
31-
59+
3260 // MARK: Private methods
33-
61+
3462 private func process( text: String ) {
35- let characterSet = CharacterSet ( charactersIn: " \r \" " )
63+ let characterSet = CharacterSet ( charactersIn: " \r " )
3664 var remainingRange = text. startIndex..< text. endIndex
37- let regex = try ! NSRegularExpression ( pattern: " ^ \\ d* \\ .? \\ d*ms \\ t/ " , options: [ ] )
38-
65+
3966 rawMeasures. removeAll ( )
40-
67+
4168 processingDidStart ( )
42-
43- while let nextRange = text. rangeOfCharacter ( from: characterSet, options: [ ] , range: remainingRange) {
44- let text = String ( text [ remainingRange. lowerBound..< nextRange. upperBound] )
45-
69+
70+ while !shouldCancel, let characterRange = text. rangeOfCharacter ( from: characterSet,
71+ options: . literal,
72+ range: remainingRange) {
73+ let nextRange = remainingRange. lowerBound..< characterRange. upperBound
74+
4675 defer {
4776 remainingRange = nextRange. upperBound..< remainingRange. upperBound
4877 }
49-
50- // From LuizZak: (text as NSString).length improves the performance by about 2x compared to text.characters.count
51- let range = NSMakeRange ( 0 , ( text as NSString ) . length)
78+
79+ let range = NSRange ( nextRange, in: text)
5280 guard let match = regex. firstMatch ( in: text, options: [ ] , range: range) else { continue }
53-
54- let timeString = text [ .. < text. index ( text . startIndex , offsetBy: match . range . length - 4 ) ]
81+ let matchRange = Range < String . Index > . init ( match . range , in : text ) !
82+ let timeString = text [ remainingRange . lowerBound .. < text. index ( matchRange . upperBound , offsetBy: - 4 ) ]
5583 if let time = Double ( timeString) {
56- let value = String ( text [ text. index ( text . startIndex , offsetBy : match . range . length - 1 ) ... ] )
57- if var rawMeasure = rawMeasures [ value] {
84+ let value = String ( text [ text. index ( before : matchRange . upperBound ) ..< nextRange . upperBound ] )
85+ if let rawMeasure = rawMeasures [ value] {
5886 rawMeasure. time += time
5987 rawMeasure. references += 1
60- rawMeasures [ value] = rawMeasure
6188 } else {
6289 rawMeasures [ value] = RawMeasure ( time: time, text: value)
6390 }
6491 }
65- guard !shouldCancel else { break }
6692 }
6793 processingDidFinish ( )
6894 }
69-
95+
7096 fileprivate func updateResults( didComplete completed: Bool , didCancel: Bool ) {
71- var filteredResults = rawMeasures. values. filter { $0. time > 10 }
72- if filteredResults. count < 20 {
73- filteredResults = rawMeasures. values. filter { $0. time > 0.1 }
74- }
75-
76- let sortedResults = filteredResults. sorted ( by: { $0. time > $1. time } )
77- updateHandler ? ( processResult ( sortedResults) , completed, didCancel)
78-
79- if completed {
80- rawMeasures. removeAll ( )
97+ DispatchQueue . global ( qos: . userInteractive) . async {
98+ let measures = self . rawMeasures. values
99+ var filteredResults = measures. filter { $0. time > 10 }
100+ if filteredResults. count < 20 {
101+ filteredResults = measures. filter { $0. time > 0.1 }
102+ }
103+
104+ let sortedResults = filteredResults. sorted ( by: { $0. time > $1. time } )
105+ let result = self . processResult ( sortedResults)
106+
107+ if completed {
108+ self . rawMeasures. removeAll ( )
109+ }
110+
111+ DispatchQueue . main. async {
112+ self . updateHandler ? ( result, completed, didCancel)
113+ }
81114 }
82115 }
83-
116+
84117 private func processResult( _ unprocessedResult: [ RawMeasure ] ) -> [ CompileMeasure ] {
85118 let characterSet = CharacterSet ( charactersIn: " \r \" " )
86-
119+
87120 var result : [ CompileMeasure ] = [ ]
88121 for entry in unprocessedResult {
89122 let code = entry. text. split ( separator: " \t " ) . map ( String . init)
90123 let method = code. count >= 2 ? trimPrefixes ( code [ 1 ] ) : " - "
91-
124+
92125 if let path = code. first? . trimmingCharacters ( in: characterSet) , let measure = CompileMeasure ( time: entry. time, rawPath: path, code: method, references: entry. references) {
93126 result. append ( measure)
94127 }
95128 }
96129 return result
97130 }
98-
131+
99132 private func trimPrefixes( _ code: String ) -> String {
100133 var code = code
101134 [ " @objc " , " final " , " @IBAction " ] . forEach { ( prefix) in
@@ -106,31 +139,3 @@ extension LogProcessorProtocol {
106139 return code
107140 }
108141}
109-
110- class LogProcessor : NSObject , LogProcessorProtocol {
111-
112- var rawMeasures : [ String : RawMeasure ] = [ : ]
113- var updateHandler : CMUpdateClosure ?
114- var shouldCancel = false
115- var timer : Timer ?
116-
117- func processingDidStart( ) {
118- DispatchQueue . main. async {
119- self . timer = Timer . scheduledTimer ( timeInterval: 1.5 , target: self , selector: #selector( self . timerCallback ( _: ) ) , userInfo: nil , repeats: true )
120- }
121- }
122-
123- func processingDidFinish( ) {
124- DispatchQueue . main. async {
125- self . timer? . invalidate ( )
126- self . timer = nil
127- let didCancel = self . shouldCancel
128- self . shouldCancel = false
129- self . updateResults ( didComplete: true , didCancel: didCancel)
130- }
131- }
132-
133- @objc func timerCallback( _ timer: Timer ) {
134- updateResults ( didComplete: false , didCancel: false )
135- }
136- }
0 commit comments