Skip to content

Commit

Permalink
Merge pull request #84 from orchetect/dev
Browse files Browse the repository at this point in the history
Updated utility methods
  • Loading branch information
orchetect authored Apr 18, 2022
2 parents 7a219c5 + e2a6e9e commit d32662b
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 33 deletions.
27 changes: 20 additions & 7 deletions Sources/MIDIKit/Common/Utilities/Atomic.swift
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
/// Borrowed from [OTCore 1.1.17](https://github.com/orchetect/OTCore) under MIT license.
/// Methods herein are unit tested in OTCore, so no unit tests are necessary in MIDIKit.
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------------
/// Borrowed from [OTAtomics 1.0.0](https://github.com/orchetect/OTAtomics) under MIT license.
/// Methods herein are unit tested in OTAtomics, so no unit tests are necessary in MIDIKit.
/// ------------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------------

import Foundation

extension MIDI {

/// Atomic: A property wrapper that ensures thread-safe atomic access to a value.
/// `Atomic`: A property wrapper that ensures thread-safe atomic access to a value.
/// Multiple read accesses can potentially read at the same time, just not during a write.
///
/// By using `pthread` to do the locking, this safer than using a `DispatchQueue/barrier` as there isn't a chance of priority inversion.
///
/// This is safe to use on collection types (`Array`, `Dictionary`, etc.)
///
/// - Warning: Do not instance this wrapper on a variable declaration inside a function. Only wrap class-bound, struct-bound, or global-bound variables.
@propertyWrapper
public final class Atomic<T> {

@inline(__always)
private var value: T

@inline(__always)
private let lock: ThreadLock = RWThreadLock()

@inline(__always)
public init(wrappedValue value: T) {

self.value = value

}

@inline(__always)
public var wrappedValue: T {

get {
Expand All @@ -36,6 +42,12 @@ extension MIDI {
return self.value
}

set {
self.lock.writeLock()
value = newValue
self.lock.unlock()
}

// _modify { } is an internal Swift computed setter, similar to set { }
// however it gives in-place exclusive mutable access
// which allows get-then-set operations such as collection subscripts
Expand All @@ -49,6 +61,7 @@ extension MIDI {
}

}


}

Expand Down
20 changes: 13 additions & 7 deletions Sources/MIDIKit/Common/Utilities/Clamped.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
/// Borrowed from [OTCore 1.1.8](https://github.com/orchetect/OTCore) under MIT license.
/// Borrowed from [OTCore 1.4.1](https://github.com/orchetect/OTCore) under MIT license.
/// Methods herein are unit tested in OTCore, so no unit tests are necessary in MIDIKit.
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
Expand All @@ -14,8 +14,9 @@ extension Comparable {
// ie: 5.clamped(to: 7...10)
// ie: 5.0.clamped(to: 7.0...10.0)
// ie: "a".clamped(to: "b"..."h")
/// **OTCore:**
/// Returns the value clamped to the passed range.
@inlinable
@inlinable @_disfavoredOverload
internal func clamped(to limits: ClosedRange<Self>) -> Self {

min(max(self, limits.lowerBound), limits.upperBound)
Expand All @@ -25,8 +26,9 @@ extension Comparable {
// ie: 5.clamped(to: 300...)
// ie: 5.0.clamped(to: 300.00...)
// ie: "a".clamped(to: "b"...)
/// **OTCore:**
/// Returns the value clamped to the passed range.
@inlinable
@inlinable @_disfavoredOverload
internal func clamped(to limits: PartialRangeFrom<Self>) -> Self {

max(self, limits.lowerBound)
Expand All @@ -36,8 +38,9 @@ extension Comparable {
// ie: 400.clamped(to: ...300)
// ie: 400.0.clamped(to: ...300.0)
// ie: "k".clamped(to: ..."h")
/// **OTCore:**
/// Returns the value clamped to the passed range.
@inlinable
@inlinable @_disfavoredOverload
internal func clamped(to limits: PartialRangeThrough<Self>) -> Self {

min(self, limits.upperBound)
Expand All @@ -55,8 +58,9 @@ extension Strideable {

// ie: 400.clamped(to: ..<300)
// won't work for String
/// **OTCore:**
/// Returns the value clamped to the passed range.
@inlinable
@inlinable @_disfavoredOverload
internal func clamped(to limits: PartialRangeUpTo<Self>) -> Self {

// advanced(by:) requires Strideable, not available on just Comparable
Expand All @@ -70,12 +74,14 @@ extension Strideable where Self.Stride: SignedInteger {

// ie: 5.clamped(to: 7..<10)
// won't work for String
/// **OTCore:**
/// Returns the value clamped to the passed range.
@inlinable
@inlinable @_disfavoredOverload
internal func clamped(to limits: Range<Self>) -> Self {

// index(before:) only available on SignedInteger
min(max(self, limits.lowerBound), limits.index(before: limits.upperBound))
min(max(self, limits.lowerBound),
limits.index(before: limits.upperBound))

}

Expand Down
57 changes: 38 additions & 19 deletions Sources/MIDIKit/Common/Utilities/String Extensions.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
/// Borrowed from [OTCore 1.1.8](https://github.com/orchetect/OTCore) under MIT license.
/// Borrowed from [OTCore 1.4.1](https://github.com/orchetect/OTCore) under MIT license.
/// Methods herein are unit tested in OTCore, so no unit tests are necessary in MIDIKit.
/// ------------------------------------------------------------------------------------
/// ------------------------------------------------------------------------------------
Expand All @@ -10,7 +10,7 @@ import Foundation
extension String {

/// Wraps a string with double-quotes (`"`)
@inlinable
@inlinable @_disfavoredOverload
internal var quoted: Self {

"\"\(self)\""
Expand All @@ -23,50 +23,69 @@ extension String {

extension StringProtocol {

/// **OTCore:**
/// Returns a string preserving only characters from the CharacterSet and removing all other characters.
/// Returns a string preserving only characters from one or more `CharacterSet`s.
///
/// Example:
///
/// "A string 123".only(.alphanumerics)`
/// "A string 123".only(.letters, .decimalDigits)`
///
public func only(_ characterSet: CharacterSet) -> String {
@inlinable @_disfavoredOverload
internal func only(_ characterSet: CharacterSet,
_ characterSets: CharacterSet...) -> String {

self.map { characterSet.contains(UnicodeScalar("\($0)")!) ? "\($0)" : "" }
let mergedCharacterSet = characterSets.isEmpty
? characterSet
: characterSets.reduce(into: characterSet, { $0.formUnion($1) })

return unicodeScalars
.filter { mergedCharacterSet.contains($0) }
.map { "\($0)" }
.joined()

}

/// **OTCore:**
/// Returns a string preserving only characters from the passed string and removing all other characters.
public func only(characters: String) -> String {
@inlinable @_disfavoredOverload
internal func only(characters: String) -> String {

self.only(CharacterSet(charactersIn: characters))
only(CharacterSet(charactersIn: characters))

}

/// **OTCore:**
/// Returns a string containing only alphanumeric characters and removing all other characters.
public var onlyAlphanumerics: String {
@inlinable @_disfavoredOverload
internal var onlyAlphanumerics: String {

self.only(.alphanumerics)
only(.alphanumerics)

}

/// **OTCore:**
/// Returns a string removing all characters from the passed CharacterSet.
public func removing(_ characterSet: CharacterSet) -> String {
/// Returns a string removing all characters from the passed `CharacterSet`s.
///
/// Example:
///
/// "A string 123".removing(.whitespaces)`
/// "A string 123".removing(.letters, .decimalDigits)`
///
@inlinable @_disfavoredOverload
internal func removing(_ characterSet: CharacterSet,
_ characterSets: CharacterSet...) -> String {

let mergedCharacterSet = characterSets.isEmpty
? characterSet
: characterSets.reduce(into: characterSet, { $0.formUnion($1) })

self.components(separatedBy: characterSet)
return components(separatedBy: mergedCharacterSet)
.joined()

}

/// **OTCore:**
/// Returns a string removing all characters from the passed string.
public func removing(characters: String) -> String {
@inlinable @_disfavoredOverload
internal func removing(characters: String) -> String {

self.components(separatedBy: CharacterSet(charactersIn: characters))
components(separatedBy: CharacterSet(charactersIn: characters))
.joined()

}
Expand Down

0 comments on commit d32662b

Please sign in to comment.