@@ -12,35 +12,8 @@ import Foundation
1212import TSCBasic
1313
1414extension Bitcode {
15- /// Parse a bitstream from data.
16- @available ( * , deprecated, message: " Use Bitcode.init(bytes:) instead " )
17- public init ( data: Data ) throws {
18- precondition ( data. count > 4 )
19- try self . init ( bytes: ByteString ( data) )
20- }
21-
22- public init ( bytes: ByteString ) throws {
23- precondition ( bytes. count > 4 )
24- var reader = BitstreamReader ( buffer: bytes)
25- let signature = try reader. readSignature ( )
26- var visitor = CollectingVisitor ( )
27- try reader. readBlock ( id: BitstreamReader . fakeTopLevelBlockID,
28- abbrevWidth: 2 ,
29- abbrevInfo: [ ] ,
30- visitor: & visitor)
31- self . init ( signature: signature,
32- elements: visitor. finalizeTopLevelElements ( ) ,
33- blockInfo: reader. blockInfo)
34- }
35-
3615 /// Traverse a bitstream using the specified `visitor`, which will receive
3716 /// callbacks when blocks and records are encountered.
38- @available ( * , deprecated, message: " Use Bitcode.read(bytes:using:) instead " )
39- public static func read< Visitor: BitstreamVisitor > ( stream data: Data , using visitor: inout Visitor ) throws {
40- precondition ( data. count > 4 )
41- try Self . read ( bytes: ByteString ( data) , using: & visitor)
42- }
43-
4417 public static func read< Visitor: BitstreamVisitor > ( bytes: ByteString , using visitor: inout Visitor ) throws {
4518 precondition ( bytes. count > 4 )
4619 var reader = BitstreamReader ( buffer: bytes)
@@ -52,36 +25,6 @@ extension Bitcode {
5225 }
5326}
5427
55- /// A basic visitor that collects all the blocks and records in a stream.
56- private struct CollectingVisitor : BitstreamVisitor {
57- var stack : [ ( UInt64 , [ BitcodeElement ] ) ] = [ ( BitstreamReader . fakeTopLevelBlockID, [ ] ) ]
58-
59- func validate( signature: Bitcode . Signature ) throws { }
60-
61- mutating func shouldEnterBlock( id: UInt64 ) throws -> Bool {
62- stack. append ( ( id, [ ] ) )
63- return true
64- }
65-
66- mutating func didExitBlock( ) throws {
67- guard let ( id, elements) = stack. popLast ( ) else {
68- fatalError ( " Unbalanced calls to shouldEnterBlock/didExitBlock " )
69- }
70-
71- let block = BitcodeElement . Block ( id: id, elements: elements)
72- stack [ stack. endIndex- 1 ] . 1 . append ( . block( block) )
73- }
74-
75- mutating func visit( record: BitcodeElement . Record ) throws {
76- stack [ stack. endIndex- 1 ] . 1 . append ( . record( record) )
77- }
78-
79- func finalizeTopLevelElements( ) -> [ BitcodeElement ] {
80- assert ( stack. count == 1 )
81- return stack [ 0 ] . 1
82- }
83- }
84-
8528private extension Bits . Cursor {
8629 enum BitcodeError : Swift . Error {
8730 case vbrOverflow
@@ -161,6 +104,7 @@ private struct BitstreamReader {
161104 guard numOps > 0 else { throw Error . invalidAbbrev }
162105
163106 var operands : [ Bitstream . Abbreviation . Operand ] = [ ]
107+ operands. reserveCapacity ( numOps)
164108 for i in 0 ..< numOps {
165109 operands. append ( try readAbbrevOp ( ) )
166110
@@ -204,15 +148,29 @@ private struct BitstreamReader {
204148 }
205149 }
206150
207- mutating func readAbbreviatedRecord( _ abbrev: Bitstream . Abbreviation ) throws -> BitcodeElement . Record {
151+ /// Computes a non-owning view of a `BitcodeElement.Record` that is valid for
152+ /// the lifetime of the call to `body`.
153+ ///
154+ /// - Warning: If this function throws, the `body` block will not be called.
155+ mutating func withAbbreviatedRecord(
156+ _ abbrev: Bitstream . Abbreviation ,
157+ body: ( BitcodeElement . Record ) throws -> Void
158+ ) throws {
208159 let code = try readSingleAbbreviatedRecordOperand ( abbrev. operands. first!)
209160
210161 let lastOperand = abbrev. operands. last!
211162 let lastRegularOperandIndex : Int = abbrev. operands. endIndex - ( lastOperand. isPayload ? 1 : 0 )
212163
213- var fields = [ UInt64] ( )
214- for op in abbrev. operands [ 1 ..< lastRegularOperandIndex] {
215- fields. append ( try readSingleAbbreviatedRecordOperand ( op) )
164+ // Safety: `lastRegularOperandIndex` is always at least 1. An abbreviation
165+ // is required by the format to contain at least one operand. If that last
166+ // operand is a payload (and thus we subtracted one from the total number of
167+ // operands above), then that must mean it is either a trailing array
168+ // or trailing blob. Both of these are preceded by their length field.
169+ let fields = UnsafeMutableBufferPointer< UInt64> . allocate( capacity: lastRegularOperandIndex - 1 )
170+ defer { fields. deallocate ( ) }
171+
172+ for (idx, op) in abbrev. operands [ 1 ..< lastRegularOperandIndex] . enumerated ( ) {
173+ fields [ idx] = try readSingleAbbreviatedRecordOperand ( op)
216174 }
217175
218176 let payload : BitcodeElement . Record . Payload
@@ -222,26 +180,42 @@ private struct BitstreamReader {
222180 switch lastOperand {
223181 case . array( let element) :
224182 let length = try cursor. readVBR ( 6 )
225- var elements = [ UInt64] ( )
226- for _ in 0 ..< length {
227- elements. append ( try readSingleAbbreviatedRecordOperand ( element) )
228- }
229183 if case . char6 = element {
230- payload = . char6String( String ( String . UnicodeScalarView ( elements. map { UnicodeScalar ( UInt8 ( $0) ) } ) ) )
184+ // FIXME: Once the minimum deployment target bumps to macOS 11, use
185+ // the more ergonomic stdlib API everywhere.
186+ if #available( macOS 11 . 0 , * ) {
187+ payload = try . char6String( String ( unsafeUninitializedCapacity: Int ( length) ) { buffer in
188+ for i in 0 ..< Int ( length) {
189+ buffer [ i] = try UInt8 ( readSingleAbbreviatedRecordOperand ( element) )
190+ }
191+ return Int ( length)
192+ } )
193+ } else {
194+ let buffer = UnsafeMutableBufferPointer< UInt8> . allocate( capacity: Int ( length) )
195+ defer { buffer. deallocate ( ) }
196+ for i in 0 ..< Int ( length) {
197+ buffer [ i] = try UInt8 ( readSingleAbbreviatedRecordOperand ( element) )
198+ }
199+ payload = . char6String( String ( decoding: buffer, as: UTF8 . self) )
200+ }
231201 } else {
202+ var elements = [ UInt64] ( )
203+ for _ in 0 ..< length {
204+ elements. append ( try readSingleAbbreviatedRecordOperand ( element) )
205+ }
232206 payload = . array( elements)
233207 }
234208 case . blob:
235209 let length = Int ( try cursor. readVBR ( 6 ) )
236210 try cursor. advance ( toBitAlignment: 32 )
237- payload = . blob( try Data ( cursor. read ( bytes: length) ) )
211+ payload = . blob( try cursor. read ( bytes: length) )
238212 try cursor. advance ( toBitAlignment: 32 )
239213 default :
240214 fatalError ( )
241215 }
242216 }
243217
244- return . init( id: code, fields: fields, payload: payload)
218+ return try body ( . init( id: code, fields: UnsafeBufferPointer ( fields) , payload: payload) )
245219 }
246220
247221 mutating func readBlockInfoBlock( abbrevWidth: Int ) throws {
@@ -341,17 +315,20 @@ private struct BitstreamReader {
341315 case Bitstream . AbbreviationID. unabbreviatedRecord. rawValue:
342316 let code = try cursor. readVBR ( 6 )
343317 let numOps = try cursor. readVBR ( 6 )
344- var operands = [ UInt64] ( )
345- for _ in 0 ..< numOps {
346- operands. append ( try cursor. readVBR ( 6 ) )
318+ let operands = UnsafeMutableBufferPointer< UInt64> . allocate( capacity: Int ( numOps) )
319+ defer { operands. deallocate ( ) }
320+ for i in 0 ..< Int ( numOps) {
321+ operands [ i] = try cursor. readVBR ( 6 )
347322 }
348- try visitor. visit ( record: . init( id: code, fields: operands, payload: . none) )
323+ try visitor. visit ( record: . init( id: code, fields: UnsafeBufferPointer ( operands) , payload: . none) )
349324
350325 case let abbrevID:
351326 guard Int ( abbrevID) - 4 < abbrevInfo. count else {
352327 throw Error . noSuchAbbrev ( blockID: id, abbrevID: Int ( abbrevID) )
353328 }
354- try visitor. visit ( record: try readAbbreviatedRecord ( abbrevInfo [ Int ( abbrevID) - 4 ] ) )
329+ try withAbbreviatedRecord ( abbrevInfo [ Int ( abbrevID) - 4 ] ) { record in
330+ try visitor. visit ( record: record)
331+ }
355332 }
356333 }
357334
0 commit comments