Skip to content

Commit c8c824e

Browse files
authored
Merge pull request #258 from p-x9/feature/fix-string-bom-handling
Fix BOM handling in string table
2 parents 97a600d + a7196f6 commit c8c824e

10 files changed

Lines changed: 241 additions & 70 deletions

Package.resolved

Lines changed: 4 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let package = Package(
2121
)
2222
],
2323
dependencies: [
24-
.package(url: "https://github.com/p-x9/swift-fileio.git", from: "0.11.0")
24+
.package(url: "https://github.com/p-x9/swift-fileio.git", from: "0.13.0")
2525
],
2626
targets: [
2727
.target(

Sources/MachOKit/Extension/_FileIOProtocol+.swift

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,54 @@ extension _FileIOProtocol {
277277
}
278278
#endif
279279

280+
extension _FileIOProtocol {
281+
@_disfavoredOverload
282+
@inline(__always)
283+
func _readString<Encoding: _UnicodeEncoding>(
284+
offset: Int,
285+
as encoding: Encoding.Type
286+
) -> (String, Int)? {
287+
if let fileHandle = self as? (any _MemoryMappedFileIOProtocol) {
288+
return UnsafeRawPointer(fileHandle.ptr)
289+
.advanced(by: offset)
290+
.assumingMemoryBound(to: Encoding.CodeUnit.self)
291+
.readString(
292+
as: Encoding.self
293+
)
294+
} else {
295+
var count = 0
296+
var offset: Int = offset
297+
298+
var characters: [Encoding.CodeUnit] = []
299+
300+
while let char = try? read(
301+
offset: offset,
302+
as: Encoding.CodeUnit.self
303+
), char != 0 {
304+
characters.append(char)
305+
count += 1
306+
offset += MemoryLayout<Encoding.CodeUnit>.size
307+
}
308+
309+
characters.append(0)
310+
311+
return characters.withUnsafeBytes { bufferPtr in
312+
guard let baseAddress = bufferPtr.baseAddress else {
313+
return nil
314+
}
315+
let string = String(
316+
decodingCString: baseAddress
317+
.assumingMemoryBound(to: Encoding.CodeUnit.self),
318+
as: Encoding.self
319+
)
320+
let length = (count + 1) * MemoryLayout<Encoding.CodeUnit>.size
321+
return (string, length)
322+
}
323+
}
324+
}
325+
}
326+
327+
280328
extension MemoryMappedFileIOProtocol {
281329
@inline(__always)
282330
func readString(

Sources/MachOKit/MachOFile+CodeSign.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77
//
88

9-
import CoreFoundation
109
import Foundation
1110
#if compiler(>=6.0) || (compiler(>=5.10) && hasFeature(AccessLevelOnImport))
1211
internal import FileIO
@@ -27,7 +26,7 @@ extension MachOFile {
2726
extension MachOFile.CodeSign {
2827
init(fileSlice: FileSlice) {
2928
self.fileSlice = fileSlice
30-
self.isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue
29+
self.isSwapped = Endian.current != .big
3130
}
3231
}
3332

Sources/MachOKit/MachOFile+Strings.swift

Lines changed: 82 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,30 @@ extension MachOFile.UnicodeStrings {
7878
extension MachOFile.UnicodeStrings {
7979
public func string(at offset: Int) -> Element? {
8080
guard 0 <= offset, offset < fileSlice.size else { return nil }
81-
let string = String(
82-
cString: fileSlice.ptr
83-
.advanced(by: offset)
84-
.assumingMemoryBound(to: CChar.self)
81+
82+
guard let (_string, length) = fileSlice._readString(
83+
offset: numericCast(offset),
84+
as: Encoding.self
85+
) else {
86+
return nil
87+
}
88+
var string = _string
89+
90+
let char = try! fileSlice.read(
91+
offset: offset,
92+
as: Encoding.CodeUnit.self
8593
)
94+
95+
if isSwapped || Iterator.shouldSwap(char) {
96+
handleSwap(
97+
string: &string,
98+
at: offset,
99+
length: length,
100+
fileHandle: fileSlice,
101+
hasBOM: Iterator.shouldSwap(char),
102+
encoding: Encoding.self
103+
)
104+
}
86105
return .init(string: string, offset: offset)
87106
}
88107
}
@@ -107,26 +126,30 @@ extension MachOFile.UnicodeStrings {
107126
public mutating func next() -> Element? {
108127
guard nextOffset < tableSize else { return nil }
109128

110-
let ptr = UnsafeRawPointer(fileSlice.ptr)
111-
.advanced(by: nextOffset)
112-
.assumingMemoryBound(to: Encoding.CodeUnit.self)
113-
var (string, offset) = ptr.readString(as: Encoding.self)
129+
guard let (_string, length) = fileSlice._readString(
130+
offset: nextOffset,
131+
as: Encoding.self
132+
) else { return nil }
133+
var string = _string
114134

115135
defer {
116-
nextOffset += offset
136+
nextOffset += length
117137
}
118138

119-
if isSwapped || shouldSwap(ptr) {
120-
let data = Data(bytes: ptr, count: offset)
121-
.byteSwapped(Encoding.CodeUnit.self)
122-
string = data.withUnsafeBytes {
123-
let baseAddress = $0.baseAddress!
124-
.assumingMemoryBound(to: Encoding.CodeUnit.self)
125-
return .init(
126-
decodingCString: baseAddress,
127-
as: Encoding.self
128-
)
129-
}
139+
let char = try! fileSlice.read(
140+
offset: nextOffset,
141+
as: Encoding.CodeUnit.self
142+
)
143+
144+
if isSwapped || Self.shouldSwap(char) {
145+
handleSwap(
146+
string: &string,
147+
at: nextOffset,
148+
length: length,
149+
fileHandle: fileSlice,
150+
hasBOM: Self.shouldSwap(char),
151+
encoding: Encoding.self
152+
)
130153
}
131154

132155
return .init(
@@ -139,17 +162,53 @@ extension MachOFile.UnicodeStrings {
139162

140163
extension MachOFile.UnicodeStrings.Iterator {
141164
// https://github.com/swiftlang/swift-corelibs-foundation/blob/4a9694d396b34fb198f4c6dd865702f7dc0b0dcf/Sources/Foundation/NSString.swift#L1390
142-
func shouldSwap(_ ptr: UnsafePointer<Encoding.CodeUnit>) -> Bool {
165+
static func shouldSwap(
166+
_ char: Encoding.CodeUnit
167+
) -> Bool {
143168
let size = MemoryLayout<Encoding.CodeUnit>.size
169+
var char = char
170+
if Endian.current == .little {
171+
char = char.byteSwapped
172+
}
144173
switch size {
145174
case 1:
146175
return false
147176
case 2:
148-
return ptr.pointee == 0xFFFE /* ZERO WIDTH NO-BREAK SPACE (swapped) */
177+
return char == 0xFFFE /* ZERO WIDTH NO-BREAK SPACE */
149178
case 4:
150-
return ptr.pointee == UInt32(0xFFFE0000) // avoid overflows in 32bit env
179+
return char == UInt32(0xFFFE0000) // avoid overflows in 32bit env
151180
default:
152181
return false
153182
}
154183
}
155184
}
185+
186+
fileprivate func handleSwap<Encoding: _UnicodeEncoding>(
187+
string: inout String,
188+
at offset: Int,
189+
length: Int,
190+
fileHandle: some _FileIOProtocol,
191+
hasBOM: Bool,
192+
encoding: Encoding.Type
193+
) {
194+
var data = try! fileHandle.readData(
195+
offset: offset,
196+
length: length
197+
)
198+
199+
// strip BOM
200+
if hasBOM {
201+
data.removeFirst(MemoryLayout<Encoding.CodeUnit>.size)
202+
}
203+
204+
data = data.byteSwapped(Encoding.CodeUnit.self)
205+
206+
string = data.withUnsafeBytes {
207+
let baseAddress = $0.baseAddress!
208+
.assumingMemoryBound(to: Encoding.CodeUnit.self)
209+
return .init(
210+
decodingCString: baseAddress,
211+
as: Encoding.self
212+
)
213+
}
214+
}

Sources/MachOKit/MachOFile.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77
//
88

9-
import CoreFoundation // for CFByteOrderGetCurrent (Linux)
109
import Foundation
1110
#if compiler(>=6.0) || (compiler(>=5.10) && hasFeature(AccessLevelOnImport))
1211
internal import FileIO
@@ -158,7 +157,7 @@ extension MachOFile {
158157

159158
extension MachOFile {
160159
public var endian: Endian {
161-
let hostIsLittleEndian = CFByteOrderGetCurrent() == CFByteOrderLittleEndian.rawValue
160+
let hostIsLittleEndian = Endian.current == .little
162161
return hostIsLittleEndian
163162
? (isSwapped ? .big : .little)
164163
: (isSwapped ? .little : .big)

Sources/MachOKit/MachOImage+CodeSign.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ extension MachOImage.CodeSign: CodeSignProtocol {
3333
.advanced(by: -numericCast(linkedit.fileoff))
3434
.advanced(by: numericCast(codeSignature.dataoff))
3535
let size: Int = numericCast(codeSignature.datasize)
36-
let isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue
36+
let isSwapped = Endian.current != .big
3737

3838
self.init(
3939
basePointer: start,
@@ -58,7 +58,7 @@ extension MachOImage.CodeSign: CodeSignProtocol {
5858
.advanced(by: numericCast(codeSignature.dataoff))
5959
.assumingMemoryBound(to: UInt8.self)
6060
let size: Int = numericCast(codeSignature.datasize)
61-
let isSwapped = CFByteOrderGetCurrent() != CFByteOrderBigEndian.rawValue
61+
let isSwapped = Endian.current != .big
6262

6363
self.init(
6464
basePointer: start,

0 commit comments

Comments
 (0)