Skip to content
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

POC: OrderedSet implementation #25

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
- name: Use Swift v6.0
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.0-beta"
xcode-version: "16.0"
- name: Lint
run: swift package plugin swiftlint --reporter github-actions-logging --strict
- name: Build
Expand Down
11 changes: 10 additions & 1 deletion Package.resolved
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
{
"originHash" : "13e77cfe605311a25ca578c6b9723a9453537668a1626df5a3988b4fd2347ae7",
"originHash" : "2d2e97e160f96f8aa3f654aa41260ee9579635d1b832493b77c7ac0983927247",
"pins" : [
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections.git",
"state" : {
"revision" : "9bf03ff58ce34478e66aaee630e491823326fd06",
"version" : "1.1.3"
}
},
{
"identity" : "swiftlintplugins",
"kind" : "remoteSourceControl",
Expand Down
9 changes: 7 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// swift-tools-version: 5.10
import PackageDescription

var dependencies: [Package.Dependency] = []
var dependencies: [Package.Dependency] = [
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.3"),
]
var plugins: [Target.PluginUsage]?

#if os(macOS)
dependencies = [.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.56.2")]
dependencies += [.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.56.2")]
plugins = [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
#endif

Expand All @@ -21,6 +23,9 @@ let package = Package(
targets: [
.target(
name: "BijectiveDictionary",
dependencies: [
.product(name: "OrderedCollections", package: "swift-collections"),
],
swiftSettings: [.enableExperimentalFeature("StrictConcurrency")],
plugins: plugins
),
Expand Down
11 changes: 8 additions & 3 deletions Package@swift-6.0.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// swift-tools-version: 6.0
import PackageDescription

var dependencies: [Package.Dependency] = []
var dependencies: [Package.Dependency] = [
.package(url: "https://github.com/apple/swift-collections.git", from: "1.1.3"),
]
var plugins: [Target.PluginUsage]?

#if os(macOS)
dependencies = [.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.56.2")]
dependencies += [.package(url: "https://github.com/SimplyDanny/SwiftLintPlugins", from: "0.56.2")]
plugins = [.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLintPlugins")]
#endif

Expand All @@ -21,6 +23,9 @@ let package = Package(
targets: [
.target(
name: "BijectiveDictionary",
dependencies: [
.product(name: "OrderedCollections", package: "swift-collections"),
],
plugins: plugins
),
.testTarget(
Expand All @@ -29,5 +34,5 @@ let package = Package(
plugins: plugins
),
],
swiftLanguageVersions: [.v6]
swiftLanguageModes: [.v6]
)
11 changes: 11 additions & 0 deletions Sources/BijectiveDictionary/Experiments/+_invariantCheck.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
extension POCOrderedSetImplementation {
#if DEBUG
@usableFromInline @inline(never)
internal func _invariantCheck() {
print("👷🏼‍♀️ WIP: _invariantCheck is kept for consistency, but I'm not sure it's necessary, yet")
}
#else
@inlinable @inline(__always)
internal func _invariantCheck() {}
#endif
}
37 changes: 37 additions & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Collection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// File.swift
// BijectiveDictionary
//
// Created by Daniel Lyons on 2024-09-16.
//

import OrderedCollections

extension POCOrderedSetImplementation: Collection {
public typealias Index = Int
public var startIndex: Int { 0 }
public var endIndex: Int {
assert(_ltr.count == _rtl.count)
return _ltr.count
}

public func index(after index: Int) -> Int {
_ltr.index(after: index)
}

public subscript(position: Int) -> (left: Left, right: Right) {
let leftValue = _ltr[position]
let rightValue = _rtl[position]
return (left: leftValue, right: rightValue)
}

@inlinable public var isEmpty: Bool {
assert(_ltr.isEmpty == _rtl.isEmpty)
return _ltr.isEmpty
}

@inlinable public var count: Int {
assert(_ltr.count == _rtl.count)
return _ltr.count
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// File.swift
// BijectiveDictionary
//
// Created by Daniel Lyons on 2024-09-16.
//

extension POCOrderedSetImplementation: ExpressibleByDictionaryLiteral {
/// Creates a new BijectiveDictionary from a dictionary literal.
///
/// >Warning: Both left and right values must be unique or else this will fatal error.
public init(dictionaryLiteral elements: (Left, Right)...) {
self.init(minimumCapacity: elements.count)

for (leftKey, rightKey) in elements {
let (leftInserted, atLeftIndex) = _ltr.append(leftKey)
let (rightInserted, atRightIndex) = _rtl.append(rightKey)

guard leftInserted == true,
rightInserted == true,
atLeftIndex == atRightIndex else {
fatalError("dictionary literal contains duplicate value")
}
}
}
}
10 changes: 10 additions & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Hashable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
extension POCOrderedSetImplementation: Hashable {

public func hash(into hasher: inout Hasher) {
hasher.combine(_ltr)
}

public static func == (lhs: POCOrderedSetImplementation, rhs: POCOrderedSetImplementation) -> Bool {
lhs._ltr == rhs._ltr
}
}
64 changes: 64 additions & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Initializers.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// File.swift
// BijectiveDictionary
//
// Created by Daniel Lyons on 2024-09-16.
//

import OrderedCollections

extension POCOrderedSetImplementation {
@inlinable public init() {
self._ltr = OrderedSet()
self._rtl = OrderedSet()
}

@inlinable public init(minimumCapacity: Int, persistent: Bool = false) {
self._ltr = OrderedSet(minimumCapacity: minimumCapacity, persistent: persistent)
self._rtl = OrderedSet(minimumCapacity: minimumCapacity, persistent: persistent)
}

@inlinable public init<S>(uniqueLeftRightPairs pairs: S) where S: Sequence, S.Element == (Left, Right) {
defer { _invariantCheck() } // necessary?
let leftValues = pairs.map(\.0)
self._ltr = OrderedSet(leftValues)
let rightValues = pairs.map(\.1)
self._rtl = OrderedSet(rightValues)
}

/// Create an ordered `POCOrderedSetImplementation` from an unordered `Dictionary`
@inlinable public init?(_ dictionary: [Left: Right]) {
defer { _invariantCheck() } // necessary?

let dictElements: [(Left, Right)] = dictionary.map { ($0.key, $0.value) }
self._ltr = OrderedSet(dictElements.map(\.0))
self._rtl = OrderedSet(dictElements.map(\.1))

let leftValues = dictElements.map { $0 }
let rightValues = dictElements.map { $1 }
guard _ltr.count == leftValues.count,
_rtl.count == rightValues.count else {
// return `nil` if either set contains less elements
// i.e. if there were any duplicates
// ROOM FOR IMPROVEMENT: This approach doesn't check for duplicates until
// after the work has already been done.
return nil
}
}
}

extension Dictionary {
@inlinable public init(_ poc: POCOrderedSetImplementation<Key, Value>) where Value: Hashable {
self = poc.asDictionary
}
}

extension POCOrderedSetImplementation {
@inlinable public var asElementsArray: [Element] {
return Array(zip(_ltr, _rtl))
}

@inlinable public var asDictionary: [Left: Right] {
Dictionary.init(uniqueKeysWithValues: asElementsArray)
}
}
9 changes: 9 additions & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Operators.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
extension POCOrderedSetImplementation {

static func == (lhs: Self, rhs: [Left: Right]) -> Bool {
lhs.asDictionary == rhs
}
static func == (lhs: [Left: Right], rhs: Self) -> Bool {
lhs == rhs.asDictionary
}
}
1 change: 1 addition & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Sendable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

49 changes: 49 additions & 0 deletions Sources/BijectiveDictionary/Experiments/POC+Sequence.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
//
// POCOrderedSetImplementation
//
// Created by Daniel Lyons on 2024-09-16.
//

import OrderedCollections

extension POCOrderedSetImplementation: Sequence {
public func makeIterator() -> Iterator {
Iterator(
_ltr: _ltr,
_rtl: _rtl,
start: 0,
end: _ltr.count - 1
)
}

public typealias Element = (left: Left, right: Right)

@frozen public struct Iterator: IteratorProtocol {
public init(
_ltr: OrderedSet<Left>,
_rtl: OrderedSet<Right>,
start: Int,
end: Int
) {
self._ltr = _ltr
self._rtl = _rtl
self.current = start
self.end = end
}

let _ltr: OrderedSet<Left>
let _rtl: OrderedSet<Right>
var current: Int
let end: Int

public mutating func next() -> (left: Left, right: Right)? {
defer { current += 1 }
guard current < end else {
return nil
}
let leftValue = _ltr[current]
let rightValue = _rtl[current]
return (left: leftValue, right: rightValue)
}
}
}
Loading