Skip to content

Commit

Permalink
Remove dependencies on Everything and SIMDSupport.
Browse files Browse the repository at this point in the history
  • Loading branch information
schwa committed May 12, 2024
1 parent a4fd051 commit 0f93896
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,6 @@
"version" : "0.4.4"
}
},
{
"identity" : "simd-support",
"kind" : "remoteSourceControl",
"location" : "https://github.com/schwa/SIMD-Support",
"state" : {
"revision" : "b163c8951dca3089d69f4bcc3febe60d178965ac",
"version" : "0.2.1"
}
},
{
"identity" : "swift-algorithms",
"kind" : "remoteSourceControl",
Expand Down
8 changes: 1 addition & 7 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,10 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/schwa/Everything", from: "0.4.4"),
.package(url: "https://github.com/schwa/SIMD-Support", from: "0.2.1"),
],
targets: [
.target(
name: "SwiftGLTF",
dependencies: [
"Everything",
.product(name: "SIMDSupport", package: "SIMD-Support")
]
name: "SwiftGLTF"
),
.testTarget(
name: "SwiftGLTFTests",
Expand Down
279 changes: 279 additions & 0 deletions Sources/SwiftGLTF/CollectionScanner.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
import Foundation
import os

public struct CollectionScanner<C> where C: Collection {
public typealias Element = C.Element
public typealias SubSequence = C.SubSequence
public typealias Index = C.Index

public let elements: C

public init(elements: C) {
self.elements = elements
current = self.elements.startIndex
}

public var current: Index {
willSet {
assert(current >= elements.startIndex)
assert(current <= elements.endIndex)
}
}

public var remaining: SubSequence {
elements[current ..< elements.endIndex]
}

public var remainingCount: Int {
elements.distance(from: current, to: elements.endIndex)
}

public var atEnd: Bool {
elements.distance(from: current, to: elements.endIndex) == 0
}

public mutating func scan(count: Int) -> SubSequence? {
guard !atEnd else {
return nil
}
guard remainingCount >= count else {
return nil
}
let result = elements[current ..< elements.index(current, offsetBy: count)]
current = elements.index(current, offsetBy: count)
return result
}

public func peek() -> Element? {
if atEnd {
return nil
}
return elements[current]
}
}

public extension CollectionScanner where Element: Equatable {
mutating func scan(value: Element) -> Bool {
guard !atEnd else {
return false
}
guard elements[current] == value else {
return false
}
current = elements.index(current, offsetBy: 1)
return true
}

mutating func scan(value: [Element]) -> Bool {
let saved = current
guard let scanned = scan(count: value.count) else {
return false
}
guard Array(scanned) == value else {
current = saved
return false
}
return true
}

mutating func scanUpTo(value: Element, consuming: Bool = false) -> SubSequence? {
guard !atEnd else {
return nil
}
let index = remaining.firstIndex(of: value) ?? elements.endIndex
let result = elements[current ..< index]
current = index
if consuming && !atEnd {
current = elements.index(current, offsetBy: 1)
}
return result
}

// TODO: add a flag to control whether remaining to count as a match even if it doesnt end in a token
mutating func scanUpTo(value: [Element], consuming: Bool = false) -> SubSequence? {
assert(!value.isEmpty)
guard !atEnd else {
return nil
}
// TODO: Replace with https://en.wikipedia.org/wiki/Knuth–Morris–Pratt_algorithm where possible
var remainingToSearch = remaining
while !atEnd {
if value.count > remainingToSearch.count {
break
}
if let index = remainingToSearch.firstIndex(of: value.first!) {
guard remainingToSearch.distance(from: index, to: remainingToSearch.endIndex) >= value.count else {
return nil
}
let hit = remainingToSearch[index ..< remainingToSearch.index(index, offsetBy: value.count)]
// TODO: Is converting to an array slice efficient?
if ArraySlice<Element>(hit) == value[value.startIndex ..< value.endIndex] {
current = index
let result = remainingToSearch[..<index]
if consuming && !atEnd {
current = elements.index(current, offsetBy: value.count)
return result
}
return result
}
remainingToSearch = remainingToSearch[index...]
}
else {
break
}
}
let result = remaining
current = elements.endIndex
return result
}

mutating func scan(componentsSeparatedBy separator: Element) -> [SubSequence] {
guard !atEnd else {
return []
}
let it = iterator(forComponentsSeparatedBy: separator)
let result = it.collect()
current = elements.endIndex // TODO: Is this always correct?
return result
}

mutating func scan(anyOf elements: [Element]) -> Element? {
guard let element = peek(), elements.contains(element) else {
return nil
}
current = self.elements.index(current, offsetBy: 1)
return element
}
}

extension CollectionScanner where Element: Equatable {
// TODO: This operates on a COPY of the Scanner
func iterator(for block: @escaping (inout Self) -> SubSequence?) -> AnyIterator<SubSequence> {
CollectionScannerIterator(scanner: self, block: block).eraseToAnyIterator()
}

// TODO: This operates on a COPY of the Scanner
func iterator(forComponentsSeparatedBy separator: Element) -> AnyIterator<SubSequence> {
iterator { scanner in
if let result = scanner.scanUpTo(value: separator, consuming: true) {
return result
}
return nil
}
}

// TODO: This operates on a COPY of the Scanner
func iterator(forComponentsSeparatedBy separator: [Element]) -> AnyIterator<SubSequence> {
iterator { scanner in
if let result = scanner.scanUpTo(value: separator, consuming: true) {
return result
}
return nil
}
}
}

// TODO: This operates on a COPY of the Scanner
public struct CollectionScannerIterator<C>: IteratorProtocol where C: Collection, C.Element: Equatable {
public typealias Scanner = CollectionScanner<C>
var scanner: Scanner
let block: (inout Scanner) -> Scanner.SubSequence?
public mutating func next() -> Scanner.SubSequence? {
if scanner.atEnd {
return nil
}
return block(&scanner)
}
}

extension CollectionScanner: CustomDebugStringConvertible {
public var debugDescription: String {
let startIndex = elements.distance(from: elements.startIndex, to: elements.startIndex)
let endIndex = elements.distance(from: elements.startIndex, to: elements.endIndex)
let current = elements.distance(from: elements.startIndex, to: current)
return "\(startIndex) / \(endIndex) / \(current)"
}
}

public extension CollectionScanner where Element == UInt8 {
mutating func scan<T>(type t: T.Type) -> T? where T: BinaryFloatingPoint {
let saved = current
guard let array = scan(count: MemoryLayout<T>.size) else {
return nil
}
return Array(array).withUnsafeBufferPointer { buffer -> T? in
guard let pointer = buffer.baseAddress else {
current = saved
return nil
}
return pointer.withMemoryRebound(to: t, capacity: 1) { pointer in
pointer.pointee
}
}
}

mutating func scan<T>(type t: T.Type) -> T? where T: BinaryInteger {
let saved = current
guard let array = scan(count: MemoryLayout<T>.size) else {
return nil
}
return Array(array).withUnsafeBufferPointer { buffer in
guard let pointer = buffer.baseAddress else {
current = saved
return nil
}
return pointer.withMemoryRebound(to: t, capacity: 1) { pointer in
pointer.pointee
}
}
}

mutating func scan<T>(type t: T.Type, count: Int) -> [T]? where T: BinaryInteger {
let values = (0 ..< count).compactMap { _ in
scan(type: t)
}
assert(values.count == count)
return values
}

mutating func scan<T>(type t: T.Type) -> T? where T: SIMD, T.Scalar: BinaryInteger {
scan(type: t.Scalar.self, count: t.scalarCount).map { T($0) }
}
}

public extension CollectionScanner {
mutating func scan(until block: (Element) -> Bool) -> SubSequence? {
guard !atEnd else {
return nil
}
for index in remaining.indices {
let element = remaining[index]
if block(element) == true {
let result = elements[current ..< index]
current = index
return result
}
}
let result = remaining
current = elements.endIndex
return result
}
}

public extension CollectionScanner {
// Similar to SwiftUI.Path.path
func scan<R>(block: (inout CollectionScanner) throws -> R) rethrows -> R {
var scanner = self
return try block(&scanner)
}
}

internal extension IteratorProtocol {
func eraseToAnyIterator() -> AnyIterator<Element> {
AnyIterator(self)
}

func collect() -> [Element] {
Array(AnyIterator(self))
}
}
51 changes: 45 additions & 6 deletions Sources/SwiftGLTF/Scratch.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import CoreGraphics
import CoreImage
import Foundation
import simd
import os

extension CGImage {
@available(*, deprecated, message: "Inefficient")
Expand Down Expand Up @@ -32,12 +34,6 @@ extension CGImage {
}
}

extension Index: CustomStringConvertible {
public var description: String {
"\(C.Element.self)#\(index)"
}
}

extension Accessor.ComponentType {
var size: Int {
switch self {
Expand Down Expand Up @@ -85,3 +81,46 @@ extension SIMD where Scalar == Float {
return true
}
}

internal extension SIMD3<Float> {
func map(_ f: (Float) -> Float) -> Self {
[f(x), f(y), f(z)]
}
}

internal extension SIMD4<Float> {
func map(_ f: (Float) -> Float) -> Self {
[f(x), f(y), f(z), f(w)]
}

var xyz: SIMD3<Float> {
return [x, y, z]
}

var cgColor: CGColor {
return CGColor(red: Double(x), green: Double(y), blue: Double(z), alpha: Double(w))
}
}

extension simd_float4x4 {
static let identity = simd_float4x4(diagonal: [1, 1, 1, 1])
}


internal func warning(_ message: @autoclosure () -> String? = Optional.none, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
warning(false, message(), file: file, function: function, line: line)
}

internal func warning(_ closure: @autoclosure () -> Bool = false, _ message: @autoclosure () -> String? = Optional.none, file: StaticString = #file, function: StaticString = #function, line: UInt = #line) {
guard closure() == false else {
return
}

let logger = Logger()
if let message = message() {
logger.debug("\(message)")
}
else {
logger.debug("Warning! \(file)#\(line)")
}
}
2 changes: 1 addition & 1 deletion Sources/SwiftGLTF/glb.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Everything
//import Everything
import Foundation

// swiftlint:disable fatal_error_message
Expand Down

0 comments on commit 0f93896

Please sign in to comment.