Skip to content

Commit d28e9e3

Browse files
authored
Merge pull request #82 from dmbryson/llb
Sink some support code from llbuild2
2 parents 81e2cda + 82e94e5 commit d28e9e3

File tree

6 files changed

+328
-1
lines changed

6 files changed

+328
-1
lines changed

Diff for: Sources/TSCBasic/FileSystem.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public enum FileSystemError: Swift.Error {
6666
case alreadyExistsAtDestination
6767
}
6868

69-
extension FileSystemError {
69+
public extension FileSystemError {
7070
init(errno: Int32) {
7171
switch errno {
7272
case TSCLibc.EACCES:
@@ -899,3 +899,16 @@ extension FileSystem {
899899
}
900900
}
901901
}
902+
903+
extension dirent {
904+
/// Get the directory name.
905+
///
906+
/// This returns nil if the name is not valid UTF8.
907+
public var name: String? {
908+
var d_name = self.d_name
909+
return withUnsafePointer(to: &d_name) {
910+
String(validatingUTF8: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self))
911+
}
912+
}
913+
}
914+

Diff for: Sources/TSCUtility/Array+Extensions.swift

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
10+
extension Array {
11+
/// Make several slices out of a given array.
12+
/// - Returns:
13+
/// An array of slices of `maxStride` elements each.
14+
@inlinable
15+
public func tsc_sliceBy(maxStride: Int) -> [ArraySlice<Element>] {
16+
let elementsCount = self.count
17+
let groupsCount = (elementsCount + maxStride - 1) / maxStride
18+
return (0..<groupsCount).map({ n in
19+
self[n*maxStride..<Swift.min(elementsCount, (n+1)*maxStride)]
20+
})
21+
}
22+
}

Diff for: Sources/TSCUtility/Hex.swift

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
import Foundation
10+
11+
12+
@usableFromInline
13+
internal func char(forNibble value: UInt8) -> CChar {
14+
switch value {
15+
case 0 ..< 10:
16+
return CChar(UInt8(ascii: "0") + value)
17+
default:
18+
precondition(value < 16)
19+
return CChar(UInt8(ascii: "a") + value - 10)
20+
}
21+
}
22+
23+
@usableFromInline
24+
internal func nibble(forHexChar char: UInt8) -> UInt8? {
25+
switch char {
26+
case UInt8(ascii: "0")...UInt8(ascii: "9"):
27+
return char - UInt8(ascii: "0")
28+
case UInt8(ascii: "a")...UInt8(ascii: "f"):
29+
return 10 + char - UInt8(ascii: "a")
30+
case UInt8(ascii: "A")...UInt8(ascii: "F"):
31+
return 10 + char - UInt8(ascii: "a")
32+
default:
33+
return nil
34+
}
35+
}
36+
37+
@inlinable
38+
public func hexEncode<T: Collection>(_ bytes: T) -> [CChar] where T.Element == UInt8, T.Index == Int {
39+
var output = [CChar](repeating: 0, count: Int(bytes.count) * 2)
40+
for (i, byte) in bytes.enumerated() {
41+
output[i*2 + 0] = char(forNibble: (byte >> 4) & 0xF)
42+
output[i*2 + 1] = char(forNibble: (byte >> 0) & 0xF)
43+
}
44+
return output
45+
}
46+
47+
@inlinable
48+
public func hexEncode<T: Collection>(_ bytes: T) -> String where T.Element == UInt8, T.Index == Int {
49+
let chars = hexEncode(bytes) as [CChar]
50+
return String(tsc_fromUTF8: chars.map{ UInt8($0) })
51+
}
52+
53+
extension String {
54+
/// Decode the string as a sequence of hex bytes (with no leading 0x prefix).
55+
@inlinable
56+
public func tsc_hexDecode() -> [UInt8]? {
57+
let utf8 = self.utf8
58+
let count = utf8.count
59+
let byteCount = count / 2
60+
if count != byteCount * 2 { return nil }
61+
62+
var result = [UInt8](repeating: 0, count: byteCount)
63+
var seq = utf8.makeIterator()
64+
for i in 0 ..< byteCount {
65+
guard let hi = nibble(forHexChar: seq.next()!) else { return nil }
66+
guard let lo = nibble(forHexChar: seq.next()!) else { return nil }
67+
result[i] = (hi << 4) | lo
68+
}
69+
return result
70+
}
71+
}

Diff for: Sources/TSCUtility/OrderedZip.swift

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
10+
/// Combine two sequences which are already ordered with respect to
11+
/// `compare` into a single ordered sequence with the items from each
12+
/// sequence, in order.
13+
///
14+
/// - Parameters:
15+
/// - lhs: The left-hand side sequence.
16+
/// - rhs: The right-hand side sequence.
17+
/// - areInIncreasingOrder: A predicate that returns true if its first
18+
/// argument should be ordered before its second argument; otherwise,
19+
/// false. Each sequence *MUST* already be in order with respect to this
20+
/// predicate (this is checked, in debug builds).
21+
/// - Returns: A list of pairs, ordered with respect to `compare`. The first
22+
/// element in the pair will come from the LHS sequence, and the second will
23+
/// come from the RHS sequence, and equal elements appear in both lists will be
24+
/// returned together.
25+
@inlinable
26+
public func orderedZip<S: Sequence>(
27+
_ lhs: S,
28+
_ rhs: S,
29+
by areInIncreasingOrder: (S.Element, S.Element) -> Bool
30+
) -> [(S.Element?, S.Element?)] {
31+
var result: [(S.Element?, S.Element?)] = []
32+
result.reserveCapacity(max(lhs.underestimatedCount, rhs.underestimatedCount))
33+
34+
// Initialize.
35+
var lhsIt = lhs.makeIterator()
36+
var rhsIt = rhs.makeIterator()
37+
var lhsItem = lhsIt.next()
38+
var rhsItem = rhsIt.next()
39+
40+
// While each list has items...
41+
while let a = lhsItem, let b = rhsItem {
42+
// If a < b, take a.
43+
if areInIncreasingOrder(a, b) {
44+
result.append((a, nil))
45+
lhsItem = lhsIt.next()
46+
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
47+
continue
48+
}
49+
50+
// If b < a, take b.
51+
if areInIncreasingOrder(b, a) {
52+
result.append((nil, b))
53+
rhsItem = rhsIt.next()
54+
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
55+
continue
56+
}
57+
58+
// Otherwise, a == b, take them both.
59+
result.append((a, b))
60+
lhsItem = lhsIt.next()
61+
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
62+
rhsItem = rhsIt.next()
63+
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
64+
}
65+
66+
// Add an remaining items from either list (only one of these can actually be true).
67+
while let a = lhsItem {
68+
result.append((a, nil))
69+
lhsItem = lhsIt.next()
70+
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
71+
}
72+
while let b = rhsItem {
73+
result.append((nil, b))
74+
rhsItem = rhsIt.next()
75+
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
76+
}
77+
78+
return result
79+
}
80+
81+
/// Combine a list of sequences which are already ordered with respect to
82+
/// `compare` into a single ordered sequence with the items from each sequence,
83+
/// in order.
84+
///
85+
/// - Parameters:
86+
/// - sequences: The list of sequences.
87+
/// - areInIncreasingOrder: A predicate that returns true if its first
88+
/// argument should be ordered before its second argument; otherwise,
89+
/// false. Each sequence *MUST* already be in order with respect to this
90+
/// predicate (this is checked, in debug builds).
91+
/// - Returns: A sequence of arrays, ordered with respect to `compare`. Each row
92+
/// in the result will have exactly `sequences.count` entries, and each Nth item
93+
/// either be nil or the equivalently ordered item from the Nth sequence.
94+
@inlinable
95+
public func orderedZip<S: Sequence>(
96+
sequences: [S],
97+
by areInIncreasingOrder: (S.Element, S.Element) -> Bool
98+
) -> [[S.Element?]] {
99+
var result: [[S.Element?]] = []
100+
result.reserveCapacity(sequences.map{ $0.underestimatedCount }.max() ?? 0)
101+
102+
// Initialize iterators.
103+
var iterators = sequences.map{ $0.makeIterator() }
104+
105+
// We keep a "current" item for each iterator.
106+
//
107+
// This strategy is not particularly efficient if we have many many
108+
// sequences with highly varied lengths, but is simple.
109+
var items: [S.Element?] = []
110+
for i in 0 ..< iterators.count {
111+
items.append(iterators[i].next())
112+
}
113+
114+
// Iterate...
115+
while true {
116+
// Find the smallest item.
117+
var maybeSmallest: S.Element?
118+
var smallestIndex: Int = -1
119+
for (i, itemOpt) in items.enumerated() {
120+
if let item = itemOpt, maybeSmallest == nil || areInIncreasingOrder(item, maybeSmallest!) {
121+
maybeSmallest = item
122+
smallestIndex = i
123+
}
124+
}
125+
126+
// If there was no smallest, we have reached the end of all lists.
127+
guard let smallest = maybeSmallest else {
128+
// We are done.
129+
break
130+
}
131+
132+
// Now, we have to take all items equivalent to the smallest. Since we
133+
// have already taken the smallest from each list, we can find this by
134+
// reversing the equivalence (assuming a proper predicate). We do this
135+
// in lieu of tracking equivalence while we iterate through the items.
136+
var row: [S.Element?] = []
137+
for (i, itemOpt) in items.enumerated() {
138+
// If this is the smallest item, or is not greater than the smallest
139+
// (and thus is equal), it should go in the list.
140+
if let item = itemOpt, i == smallestIndex || !areInIncreasingOrder(smallest, item) {
141+
// Walk the item (and validate individual sequence ordering, in debug mode).
142+
let next = iterators[i].next()
143+
assert(next == nil || !areInIncreasingOrder(next!, item))
144+
items[i] = next
145+
row.append(item)
146+
} else {
147+
// Otherwise, no entry for this sequence.
148+
row.append(nil)
149+
}
150+
}
151+
result.append(row)
152+
}
153+
154+
return result
155+
}

Diff for: Sources/TSCUtility/StringExtensions.swift

+35
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
import Foundation
12+
13+
1114
extension String {
1215
/**
1316
Remove trailing newline characters. By default chomp removes
@@ -102,4 +105,36 @@ extension String {
102105
.map { indent + $0 }
103106
.joined(separator: "\n")
104107
}
108+
109+
@inlinable
110+
public init(tsc_fromUTF8 bytes: Array<UInt8>) {
111+
if let string = bytes.withContiguousStorageIfAvailable({ bptr in
112+
String(decoding: bptr, as: UTF8.self)
113+
}) {
114+
self = string
115+
} else {
116+
self = bytes.withUnsafeBufferPointer { ubp in
117+
String(decoding: ubp, as: UTF8.self)
118+
}
119+
}
120+
}
121+
122+
@inlinable
123+
public init(tsc_fromUTF8 bytes: ArraySlice<UInt8>) {
124+
if let string = bytes.withContiguousStorageIfAvailable({ bptr in
125+
String(decoding: bptr, as: UTF8.self)
126+
}) {
127+
self = string
128+
} else {
129+
self = bytes.withUnsafeBufferPointer { ubp in
130+
String(decoding: ubp, as: UTF8.self)
131+
}
132+
}
133+
}
134+
135+
@inlinable
136+
public init(tsc_fromUTF8 bytes: Data) {
137+
self = String(decoding: bytes, as: UTF8.self)
138+
}
139+
105140
}

Diff for: Tests/TSCUtilityTests/HexTests.swift

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// This source file is part of the Swift.org open source project
2+
//
3+
// Copyright (c) 2020 Apple Inc. and the Swift project authors
4+
// Licensed under Apache License v2.0 with Runtime Library Exception
5+
//
6+
// See http://swift.org/LICENSE.txt for license information
7+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
8+
9+
import XCTest
10+
11+
import TSCUtility
12+
13+
class HexTests: XCTestCase {
14+
func testHexDecode() {
15+
XCTAssert("0".tsc_hexDecode() == nil)
16+
XCTAssert("0x".tsc_hexDecode() == nil)
17+
XCTAssertEqual("00".tsc_hexDecode()!, [0])
18+
XCTAssertEqual("01".tsc_hexDecode()!, [1])
19+
XCTAssertEqual("0a".tsc_hexDecode()!, [10])
20+
XCTAssertEqual("10".tsc_hexDecode()!, [16])
21+
XCTAssertEqual("a0".tsc_hexDecode()!, [160])
22+
}
23+
24+
func testHexEncode() {
25+
XCTAssertEqual(hexEncode([0]), "00")
26+
XCTAssertEqual(hexEncode([1]), "01")
27+
XCTAssertEqual(hexEncode([10]), "0a")
28+
XCTAssertEqual(hexEncode([16]), "10")
29+
XCTAssertEqual(hexEncode([160]), "a0")
30+
}
31+
}

0 commit comments

Comments
 (0)