in Sources/TSCBasic/Path.swift [548:616]
init(normalizingAbsolutePath path: String) {
#if os(Windows)
var buffer: [WCHAR] = Array<WCHAR>(repeating: 0, count: Int(MAX_PATH + 1))
_ = path.withCString(encodedAs: UTF16.self) {
PathCanonicalizeW(&buffer, $0)
}
self.init(string: String(decodingCString: buffer, as: UTF16.self))
#else
precondition(path.first == "/", "Failure normalizing \(path), absolute paths should start with '/'")
// At this point we expect to have a path separator as first character.
assert(path.first == "/")
// Fast path.
if !mayNeedNormalization(absolute: path) {
self.init(string: path)
}
// Split the character array into parts, folding components as we go.
// As we do so, we count the number of characters we'll end up with in
// the normalized string representation.
var parts: [String] = []
var capacity = 0
for part in path.split(separator: "/") {
switch part.count {
case 0:
// Ignore empty path components.
continue
case 1 where part.first == ".":
// Ignore `.` path components.
continue
case 2 where part.first == "." && part.last == ".":
// If there's a previous part, drop it; otherwise, do nothing.
if let prev = parts.last {
parts.removeLast()
capacity -= prev.count
}
default:
// Any other component gets appended.
parts.append(String(part))
capacity += part.count
}
}
capacity += max(parts.count, 1)
// Create an output buffer using the capacity we've calculated.
// FIXME: Determine the most efficient way to reassemble a string.
var result = ""
result.reserveCapacity(capacity)
// Put the normalized parts back together again.
var iter = parts.makeIterator()
result.append("/")
if let first = iter.next() {
result.append(contentsOf: first)
while let next = iter.next() {
result.append("/")
result.append(contentsOf: next)
}
}
// Sanity-check the result (including the capacity we reserved).
assert(!result.isEmpty, "unexpected empty string")
assert(result.count == capacity, "count: " +
"\(result.count), cap: \(capacity)")
// Use the result as our stored string.
self.init(string: result)
#endif
}