Skip to content

Sink some support code from llbuild2 #82

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion Sources/TSCBasic/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public enum FileSystemError: Swift.Error {
case alreadyExistsAtDestination
}

extension FileSystemError {
public extension FileSystemError {
init(errno: Int32) {
switch errno {
case TSCLibc.EACCES:
Expand Down Expand Up @@ -899,3 +899,16 @@ extension FileSystem {
}
}
}

extension dirent {
/// Get the directory name.
///
/// This returns nil if the name is not valid UTF8.
public var name: String? {
var d_name = self.d_name
return withUnsafePointer(to: &d_name) {
String(validatingUTF8: UnsafeRawPointer($0).assumingMemoryBound(to: CChar.self))
}
}
}

22 changes: 22 additions & 0 deletions Sources/TSCUtility/Array+Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


extension Array {
/// Make several slices out of a given array.
/// - Returns:
/// An array of slices of `maxStride` elements each.
@inlinable
public func tsc_sliceBy(maxStride: Int) -> [ArraySlice<Element>] {
let elementsCount = self.count
let groupsCount = (elementsCount + maxStride - 1) / maxStride
return (0..<groupsCount).map({ n in
self[n*maxStride..<Swift.min(elementsCount, (n+1)*maxStride)]
})
}
}
71 changes: 71 additions & 0 deletions Sources/TSCUtility/Hex.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

import Foundation


@usableFromInline
internal func char(forNibble value: UInt8) -> CChar {
switch value {
case 0 ..< 10:
return CChar(UInt8(ascii: "0") + value)
default:
precondition(value < 16)
return CChar(UInt8(ascii: "a") + value - 10)
}
}

@usableFromInline
internal func nibble(forHexChar char: UInt8) -> UInt8? {
switch char {
case UInt8(ascii: "0")...UInt8(ascii: "9"):
return char - UInt8(ascii: "0")
case UInt8(ascii: "a")...UInt8(ascii: "f"):
return 10 + char - UInt8(ascii: "a")
case UInt8(ascii: "A")...UInt8(ascii: "F"):
return 10 + char - UInt8(ascii: "a")
default:
return nil
}
}

@inlinable
public func hexEncode<T: Collection>(_ bytes: T) -> [CChar] where T.Element == UInt8, T.Index == Int {
var output = [CChar](repeating: 0, count: Int(bytes.count) * 2)
for (i, byte) in bytes.enumerated() {
output[i*2 + 0] = char(forNibble: (byte >> 4) & 0xF)
output[i*2 + 1] = char(forNibble: (byte >> 0) & 0xF)
}
return output
}

@inlinable
public func hexEncode<T: Collection>(_ bytes: T) -> String where T.Element == UInt8, T.Index == Int {
let chars = hexEncode(bytes) as [CChar]
return String(tsc_fromUTF8: chars.map{ UInt8($0) })
}

extension String {
/// Decode the string as a sequence of hex bytes (with no leading 0x prefix).
@inlinable
public func tsc_hexDecode() -> [UInt8]? {
let utf8 = self.utf8
let count = utf8.count
let byteCount = count / 2
if count != byteCount * 2 { return nil }

var result = [UInt8](repeating: 0, count: byteCount)
var seq = utf8.makeIterator()
for i in 0 ..< byteCount {
guard let hi = nibble(forHexChar: seq.next()!) else { return nil }
guard let lo = nibble(forHexChar: seq.next()!) else { return nil }
result[i] = (hi << 4) | lo
}
return result
}
}
155 changes: 155 additions & 0 deletions Sources/TSCUtility/OrderedZip.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors


/// Combine two sequences which are already ordered with respect to
/// `compare` into a single ordered sequence with the items from each
/// sequence, in order.
///
/// - Parameters:
/// - lhs: The left-hand side sequence.
/// - rhs: The right-hand side sequence.
/// - areInIncreasingOrder: A predicate that returns true if its first
/// argument should be ordered before its second argument; otherwise,
/// false. Each sequence *MUST* already be in order with respect to this
/// predicate (this is checked, in debug builds).
/// - Returns: A list of pairs, ordered with respect to `compare`. The first
/// element in the pair will come from the LHS sequence, and the second will
/// come from the RHS sequence, and equal elements appear in both lists will be
/// returned together.
@inlinable
public func orderedZip<S: Sequence>(
_ lhs: S,
_ rhs: S,
by areInIncreasingOrder: (S.Element, S.Element) -> Bool
) -> [(S.Element?, S.Element?)] {
var result: [(S.Element?, S.Element?)] = []
result.reserveCapacity(max(lhs.underestimatedCount, rhs.underestimatedCount))

// Initialize.
var lhsIt = lhs.makeIterator()
var rhsIt = rhs.makeIterator()
var lhsItem = lhsIt.next()
var rhsItem = rhsIt.next()

// While each list has items...
while let a = lhsItem, let b = rhsItem {
// If a < b, take a.
if areInIncreasingOrder(a, b) {
result.append((a, nil))
lhsItem = lhsIt.next()
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
continue
}

// If b < a, take b.
if areInIncreasingOrder(b, a) {
result.append((nil, b))
rhsItem = rhsIt.next()
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
continue
}

// Otherwise, a == b, take them both.
result.append((a, b))
lhsItem = lhsIt.next()
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
rhsItem = rhsIt.next()
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
}

// Add an remaining items from either list (only one of these can actually be true).
while let a = lhsItem {
result.append((a, nil))
lhsItem = lhsIt.next()
assert(lhsItem == nil || !areInIncreasingOrder(lhsItem!, a))
}
while let b = rhsItem {
result.append((nil, b))
rhsItem = rhsIt.next()
assert(rhsItem == nil || !areInIncreasingOrder(rhsItem!, b))
}

return result
}

/// Combine a list of sequences which are already ordered with respect to
/// `compare` into a single ordered sequence with the items from each sequence,
/// in order.
///
/// - Parameters:
/// - sequences: The list of sequences.
/// - areInIncreasingOrder: A predicate that returns true if its first
/// argument should be ordered before its second argument; otherwise,
/// false. Each sequence *MUST* already be in order with respect to this
/// predicate (this is checked, in debug builds).
/// - Returns: A sequence of arrays, ordered with respect to `compare`. Each row
/// in the result will have exactly `sequences.count` entries, and each Nth item
/// either be nil or the equivalently ordered item from the Nth sequence.
@inlinable
public func orderedZip<S: Sequence>(
sequences: [S],
by areInIncreasingOrder: (S.Element, S.Element) -> Bool
) -> [[S.Element?]] {
var result: [[S.Element?]] = []
result.reserveCapacity(sequences.map{ $0.underestimatedCount }.max() ?? 0)

// Initialize iterators.
var iterators = sequences.map{ $0.makeIterator() }

// We keep a "current" item for each iterator.
//
// This strategy is not particularly efficient if we have many many
// sequences with highly varied lengths, but is simple.
var items: [S.Element?] = []
for i in 0 ..< iterators.count {
items.append(iterators[i].next())
}

// Iterate...
while true {
// Find the smallest item.
var maybeSmallest: S.Element?
var smallestIndex: Int = -1
for (i, itemOpt) in items.enumerated() {
if let item = itemOpt, maybeSmallest == nil || areInIncreasingOrder(item, maybeSmallest!) {
maybeSmallest = item
smallestIndex = i
}
}

// If there was no smallest, we have reached the end of all lists.
guard let smallest = maybeSmallest else {
// We are done.
break
}

// Now, we have to take all items equivalent to the smallest. Since we
// have already taken the smallest from each list, we can find this by
// reversing the equivalence (assuming a proper predicate). We do this
// in lieu of tracking equivalence while we iterate through the items.
var row: [S.Element?] = []
for (i, itemOpt) in items.enumerated() {
// If this is the smallest item, or is not greater than the smallest
// (and thus is equal), it should go in the list.
if let item = itemOpt, i == smallestIndex || !areInIncreasingOrder(smallest, item) {
// Walk the item (and validate individual sequence ordering, in debug mode).
let next = iterators[i].next()
assert(next == nil || !areInIncreasingOrder(next!, item))
items[i] = next
row.append(item)
} else {
// Otherwise, no entry for this sequence.
row.append(nil)
}
}
result.append(row)
}

return result
}
35 changes: 35 additions & 0 deletions Sources/TSCUtility/StringExtensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation


extension String {
/**
Remove trailing newline characters. By default chomp removes
Expand Down Expand Up @@ -102,4 +105,36 @@ extension String {
.map { indent + $0 }
.joined(separator: "\n")
}

@inlinable
public init(tsc_fromUTF8 bytes: Array<UInt8>) {
if let string = bytes.withContiguousStorageIfAvailable({ bptr in
String(decoding: bptr, as: UTF8.self)
}) {
self = string
} else {
self = bytes.withUnsafeBufferPointer { ubp in
String(decoding: ubp, as: UTF8.self)
}
}
}

@inlinable
public init(tsc_fromUTF8 bytes: ArraySlice<UInt8>) {
if let string = bytes.withContiguousStorageIfAvailable({ bptr in
String(decoding: bptr, as: UTF8.self)
}) {
self = string
} else {
self = bytes.withUnsafeBufferPointer { ubp in
String(decoding: ubp, as: UTF8.self)
}
}
}

@inlinable
public init(tsc_fromUTF8 bytes: Data) {
self = String(decoding: bytes, as: UTF8.self)
}

}
31 changes: 31 additions & 0 deletions Tests/TSCUtilityTests/HexTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors

import XCTest

import TSCUtility

class HexTests: XCTestCase {
func testHexDecode() {
XCTAssert("0".tsc_hexDecode() == nil)
XCTAssert("0x".tsc_hexDecode() == nil)
XCTAssertEqual("00".tsc_hexDecode()!, [0])
XCTAssertEqual("01".tsc_hexDecode()!, [1])
XCTAssertEqual("0a".tsc_hexDecode()!, [10])
XCTAssertEqual("10".tsc_hexDecode()!, [16])
XCTAssertEqual("a0".tsc_hexDecode()!, [160])
}

func testHexEncode() {
XCTAssertEqual(hexEncode([0]), "00")
XCTAssertEqual(hexEncode([1]), "01")
XCTAssertEqual(hexEncode([10]), "0a")
XCTAssertEqual(hexEncode([16]), "10")
XCTAssertEqual(hexEncode([160]), "a0")
}
}